diff --git a/BUILD.gn b/BUILD.gn deleted file mode 100644 index ab5f8940f..000000000 --- a/BUILD.gn +++ /dev/null @@ -1,3215 +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. - -# HOW TO WRITE CONDITIONALS IN THIS FILE -# ====================================== -# -# In many other places, one would write a conditional that expresses all the -# cases when a source file is used or unused, and then either add or subtract -# it from the sources list in that case -# -# Since base includes so many low-level things that vary widely and -# unpredictably for the various build types, we prefer a slightly different -# style. Instead, there are big per-platform blocks of inclusions and -# exclusions. If a given file has an inclusion or exclusion rule that applies -# for multiple conditions, prefer to duplicate it in both lists. This makes it -# a bit easier to see which files apply in which cases rather than having a -# huge sequence of random-looking conditionals. - -import("//build/buildflag_header.gni") -import("//build/config/allocator.gni") -import("//build/config/arm.gni") -import("//build/config/c++/c++.gni") -import("//build/config/chromecast_build.gni") -import("//build/config/compiler/compiler.gni") -import("//build/config/dcheck_always_on.gni") -import("//build/config/jumbo.gni") -import("//build/config/nacl/config.gni") -import("//build/config/sysroot.gni") -import("//build/config/ui.gni") -import("//build/nocompile.gni") -import("//testing/libfuzzer/fuzzer_test.gni") -import("//testing/test.gni") - -declare_args() { - # Override this value to give a specific build date. - # See //base/build_time.cc and //build/write_build_date_header.py for more - # details and the expected format. - override_build_date = "N/A" - - # Indicates if the Location object contains the source code information - # (file, function, line). False means only the program counter (and currently - # file name) is saved. - enable_location_source = true - - # Unsafe developer build. Has developer-friendly features that may weaken or - # disable security measures like sandboxing or ASLR. - # IMPORTANT: Unsafe developer builds should never be distributed to end users. - is_unsafe_developer_build = !is_official_build - - # Set to true to disable COM init check hooks. - com_init_check_hook_disabled = false - - # Set to true to enable mutex priority inheritance. See the comments in - # LockImpl::PriorityInheritanceAvailable() in lock_impl_posix.cc for the - # platform requirements to safely enable priority inheritance. - enable_mutex_priority_inheritance = false -} - -# Determines whether libevent should be dep. -dep_libevent = !is_fuchsia && !is_win && !(is_nacl && !is_nacl_nonsfi) - -# Determines whether message_pump_libevent should be used. -use_libevent = dep_libevent && !is_ios - -if (is_android) { - import("//build/config/android/rules.gni") -} - -if (is_fuchsia) { - import("//build/config/fuchsia/fidl_library.gni") -} - -config("base_flags") { - if (is_clang) { - cflags = [ - # Don't die on dtoa code that uses a char as an array index. - # This is required solely for base/third_party/dmg_fp/dtoa_wrapper.cc. - "-Wno-char-subscripts", - - # Ideally all product code (but no test code) in chrome would have these - # flags. But this isn't trivial so start with //base as a minimum - # requirement. - # https://groups.google.com/a/chromium.org/d/topic/chromium-dev/B9Q5KTD7iCo/discussion - "-Wglobal-constructors", - "-Wexit-time-destructors", - ] - } -} - -config("base_implementation") { - defines = [ "BASE_IMPLEMENTATION" ] - configs = [ "//build/config/compiler:wexit_time_destructors" ] -} - -if (is_win) { - # This is in a separate config so the flags can be applied to dependents. - # ldflags in GN aren't automatically inherited. - config("base_win_linker_flags") { - ldflags = [ - "/DELAYLOAD:cfgmgr32.dll", - "/DELAYLOAD:powrprof.dll", - "/DELAYLOAD:setupapi.dll", - ] - } -} - -if (is_nacl_nonsfi) { - # Must be in a config because of how GN orders flags (otherwise -Wall will - # appear after this, and turn it back on). - config("nacl_nonsfi_warnings") { - # file_util_posix.cc contains a function which is not - # being used by nacl_helper_nonsfi. - cflags = [ "-Wno-unused-function" ] - } -} - -if (is_android) { - config("android_system_libs") { - libs = [ - "android", - "log", # Used by logging.cc. - ] - } -} - -# Base and everything it depends on should be a static library rather than -# a source set. Base is more of a "library" in the classic sense in that many -# small parts of it are used in many different contexts. This combined with a -# few static initializers floating around means that dead code stripping -# still leaves a lot of code behind that isn't always used. For example, this -# saves more than 40K for a smaller target like chrome_elf. -# -# Use static libraries for the helper stuff as well like //base/debug since -# those things refer back to base code, which will force base compilation units -# to be linked in where they wouldn't have otherwise. This does not include -# test code (test support and anything in the test directory) which should use -# source_set as is recommended for GN targets). -jumbo_component("base") { - if (is_nacl_nonsfi) { - # TODO(phosek) bug 570839: If field_trial.cc is in a static library, - # nacl_helper_nonsfi doesn't link properly on Linux in debug builds. The - # reasons for this seem to involve obscure toolchain bugs. This should be - # fixed and this target should always be a static_library in the - # non-component case. - static_component_type = "source_set" - } - if (is_nacl || is_ios) { - # Link errors related to malloc functions if libbase for nacl is - # compiled with jumbo: https://crbug.com/775959. - # Same for ios: https://crbug.com/776313. - never_build_jumbo = true - } - - sources = [ - "allocator/allocator_check.cc", - "allocator/allocator_check.h", - "allocator/allocator_extension.cc", - "allocator/allocator_extension.h", - "allocator/allocator_interception_mac.h", - "allocator/allocator_interception_mac.mm", - "allocator/allocator_shim.h", - "allocator/malloc_zone_functions_mac.cc", - "allocator/malloc_zone_functions_mac.h", - "android/android_hardware_buffer_abi.h", - "android/android_hardware_buffer_compat.cc", - "android/android_hardware_buffer_compat.h", - "android/animation_frame_time_histogram.cc", - "android/apk_assets.cc", - "android/apk_assets.h", - "android/application_status_listener.cc", - "android/application_status_listener.h", - "android/base_jni_onload.cc", - "android/base_jni_onload.h", - "android/build_info.cc", - "android/build_info.h", - "android/callback_android.cc", - "android/callback_android.h", - "android/child_process_binding_types.h", - "android/child_process_service.cc", - "android/command_line_android.cc", - "android/content_uri_utils.cc", - "android/content_uri_utils.h", - "android/cpu_features.cc", - "android/early_trace_event_binding.cc", - "android/event_log.cc", - "android/event_log.h", - "android/field_trial_list.cc", - "android/important_file_writer_android.cc", - "android/java_exception_reporter.cc", - "android/java_exception_reporter.h", - "android/java_handler_thread.cc", - "android/java_handler_thread.h", - "android/java_runtime.cc", - "android/java_runtime.h", - "android/jni_android.cc", - "android/jni_android.h", - "android/jni_array.cc", - "android/jni_array.h", - "android/jni_generator/jni_generator_helper.h", - "android/jni_int_wrapper.h", - "android/jni_registrar.cc", - "android/jni_registrar.h", - "android/jni_string.cc", - "android/jni_string.h", - "android/jni_utils.cc", - "android/jni_utils.h", - "android/jni_weak_ref.cc", - "android/jni_weak_ref.h", - "android/library_loader/anchor_functions.cc", - "android/library_loader/anchor_functions.h", - "android/library_loader/library_load_from_apk_status_codes.h", - "android/library_loader/library_loader_hooks.cc", - "android/library_loader/library_loader_hooks.h", - "android/library_loader/library_prefetcher.cc", - "android/library_loader/library_prefetcher.h", - "android/locale_utils.cc", - "android/locale_utils.h", - "android/memory_pressure_listener_android.cc", - "android/memory_pressure_listener_android.h", - "android/path_service_android.cc", - "android/path_utils.cc", - "android/path_utils.h", - "android/record_histogram.cc", - "android/record_user_action.cc", - "android/scoped_hardware_buffer_handle.cc", - "android/scoped_hardware_buffer_handle.h", - "android/scoped_java_ref.cc", - "android/scoped_java_ref.h", - "android/statistics_recorder_android.cc", - "android/sys_utils.cc", - "android/sys_utils.h", - "android/throw_uncaught_exception.cc", - "android/throw_uncaught_exception.h", - "android/time_utils.cc", - "android/timezone_utils.cc", - "android/timezone_utils.h", - "android/trace_event_binding.cc", - "android/unguessable_token_android.cc", - "android/unguessable_token_android.h", - "at_exit.cc", - "at_exit.h", - "atomic_ref_count.h", - "atomic_sequence_num.h", - "atomicops.h", - "atomicops_internals_atomicword_compat.h", - "atomicops_internals_portable.h", - "atomicops_internals_x86_msvc.h", - "auto_reset.h", - "barrier_closure.cc", - "barrier_closure.h", - "base64.cc", - "base64.h", - "base64url.cc", - "base64url.h", - "base_export.h", - "base_switches.h", - "big_endian.cc", - "big_endian.h", - "bind.h", - "bind_helpers.h", - "bind_internal.h", - "bit_cast.h", - "bits.h", - "build_time.cc", - "build_time.h", - "callback.h", - "callback_forward.h", - "callback_helpers.cc", - "callback_helpers.h", - "callback_internal.cc", - "callback_internal.h", - "callback_list.h", - "cancelable_callback.h", - "command_line.cc", - "command_line.h", - "compiler_specific.h", - "component_export.h", - "containers/adapters.h", - "containers/circular_deque.h", - "containers/flat_map.h", - "containers/flat_set.h", - "containers/flat_tree.h", - "containers/hash_tables.h", - "containers/id_map.h", - "containers/linked_list.h", - "containers/mru_cache.h", - "containers/small_map.h", - "containers/span.h", - "containers/stack.h", - "containers/stack_container.h", - "containers/unique_ptr_adapters.h", - "containers/vector_buffer.h", - "cpu.cc", - "cpu.h", - "critical_closure.h", - "critical_closure_internal_ios.mm", - - # This file depends on files from the "debug/allocator" target, - # but this target does not depend on "debug/allocator". - "debug/activity_analyzer.cc", - "debug/activity_analyzer.h", - "debug/activity_tracker.cc", - "debug/activity_tracker.h", - "debug/alias.cc", - "debug/alias.h", - "debug/asan_invalid_access.cc", - "debug/asan_invalid_access.h", - "debug/close_handle_hook_win.cc", - "debug/close_handle_hook_win.h", - "debug/crash_logging.cc", - "debug/crash_logging.h", - "debug/debugger.cc", - "debug/debugger.h", - "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", - "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_win.cc", - "debug/task_annotator.cc", - "debug/task_annotator.h", - "debug/thread_heap_usage_tracker.cc", - "debug/thread_heap_usage_tracker.h", - "deferred_sequenced_task_runner.cc", - "deferred_sequenced_task_runner.h", - "environment.cc", - "environment.h", - "export_template.h", - "feature_list.cc", - "feature_list.h", - "file_descriptor_store.cc", - "file_descriptor_store.h", - "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/file.cc", - "files/file.h", - "files/file_enumerator.cc", - "files/file_enumerator.h", - "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_tracing.cc", - "files/file_tracing.h", - "files/file_util.cc", - "files/file_util.h", - "files/file_util_android.cc", - "files/file_util_linux.cc", - "files/file_util_mac.mm", - "files/file_util_win.cc", - "files/file_win.cc", - "files/important_file_writer.cc", - "files/important_file_writer.h", - "files/memory_mapped_file.cc", - "files/memory_mapped_file.h", - "files/memory_mapped_file_win.cc", - "files/platform_file.h", - "files/scoped_file.cc", - "files/scoped_file.h", - "files/scoped_temp_dir.cc", - "files/scoped_temp_dir.h", - "format_macros.h", - "gtest_prod_util.h", - "guid.cc", - "guid.h", - "hash.cc", - "hash.h", - "ios/block_types.h", - "ios/crb_protocol_observers.h", - "ios/crb_protocol_observers.mm", - "ios/device_util.h", - "ios/device_util.mm", - "ios/ios_util.h", - "ios/ios_util.mm", - "ios/ns_error_util.h", - "ios/ns_error_util.mm", - "ios/scoped_critical_action.h", - "ios/scoped_critical_action.mm", - "ios/weak_nsobject.h", - "ios/weak_nsobject.mm", - "json/json_file_value_serializer.cc", - "json/json_file_value_serializer.h", - "json/json_parser.cc", - "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.cc", - "json/json_value_converter.h", - "json/json_writer.cc", - "json/json_writer.h", - "json/string_escape.cc", - "json/string_escape.h", - "lazy_instance.h", - "lazy_instance_helpers.cc", - "lazy_instance_helpers.h", - "linux_util.cc", - "linux_util.h", - "location.cc", - "location.h", - "logging.cc", - "logging.h", - "logging_win.cc", - "logging_win.h", - "mac/authorization_util.h", - "mac/authorization_util.mm", - "mac/availability.h", - "mac/bundle_locations.h", - "mac/bundle_locations.mm", - "mac/call_with_eh_frame.cc", - "mac/call_with_eh_frame.h", - "mac/call_with_eh_frame_asm.S", - "mac/close_nocancel.cc", - "mac/dispatch_source_mach.cc", - "mac/dispatch_source_mach.h", - "mac/foundation_util.h", - "mac/foundation_util.mm", - "mac/launch_services_util.h", - "mac/launch_services_util.mm", - "mac/launchd.cc", - "mac/launchd.h", - "mac/mac_logging.h", - "mac/mac_logging.mm", - "mac/mac_util.h", - "mac/mac_util.mm", - "mac/mach_logging.cc", - "mac/mach_logging.h", - "mac/mach_port_broker.h", - "mac/mach_port_broker.mm", - "mac/mach_port_util.cc", - "mac/mach_port_util.h", - "mac/objc_release_properties.h", - "mac/objc_release_properties.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_cffiledescriptorref.h", - "mac/scoped_cftyperef.h", - "mac/scoped_dispatch_object.h", - "mac/scoped_ionotificationportref.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_nsobject.h", - "mac/scoped_nsobject.mm", - "mac/scoped_objc_class_swizzler.h", - "mac/scoped_objc_class_swizzler.mm", - "mac/scoped_sending_event.h", - "mac/scoped_sending_event.mm", - "mac/sdk_forward_declarations.h", - "mac/sdk_forward_declarations.mm", - "macros.h", - "md5.cc", - "md5.h", - "memory/aligned_memory.cc", - "memory/aligned_memory.h", - "memory/discardable_memory.cc", - "memory/discardable_memory.h", - "memory/discardable_memory_allocator.cc", - "memory/discardable_memory_allocator.h", - "memory/discardable_shared_memory.cc", - "memory/discardable_shared_memory.h", - "memory/free_deleter.h", - "memory/linked_ptr.h", - "memory/memory_coordinator_client.cc", - "memory/memory_coordinator_client.h", - "memory/memory_coordinator_client_registry.cc", - "memory/memory_coordinator_client_registry.h", - "memory/memory_coordinator_proxy.cc", - "memory/memory_coordinator_proxy.h", - "memory/memory_pressure_listener.cc", - "memory/memory_pressure_listener.h", - "memory/memory_pressure_monitor.cc", - "memory/memory_pressure_monitor.h", - "memory/memory_pressure_monitor_chromeos.cc", - "memory/memory_pressure_monitor_chromeos.h", - "memory/memory_pressure_monitor_mac.cc", - "memory/memory_pressure_monitor_mac.h", - "memory/memory_pressure_monitor_win.cc", - "memory/memory_pressure_monitor_win.h", - "memory/platform_shared_memory_region.cc", - "memory/platform_shared_memory_region.h", - "memory/protected_memory.cc", - "memory/protected_memory.h", - "memory/protected_memory_cfi.h", - "memory/protected_memory_win.cc", - "memory/ptr_util.h", - "memory/raw_scoped_refptr_mismatch_checker.h", - "memory/read_only_shared_memory_region.cc", - "memory/read_only_shared_memory_region.h", - "memory/ref_counted.cc", - "memory/ref_counted.h", - "memory/ref_counted_delete_on_sequence.h", - "memory/ref_counted_memory.cc", - "memory/ref_counted_memory.h", - "memory/scoped_policy.h", - "memory/scoped_refptr.h", - "memory/shared_memory.h", - "memory/shared_memory_handle.cc", - "memory/shared_memory_handle.h", - "memory/shared_memory_helper.cc", - "memory/shared_memory_helper.h", - "memory/shared_memory_mapping.cc", - "memory/shared_memory_mapping.h", - "memory/shared_memory_tracker.cc", - "memory/shared_memory_tracker.h", - "memory/singleton.h", - "memory/unsafe_shared_memory_region.cc", - "memory/unsafe_shared_memory_region.h", - "memory/weak_ptr.cc", - "memory/weak_ptr.h", - "memory/writable_shared_memory_region.cc", - "memory/writable_shared_memory_region.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_current.cc", - "message_loop/message_loop_current.h", - "message_loop/message_loop_task_runner.cc", - "message_loop/message_loop_task_runner.h", - "message_loop/message_pump.cc", - "message_loop/message_pump.h", - "message_loop/message_pump_android.cc", - "message_loop/message_pump_android.h", - "message_loop/message_pump_default.cc", - "message_loop/message_pump_default.h", - "message_loop/message_pump_for_io.h", - "message_loop/message_pump_for_ui.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_mac.h", - "message_loop/message_pump_mac.mm", - "message_loop/message_pump_win.cc", - "message_loop/message_pump_win.h", - "message_loop/timer_slack.h", - "metrics/bucket_ranges.cc", - "metrics/bucket_ranges.h", - "metrics/dummy_histogram.cc", - "metrics/dummy_histogram.h", - "metrics/field_trial.cc", - "metrics/field_trial.h", - "metrics/field_trial_param_associator.cc", - "metrics/field_trial_param_associator.h", - "metrics/field_trial_params.cc", - "metrics/field_trial_params.h", - "metrics/histogram.cc", - "metrics/histogram.h", - "metrics/histogram_base.cc", - "metrics/histogram_base.h", - "metrics/histogram_delta_serialization.cc", - "metrics/histogram_delta_serialization.h", - "metrics/histogram_flattener.h", - "metrics/histogram_functions.cc", - "metrics/histogram_functions.h", - "metrics/histogram_macros.h", - "metrics/histogram_macros_internal.h", - "metrics/histogram_macros_local.h", - "metrics/histogram_samples.cc", - "metrics/histogram_samples.h", - "metrics/histogram_snapshot_manager.cc", - "metrics/histogram_snapshot_manager.h", - "metrics/metrics_hashes.cc", - "metrics/metrics_hashes.h", - "metrics/persistent_histogram_allocator.cc", - "metrics/persistent_histogram_allocator.h", - "metrics/persistent_memory_allocator.cc", - "metrics/persistent_memory_allocator.h", - "metrics/persistent_sample_map.cc", - "metrics/persistent_sample_map.h", - "metrics/record_histogram_checker.h", - "metrics/sample_map.cc", - "metrics/sample_map.h", - "metrics/sample_vector.cc", - "metrics/sample_vector.h", - "metrics/single_sample_metrics.cc", - "metrics/single_sample_metrics.h", - "metrics/sparse_histogram.cc", - "metrics/sparse_histogram.h", - "metrics/statistics_recorder.cc", - "metrics/statistics_recorder.h", - "metrics/user_metrics.cc", - "metrics/user_metrics.h", - "metrics/user_metrics_action.h", - "native_library.cc", - "native_library.h", - "native_library_ios.mm", - "native_library_mac.mm", - "native_library_win.cc", - "nix/mime_util_xdg.cc", - "nix/mime_util_xdg.h", - "nix/xdg_util.cc", - "nix/xdg_util.h", - "no_destructor.h", - "observer_list.h", - "observer_list_threadsafe.cc", - "observer_list_threadsafe.h", - "optional.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", - "post_task_and_reply_with_result_internal.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_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_win.cc", - "process/launch.cc", - "process/launch.h", - "process/launch_ios.cc", - "process/launch_mac.cc", - "process/launch_win.cc", - "process/memory.cc", - "process/memory.h", - "process/memory_linux.cc", - "process/memory_mac.mm", - "process/memory_win.cc", - "process/port_provider_mac.cc", - "process/port_provider_mac.h", - "process/process.h", - "process/process_handle.cc", - "process/process_handle.h", - - #"process/process_handle_freebsd.cc", # Unused in Chromium build. - "process/process_handle_linux.cc", - "process/process_handle_mac.cc", - - #"process/process_handle_openbsd.cc", # Unused in Chromium build. - "process/process_handle_win.cc", - "process/process_info.h", - "process/process_info_linux.cc", - "process/process_info_mac.cc", - "process/process_info_win.cc", - "process/process_iterator.cc", - "process/process_iterator.h", - - #"process/process_iterator_freebsd.cc", # Unused in Chromium build. - "process/process_iterator_linux.cc", - "process/process_iterator_mac.cc", - - #"process/process_iterator_openbsd.cc", # Unused in Chromium build. - "process/process_iterator_win.cc", - "process/process_linux.cc", - "process/process_mac.cc", - "process/process_metrics.cc", - "process/process_metrics.h", - - #"process/process_metrics_freebsd.cc", # Unused in Chromium build. - "process/process_metrics_ios.cc", - "process/process_metrics_linux.cc", - "process/process_metrics_mac.cc", - - #"process/process_metrics_openbsd.cc", # Unused in Chromium build. - "process/process_metrics_win.cc", - "process/process_win.cc", - "profiler/native_stack_sampler.cc", - "profiler/native_stack_sampler.h", - "profiler/native_stack_sampler_mac.cc", - "profiler/native_stack_sampler_win.cc", - "profiler/stack_sampling_profiler.cc", - "profiler/stack_sampling_profiler.h", - "rand_util.cc", - "rand_util.h", - "rand_util_nacl.cc", - "rand_util_win.cc", - "run_loop.cc", - "run_loop.h", - "sampling_heap_profiler/lock_free_address_hash_set.cc", - "sampling_heap_profiler/lock_free_address_hash_set.h", - "sampling_heap_profiler/sampling_heap_profiler.cc", - "sampling_heap_profiler/sampling_heap_profiler.h", - "scoped_clear_errno.h", - "scoped_generic.h", - "scoped_native_library.cc", - "scoped_native_library.h", - "scoped_observer.h", - "sequence_checker.h", - "sequence_checker_impl.cc", - "sequence_checker_impl.h", - "sequence_token.cc", - "sequence_token.h", - "sequenced_task_runner.cc", - "sequenced_task_runner.h", - "sequenced_task_runner_helpers.h", - "sha1.cc", - "sha1.h", - "single_thread_task_runner.h", - "stl_util.h", - "strings/char_traits.h", - "strings/latin1_string_conversions.cc", - "strings/latin1_string_conversions.h", - "strings/nullable_string16.cc", - "strings/nullable_string16.h", - "strings/pattern.cc", - "strings/pattern.h", - "strings/safe_sprintf.cc", - "strings/safe_sprintf.h", - "strings/strcat.cc", - "strings/strcat.h", - "strings/string16.cc", - "strings/string16.h", - "strings/string_number_conversions.cc", - "strings/string_number_conversions.h", - "strings/string_piece.cc", - "strings/string_piece.h", - "strings/string_piece_forward.h", - "strings/string_split.cc", - "strings/string_split.h", - "strings/string_tokenizer.h", - "strings/string_util.cc", - "strings/string_util.h", - "strings/string_util_constants.cc", - "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_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_win.cc", - "synchronization/atomic_flag.cc", - "synchronization/atomic_flag.h", - "synchronization/cancellation_flag.h", - "synchronization/condition_variable.h", - "synchronization/condition_variable_win.cc", - "synchronization/lock.cc", - "synchronization/lock.h", - "synchronization/lock_impl.h", - "synchronization/lock_impl_win.cc", - "synchronization/spin_wait.h", - "synchronization/waitable_event.h", - "synchronization/waitable_event_mac.cc", - "synchronization/waitable_event_watcher.h", - "synchronization/waitable_event_watcher_mac.cc", - "synchronization/waitable_event_watcher_win.cc", - "synchronization/waitable_event_win.cc", - "sys_byteorder.h", - "sys_info.cc", - "sys_info.h", - "sys_info_android.cc", - "sys_info_chromeos.cc", - "sys_info_internal.h", - "syslog_logging.cc", - "syslog_logging.h", - - #"sys_info_freebsd.cc", # Unused in Chromium build. - "sys_info_ios.mm", - "sys_info_linux.cc", - "sys_info_mac.mm", - - #"sys_info_openbsd.cc", # Unused in Chromium build. - "sys_info_win.cc", - "system_monitor/system_monitor.cc", - "system_monitor/system_monitor.h", - "task/cancelable_task_tracker.cc", - "task/cancelable_task_tracker.h", - "task/sequence_manager/enqueue_order.cc", - "task/sequence_manager/enqueue_order.h", - "task/sequence_manager/graceful_queue_shutdown_helper.cc", - "task/sequence_manager/graceful_queue_shutdown_helper.h", - "task/sequence_manager/intrusive_heap.h", - "task/sequence_manager/lazily_deallocated_deque.h", - "task/sequence_manager/lazy_now.cc", - "task/sequence_manager/lazy_now.h", - "task/sequence_manager/real_time_domain.cc", - "task/sequence_manager/real_time_domain.h", - "task/sequence_manager/sequence_manager.h", - "task/sequence_manager/sequence_manager_impl.cc", - "task/sequence_manager/sequence_manager_impl.h", - "task/sequence_manager/sequenced_task_source.h", - "task/sequence_manager/task_queue.cc", - "task/sequence_manager/task_queue.h", - "task/sequence_manager/task_queue_impl.cc", - "task/sequence_manager/task_queue_impl.h", - "task/sequence_manager/task_queue_selector.cc", - "task/sequence_manager/task_queue_selector.h", - "task/sequence_manager/task_queue_selector_logic.h", - "task/sequence_manager/task_time_observer.h", - "task/sequence_manager/thread_controller.h", - "task/sequence_manager/thread_controller_impl.cc", - "task/sequence_manager/thread_controller_impl.h", - "task/sequence_manager/thread_controller_with_message_pump_impl.cc", - "task/sequence_manager/thread_controller_with_message_pump_impl.h", - "task/sequence_manager/time_domain.cc", - "task/sequence_manager/time_domain.h", - "task/sequence_manager/work_queue.cc", - "task/sequence_manager/work_queue.h", - "task/sequence_manager/work_queue_sets.cc", - "task/sequence_manager/work_queue_sets.h", - "task_runner.cc", - "task_runner.h", - "task_runner_util.h", - "task_scheduler/can_schedule_sequence_observer.h", - "task_scheduler/delayed_task_manager.cc", - "task_scheduler/delayed_task_manager.h", - "task_scheduler/environment_config.cc", - "task_scheduler/environment_config.h", - "task_scheduler/initialization_util.cc", - "task_scheduler/initialization_util.h", - "task_scheduler/lazy_task_runner.cc", - "task_scheduler/lazy_task_runner.h", - "task_scheduler/platform_native_worker_pool_win.cc", - "task_scheduler/platform_native_worker_pool_win.h", - "task_scheduler/post_task.cc", - "task_scheduler/post_task.h", - "task_scheduler/priority_queue.cc", - "task_scheduler/priority_queue.h", - "task_scheduler/scheduler_lock.h", - "task_scheduler/scheduler_lock_impl.cc", - "task_scheduler/scheduler_lock_impl.h", - "task_scheduler/scheduler_single_thread_task_runner_manager.cc", - "task_scheduler/scheduler_single_thread_task_runner_manager.h", - "task_scheduler/scheduler_worker.cc", - "task_scheduler/scheduler_worker.h", - "task_scheduler/scheduler_worker_observer.h", - "task_scheduler/scheduler_worker_params.h", - "task_scheduler/scheduler_worker_pool.cc", - "task_scheduler/scheduler_worker_pool.h", - "task_scheduler/scheduler_worker_pool_impl.cc", - "task_scheduler/scheduler_worker_pool_impl.h", - "task_scheduler/scheduler_worker_pool_params.cc", - "task_scheduler/scheduler_worker_pool_params.h", - "task_scheduler/scheduler_worker_stack.cc", - "task_scheduler/scheduler_worker_stack.h", - "task_scheduler/scoped_set_task_priority_for_current_thread.cc", - "task_scheduler/scoped_set_task_priority_for_current_thread.h", - "task_scheduler/sequence.cc", - "task_scheduler/sequence.h", - "task_scheduler/sequence_sort_key.cc", - "task_scheduler/sequence_sort_key.h", - "task_scheduler/service_thread.cc", - "task_scheduler/service_thread.h", - "task_scheduler/single_thread_task_runner_thread_mode.h", - "task_scheduler/task.cc", - "task_scheduler/task.h", - "task_scheduler/task_scheduler.cc", - "task_scheduler/task_scheduler.h", - "task_scheduler/task_scheduler_impl.cc", - "task_scheduler/task_scheduler_impl.h", - "task_scheduler/task_tracker.cc", - "task_scheduler/task_tracker.h", - "task_scheduler/task_traits.cc", - "task_scheduler/task_traits.h", - "task_scheduler/task_traits_details.h", - "task_scheduler/tracked_ref.h", - "template_util.h", - "test/malloc_wrapper.h", - "third_party/dmg_fp/dmg_fp.h", - "third_party/dmg_fp/dtoa_wrapper.cc", - "third_party/dmg_fp/g_fmt.cc", - "third_party/icu/icu_utf.cc", - "third_party/icu/icu_utf.h", - "third_party/nspr/prtime.cc", - "third_party/nspr/prtime.h", - "third_party/superfasthash/superfasthash.c", - "thread_annotations.h", - "threading/platform_thread.h", - "threading/platform_thread_android.cc", - "threading/platform_thread_linux.cc", - "threading/platform_thread_mac.mm", - "threading/platform_thread_win.cc", - "threading/post_task_and_reply_impl.cc", - "threading/post_task_and_reply_impl.h", - "threading/scoped_blocking_call.cc", - "threading/scoped_blocking_call.h", - "threading/sequence_local_storage_map.cc", - "threading/sequence_local_storage_map.h", - "threading/sequence_local_storage_slot.cc", - "threading/sequence_local_storage_slot.h", - "threading/sequenced_task_runner_handle.cc", - "threading/sequenced_task_runner_handle.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_storage.cc", - "threading/thread_local_storage.h", - "threading/thread_local_storage_win.cc", - "threading/thread_restrictions.cc", - "threading/thread_restrictions.h", - "threading/thread_task_runner_handle.cc", - "threading/thread_task_runner_handle.h", - "threading/watchdog.cc", - "threading/watchdog.h", - "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_override.cc", - "time/time_override.h", - "time/time_to_iso8601.cc", - "time/time_to_iso8601.h", - "timer/elapsed_timer.cc", - "timer/elapsed_timer.h", - "timer/hi_res_timer_manager.h", - "timer/hi_res_timer_manager_win.cc", - "timer/timer.cc", - "timer/timer.h", - "trace_event/auto_open_close_event.cc", - "trace_event/auto_open_close_event.h", - "trace_event/blame_context.cc", - "trace_event/blame_context.h", - "trace_event/category_registry.cc", - "trace_event/category_registry.h", - "trace_event/common/trace_event_common.h", - "trace_event/event_name_filter.cc", - "trace_event/event_name_filter.h", - "trace_event/heap_profiler.h", - "trace_event/heap_profiler_allocation_context.cc", - "trace_event/heap_profiler_allocation_context.h", - "trace_event/heap_profiler_allocation_context_tracker.cc", - "trace_event/heap_profiler_allocation_context_tracker.h", - "trace_event/heap_profiler_event_filter.cc", - "trace_event/heap_profiler_event_filter.h", - "trace_event/java_heap_dump_provider_android.cc", - "trace_event/java_heap_dump_provider_android.h", - "trace_event/malloc_dump_provider.cc", - "trace_event/malloc_dump_provider.h", - "trace_event/memory_allocator_dump.cc", - "trace_event/memory_allocator_dump.h", - "trace_event/memory_allocator_dump_guid.cc", - "trace_event/memory_allocator_dump_guid.h", - "trace_event/memory_dump_manager.cc", - "trace_event/memory_dump_manager.h", - "trace_event/memory_dump_manager_test_utils.h", - "trace_event/memory_dump_provider.h", - "trace_event/memory_dump_provider_info.cc", - "trace_event/memory_dump_provider_info.h", - "trace_event/memory_dump_request_args.cc", - "trace_event/memory_dump_request_args.h", - "trace_event/memory_dump_scheduler.cc", - "trace_event/memory_dump_scheduler.h", - "trace_event/memory_infra_background_whitelist.cc", - "trace_event/memory_infra_background_whitelist.h", - "trace_event/memory_usage_estimator.cc", - "trace_event/memory_usage_estimator.h", - "trace_event/process_memory_dump.cc", - "trace_event/process_memory_dump.h", - "trace_event/trace_buffer.cc", - "trace_event/trace_buffer.h", - "trace_event/trace_category.h", - "trace_event/trace_config.cc", - "trace_event/trace_config.h", - "trace_event/trace_config_category_filter.cc", - "trace_event/trace_config_category_filter.h", - "trace_event/trace_event.h", - "trace_event/trace_event_android.cc", - "trace_event/trace_event_argument.cc", - "trace_event/trace_event_argument.h", - "trace_event/trace_event_etw_export_win.cc", - "trace_event/trace_event_etw_export_win.h", - "trace_event/trace_event_filter.cc", - "trace_event/trace_event_filter.h", - "trace_event/trace_event_impl.cc", - "trace_event/trace_event_impl.h", - "trace_event/trace_event_memory_overhead.cc", - "trace_event/trace_event_memory_overhead.h", - "trace_event/trace_event_system_stats_monitor.cc", - "trace_event/trace_event_system_stats_monitor.h", - "trace_event/trace_log.cc", - "trace_event/trace_log.h", - "trace_event/trace_log_constants.cc", - "trace_event/tracing_agent.cc", - "trace_event/tracing_agent.h", - "tuple.h", - "unguessable_token.cc", - "unguessable_token.h", - "value_conversions.cc", - "value_conversions.h", - "value_iterators.cc", - "value_iterators.h", - "values.cc", - "values.h", - "version.cc", - "version.h", - "vlog.cc", - "vlog.h", - "win/async_operation.h", - "win/com_init_check_hook.cc", - "win/com_init_check_hook.h", - "win/com_init_util.cc", - "win/com_init_util.h", - "win/core_winrt_util.cc", - "win/core_winrt_util.h", - "win/current_module.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/object_watcher.cc", - "win/object_watcher.h", - "win/patch_util.cc", - "win/patch_util.h", - "win/process_startup_helper.cc", - "win/process_startup_helper.h", - "win/reference.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.cc", - "win/scoped_com_initializer.h", - "win/scoped_gdi_object.h", - "win/scoped_handle.cc", - "win/scoped_handle.h", - "win/scoped_handle_verifier.cc", - "win/scoped_handle_verifier.h", - "win/scoped_hdc.h", - "win/scoped_hglobal.h", - "win/scoped_hstring.cc", - "win/scoped_hstring.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/scoped_windows_thread_environment.h", - "win/scoped_winrt_initializer.cc", - "win/scoped_winrt_initializer.h", - "win/shortcut.cc", - "win/shortcut.h", - "win/startup_information.cc", - "win/startup_information.h", - "win/typed_event_handler.h", - "win/vector.cc", - "win/vector.h", - "win/wait_chain.cc", - "win/wait_chain.h", - "win/win_util.cc", - "win/win_util.h", - "win/windows_version.cc", - "win/windows_version.h", - "win/winrt_storage_util.cc", - "win/winrt_storage_util.h", - "win/wrapped_window_proc.cc", - "win/wrapped_window_proc.h", - ] - - # winternl.h and NTSecAPI.h have different definitions of UNICODE_STRING. - # There's only one client of NTSecAPI.h in base but several of winternl.h, - # so exclude the NTSecAPI.h one. - if (is_win) { - jumbo_excluded_sources = [ "rand_util_win.cc" ] - } - - if (is_posix) { - sources += [ - "base_paths_posix.h", - "debug/debugger_posix.cc", - "debug/stack_trace_posix.cc", - "file_descriptor_posix.h", - "files/dir_reader_posix.h", - "files/file_descriptor_watcher_posix.cc", - "files/file_descriptor_watcher_posix.h", - "files/file_enumerator_posix.cc", - "files/file_posix.cc", - "files/file_util_posix.cc", - "files/memory_mapped_file_posix.cc", - "memory/protected_memory_posix.cc", - "message_loop/watchable_io_message_pump_posix.cc", - "message_loop/watchable_io_message_pump_posix.h", - "native_library_posix.cc", - "posix/eintr_wrapper.h", - "posix/file_descriptor_shuffle.cc", - "posix/file_descriptor_shuffle.h", - "posix/global_descriptors.cc", - "posix/global_descriptors.h", - "posix/safe_strerror.cc", - "posix/safe_strerror.h", - "posix/unix_domain_socket.cc", - "posix/unix_domain_socket.h", - "process/kill_posix.cc", - "process/launch_posix.cc", - "process/process_handle_posix.cc", - "process/process_metrics_posix.cc", - "process/process_posix.cc", - "profiler/native_stack_sampler_posix.cc", - "rand_util_posix.cc", - "strings/string_util_posix.h", - "strings/sys_string_conversions_posix.cc", - "sync_socket_posix.cc", - "synchronization/condition_variable_posix.cc", - "synchronization/lock_impl_posix.cc", - "synchronization/waitable_event_posix.cc", - "synchronization/waitable_event_watcher_posix.cc", - "sys_info_posix.cc", - "task_scheduler/task_tracker_posix.cc", - "task_scheduler/task_tracker_posix.h", - "threading/platform_thread_internal_posix.cc", - "threading/platform_thread_internal_posix.h", - "threading/platform_thread_posix.cc", - "threading/thread_local_storage_posix.cc", - "timer/hi_res_timer_manager_posix.cc", - ] - } - - if (!is_nacl) { - sources += [ - "base_paths.cc", - "base_paths.h", - "base_paths_android.cc", - "base_paths_android.h", - "base_paths_mac.h", - "base_paths_mac.mm", - "base_paths_posix.h", - "base_paths_win.cc", - "base_paths_win.h", - "metrics/persistent_histogram_storage.cc", - "metrics/persistent_histogram_storage.h", - ] - - if (is_linux) { - sources += [ - "base_paths_posix.cc", - "debug/elf_reader_linux.cc", - "debug/elf_reader_linux.h", - ] - } - } - - all_dependent_configs = [] - defines = [] - data = [] - data_deps = [] - libs = [] - - configs += [ - ":base_flags", - ":base_implementation", - "//build/config:precompiled_headers", - "//build/config/compiler:noshadowing", - ] - - deps = [ - "//base/allocator", - "//base/allocator:buildflags", - "//base/third_party/dynamic_annotations", - "//third_party/modp_b64", - ] - - public_deps = [ - ":anchor_functions_buildflags", - ":base_static", - ":build_date", - ":cfi_buildflags", - ":debugging_buildflags", - ":orderfile_buildflags", - ":partition_alloc_buildflags", - ":protected_memory_buildflags", - ":synchronization_buildflags", - "//base/numerics:base_numerics", - ] - - # Needed for if using newer C++ library than sysroot, except if - # building inside the cros_sdk environment - use host_toolchain as a - # more robust check for this. - if (!use_sysroot && (is_android || (is_linux && !is_chromecast)) && - host_toolchain != "//build/toolchain/cros:host") { - libs += [ "atomic" ] - } - - if (use_allocator_shim) { - sources += [ - "allocator/allocator_shim.cc", - "allocator/allocator_shim.h", - "allocator/allocator_shim_internals.h", - "allocator/allocator_shim_override_cpp_symbols.h", - "allocator/allocator_shim_override_libc_symbols.h", - ] - if (is_win) { - sources += [ - "allocator/allocator_shim_default_dispatch_to_winheap.cc", - "allocator/allocator_shim_override_ucrt_symbols_win.h", - "allocator/winheap_stubs_win.cc", - "allocator/winheap_stubs_win.h", - ] - } else if (is_linux && use_allocator == "tcmalloc") { - sources += [ - "allocator/allocator_shim_default_dispatch_to_tcmalloc.cc", - "allocator/allocator_shim_override_glibc_weak_symbols.h", - ] - deps += [ "//base/allocator:tcmalloc" ] - } else if (is_linux && use_allocator == "none") { - sources += [ "allocator/allocator_shim_default_dispatch_to_glibc.cc" ] - } else if (is_android && use_allocator == "none") { - sources += [ - "allocator/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc", - "allocator/allocator_shim_override_linker_wrapped_symbols.h", - ] - all_dependent_configs += [ "//base/allocator:wrap_malloc_symbols" ] - } else if (is_mac) { - sources += [ - "allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.cc", - "allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.h", - "allocator/allocator_shim_override_mac_symbols.h", - ] - } - } - - # Allow more direct string conversions on platforms with native utf8 - # strings - if (is_mac || is_ios || is_chromeos || is_chromecast || is_fuchsia) { - defines += [ "SYSTEM_NATIVE_UTF8" ] - } - - # Android. - if (is_android) { - sources -= [ "debug/stack_trace_posix.cc" ] - sources += [ - "memory/platform_shared_memory_region_android.cc", - "memory/shared_memory_android.cc", - "memory/shared_memory_handle_android.cc", - "time/time_android.cc", - ] - - # Android uses some Linux sources, put those back. - set_sources_assignment_filter([]) - sources += [ - "debug/elf_reader_linux.cc", - "debug/elf_reader_linux.h", - "debug/proc_maps_linux.cc", - "debug/proc_maps_linux.h", - "files/file_path_watcher_linux.cc", - "power_monitor/power_monitor_device_source_android.cc", - "process/internal_linux.cc", - "process/internal_linux.h", - "process/memory_linux.cc", - "process/process_handle_linux.cc", - "process/process_info_linux.cc", - "process/process_iterator_linux.cc", - "process/process_metrics_linux.cc", - "sys_info_linux.cc", - ] - set_sources_assignment_filter(sources_assignment_filter) - - deps += [ - ":base_jni_headers", - "//third_party/android_tools:cpu_features", - "//third_party/ashmem", - ] - - # TODO(thomasanderson): Remove this once use_custom_libcxx is always set to - # true on Android. - if (!use_custom_libcxx) { - deps += [ "//buildtools/third_party/libc++abi:cxa_demangle_stub" ] - } - - # Needs to be a public config so that dependent targets link against it as - # well when doing a component build. - public_configs = [ ":android_system_libs" ] - - if (can_unwind_with_cfi_table) { - sources += [ - "trace_event/cfi_backtrace_android.cc", - "trace_event/cfi_backtrace_android.h", - ] - } - - # This is actually a linker script, but it can be added to the link in the - # same way as a library. - libs += [ "android/library_loader/anchor_functions.lds" ] - } - - # Chromeos. - if (is_chromeos) { - sources += [ "power_monitor/power_monitor_device_source_chromeos.cc" ] - } - - # Fuchsia. - if (is_fuchsia) { - sources += [ - "base_paths_fuchsia.cc", - "base_paths_fuchsia.h", - "debug/debugger_posix.cc", - "debug/stack_trace_fuchsia.cc", - "file_descriptor_posix.h", - "files/dir_reader_posix.h", - "files/file_descriptor_watcher_posix.cc", - "files/file_descriptor_watcher_posix.h", - "files/file_enumerator_posix.cc", - "files/file_path_watcher_fuchsia.cc", - "files/file_posix.cc", - "files/file_util_posix.cc", - "files/memory_mapped_file_posix.cc", - "fuchsia/async_dispatcher.cc", - "fuchsia/async_dispatcher.h", - "fuchsia/component_context.cc", - "fuchsia/component_context.h", - "fuchsia/default_job.cc", - "fuchsia/default_job.h", - "fuchsia/fidl_interface_request.cc", - "fuchsia/fidl_interface_request.h", - "fuchsia/file_utils.cc", - "fuchsia/file_utils.h", - "fuchsia/filtered_service_directory.cc", - "fuchsia/filtered_service_directory.h", - "fuchsia/fuchsia_logging.cc", - "fuchsia/fuchsia_logging.h", - "fuchsia/scoped_zx_handle.h", - "fuchsia/service_directory.cc", - "fuchsia/service_directory.h", - "memory/platform_shared_memory_region_fuchsia.cc", - "memory/protected_memory_posix.cc", - "memory/shared_memory_fuchsia.cc", - "memory/shared_memory_handle_fuchsia.cc", - "message_loop/message_pump_fuchsia.cc", - "message_loop/message_pump_fuchsia.h", - "message_loop/watchable_io_message_pump_posix.cc", - "message_loop/watchable_io_message_pump_posix.h", - "native_library_fuchsia.cc", - "posix/eintr_wrapper.h", - "posix/file_descriptor_shuffle.cc", - "posix/file_descriptor_shuffle.h", - "posix/global_descriptors.cc", - "posix/global_descriptors.h", - "posix/safe_strerror.cc", - "posix/safe_strerror.h", - "process/kill_fuchsia.cc", - "process/launch_fuchsia.cc", - "process/memory_fuchsia.cc", - "process/process_fuchsia.cc", - "process/process_handle_fuchsia.cc", - "process/process_iterator_fuchsia.cc", - "process/process_metrics_fuchsia.cc", - "process/process_metrics_posix.cc", - "profiler/native_stack_sampler_posix.cc", - "rand_util_fuchsia.cc", - "strings/string_util_posix.h", - "strings/sys_string_conversions_posix.cc", - "sync_socket_posix.cc", - "synchronization/condition_variable_posix.cc", - "synchronization/lock_impl_posix.cc", - "synchronization/waitable_event_posix.cc", - "synchronization/waitable_event_watcher_posix.cc", - "sys_info_fuchsia.cc", - "sys_info_posix.cc", - "task_scheduler/task_tracker_posix.cc", - "task_scheduler/task_tracker_posix.h", - "threading/platform_thread_fuchsia.cc", - "threading/platform_thread_posix.cc", - "threading/thread_local_storage_posix.cc", - "time/time_conversion_posix.cc", - "time/time_exploded_posix.cc", - "time/time_fuchsia.cc", - "timer/hi_res_timer_manager_posix.cc", - ] - - # These only need to be public deps because of includes of their headers - # by public //base headers, which requires they be on the include path. - # TODO(https://crbug.com/841171): Move these back to |deps|. - public_deps += [ - "//third_party/fuchsia-sdk:async", - "//third_party/fuchsia-sdk:fdio", - "//third_party/fuchsia-sdk:zx", - ] - - deps += [ - "//third_party/fuchsia-sdk:async_default", - "//third_party/fuchsia-sdk:fidl", - "//third_party/fuchsia-sdk:svc", - ] - } - - # NaCl. - if (is_nacl) { - # We reset sources_assignment_filter in order to explicitly include - # the linux file (which would otherwise be filtered out). - set_sources_assignment_filter([]) - sources += [ - "files/file_path_watcher_stub.cc", - "memory/shared_memory_nacl.cc", - "process/process_metrics_nacl.cc", - "sync_socket_nacl.cc", - "threading/platform_thread_linux.cc", - ] - set_sources_assignment_filter(sources_assignment_filter) - - sources -= [ - "cpu.cc", - "debug/crash_logging.cc", - "debug/crash_logging.h", - "debug/stack_trace.cc", - "debug/stack_trace_posix.cc", - "files/file_enumerator_posix.cc", - "files/file_proxy.cc", - "files/important_file_writer.cc", - "files/important_file_writer.h", - "files/scoped_temp_dir.cc", - "memory/discardable_memory.cc", - "memory/discardable_memory.h", - "memory/discardable_memory_allocator.cc", - "memory/discardable_memory_allocator.h", - "memory/discardable_shared_memory.cc", - "memory/discardable_shared_memory.h", - "memory/shared_memory_helper.cc", - "memory/shared_memory_helper.h", - "native_library.cc", - "native_library_posix.cc", - "path_service.cc", - "process/kill.cc", - "process/kill.h", - "process/memory.cc", - "process/memory.h", - "process/process_iterator.cc", - "process/process_iterator.h", - "process/process_metrics.cc", - "process/process_metrics_posix.cc", - "process/process_posix.cc", - "scoped_native_library.cc", - "sync_socket_posix.cc", - "sys_info.cc", - "sys_info_posix.cc", - "task_scheduler/initialization_util.cc", - "task_scheduler/initialization_util.h", - "trace_event/trace_event_system_stats_monitor.cc", - ] - - if (is_nacl_nonsfi) { - sources -= [ "rand_util_nacl.cc" ] - configs += [ ":nacl_nonsfi_warnings" ] - } else { - sources -= [ - "files/file_descriptor_watcher_posix.cc", - "files/file_descriptor_watcher_posix.h", - "files/file_util.cc", - "files/file_util.h", - "files/file_util_posix.cc", - "json/json_file_value_serializer.cc", - "json/json_file_value_serializer.h", - "posix/unix_domain_socket.cc", - "process/kill_posix.cc", - "process/launch.cc", - "process/launch.h", - "process/launch_posix.cc", - "rand_util_posix.cc", - "task_scheduler/task_tracker_posix.cc", - "task_scheduler/task_tracker_posix.h", - ] - } - } else { - # Remove NaCl stuff. - sources -= [ - "os_compat_nacl.cc", - "os_compat_nacl.h", - "rand_util_nacl.cc", - ] - - if (use_partition_alloc) { - # Add stuff that doesn't work in NaCl. - sources += [ - # PartitionAlloc uses SpinLock, which doesn't work in NaCl (see below). - "allocator/partition_allocator/address_space_randomization.cc", - "allocator/partition_allocator/address_space_randomization.h", - "allocator/partition_allocator/oom.h", - "allocator/partition_allocator/page_allocator.cc", - "allocator/partition_allocator/page_allocator.h", - "allocator/partition_allocator/page_allocator_internal.h", - "allocator/partition_allocator/partition_alloc.cc", - "allocator/partition_allocator/partition_alloc.h", - "allocator/partition_allocator/partition_alloc_constants.h", - "allocator/partition_allocator/partition_bucket.cc", - "allocator/partition_allocator/partition_bucket.h", - "allocator/partition_allocator/partition_cookie.h", - "allocator/partition_allocator/partition_direct_map_extent.h", - "allocator/partition_allocator/partition_freelist_entry.h", - "allocator/partition_allocator/partition_oom.cc", - "allocator/partition_allocator/partition_oom.h", - "allocator/partition_allocator/partition_page.cc", - "allocator/partition_allocator/partition_page.h", - "allocator/partition_allocator/partition_root_base.cc", - "allocator/partition_allocator/partition_root_base.h", - "allocator/partition_allocator/spin_lock.cc", - "allocator/partition_allocator/spin_lock.h", - ] - if (is_win) { - sources += - [ "allocator/partition_allocator/page_allocator_internals_win.h" ] - } else if (is_posix || is_fuchsia) { - sources += - [ "allocator/partition_allocator/page_allocator_internals_posix.h" ] - } - } - } - - # Windows. - if (is_win) { - sources += [ - "memory/platform_shared_memory_region_win.cc", - "memory/shared_memory_handle_win.cc", - "memory/shared_memory_win.cc", - "power_monitor/power_monitor_device_source_win.cc", - "profiler/win32_stack_frame_unwinder.cc", - "profiler/win32_stack_frame_unwinder.h", - "time/time_win.cc", - ] - - sources -= [ - "file_descriptor_store.cc", - "file_descriptor_store.h", - "memory/shared_memory_helper.cc", - "memory/shared_memory_helper.h", - "strings/string16.cc", - ] - - deps += [ - "//base/trace_event/etw_manifest:chrome_events_win", - "//base/win:base_win_buildflags", - ] - - data_deps += [ "//build/win:runtime_libs" ] - - if (com_init_check_hook_disabled) { - defines += [ "COM_INIT_CHECK_HOOK_DISABLED" ] - } - - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] - - libs += [ - "cfgmgr32.lib", - "powrprof.lib", - "propsys.lib", - "setupapi.lib", - "userenv.lib", - "winmm.lib", - ] - all_dependent_configs += [ - ":base_win_linker_flags", - "//tools/win/DebugVisualizers:chrome", - ] - } - - # Desktop Mac. - if (is_mac) { - sources -= [ "profiler/native_stack_sampler_posix.cc" ] - sources += [ - "mac/scoped_typeref.h", - "memory/platform_shared_memory_region_mac.cc", - "memory/shared_memory_handle_mac.cc", - "memory/shared_memory_mac.cc", - "power_monitor/power_monitor_device_source_mac.mm", - "time/time_conversion_posix.cc", - "time/time_exploded_posix.cc", - "time/time_mac.cc", - ] - - libs += [ - "ApplicationServices.framework", - "AppKit.framework", - "bsm", - "CoreFoundation.framework", - "IOKit.framework", - "Security.framework", - ] - } - - # Mac or iOS. - if (is_mac || is_ios) { - sources -= [ - "native_library_posix.cc", - "strings/sys_string_conversions_posix.cc", - "synchronization/waitable_event_posix.cc", - "synchronization/waitable_event_watcher_posix.cc", - "threading/platform_thread_internal_posix.cc", - ] - } else { - # Non-Mac/ios. - 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 = [] - if (use_glib) { - linux_configs += [ "//build/config/linux:glib" ] - } - - defines += [ "USE_SYMBOLIZE" ] - - 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 (!is_android) { - sources -= [ - "linux_util.cc", - "linux_util.h", - ] - } - } - - # iOS - if (is_ios) { - set_sources_assignment_filter([]) - - sources -= [ - "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", - "memory/discardable_shared_memory.cc", - "memory/discardable_shared_memory.h", - "process/kill.cc", - "process/kill.h", - "process/kill_posix.cc", - "process/launch.cc", - "process/launch.h", - "process/launch_posix.cc", - "process/memory.cc", - "process/memory.h", - "process/process_iterator.cc", - "process/process_iterator.h", - "process/process_metrics_posix.cc", - "process/process_posix.cc", - "sync_socket.h", - "sync_socket_posix.cc", - "synchronization/waitable_event_watcher.h", - ] - sources += [ - "base_paths_mac.h", - "base_paths_mac.mm", - "file_version_info_mac.h", - "file_version_info_mac.mm", - "files/file_util_mac.mm", - "mac/bundle_locations.h", - "mac/bundle_locations.mm", - "mac/call_with_eh_frame.cc", - "mac/call_with_eh_frame.h", - "mac/foundation_util.h", - "mac/foundation_util.mm", - "mac/mac_logging.h", - "mac/mac_logging.mm", - "mac/mach_logging.cc", - "mac/mach_logging.h", - "mac/objc_release_properties.h", - "mac/objc_release_properties.mm", - "mac/scoped_block.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_nsobject.h", - "mac/scoped_nsobject.mm", - "mac/scoped_objc_class_swizzler.h", - "mac/scoped_objc_class_swizzler.mm", - "mac/scoped_typeref.h", - "message_loop/message_pump_mac.h", - "message_loop/message_pump_mac.mm", - "power_monitor/power_monitor_device_source_ios.mm", - "process/memory_stubs.cc", - "strings/sys_string_conversions_mac.mm", - "synchronization/waitable_event_mac.cc", - "threading/platform_thread_mac.mm", - "time/time_conversion_posix.cc", - "time/time_mac.cc", - ] - - set_sources_assignment_filter(sources_assignment_filter) - } - - if (dep_libevent) { - deps += [ "//base/third_party/libevent" ] - } - - if (use_libevent) { - sources += [ - "message_loop/message_pump_libevent.cc", - "message_loop/message_pump_libevent.h", - ] - } - - # Android and MacOS have their own custom shared memory handle - # implementations. e.g. due to supporting both POSIX and native handles. - if (is_posix && !is_android && !is_mac) { - sources += [ - "memory/platform_shared_memory_region_posix.cc", - "memory/shared_memory_handle_posix.cc", - ] - } - - if (is_posix && !is_mac && !is_nacl) { - sources += [ "memory/shared_memory_posix.cc" ] - } - - if (is_posix && !is_mac && !is_ios) { - sources += [ - "time/time_conversion_posix.cc", - "time/time_exploded_posix.cc", - "time/time_now_posix.cc", - ] - } - - if ((is_posix && !is_mac && !is_ios && !is_android && !is_chromeos) || - is_fuchsia) { - sources += [ "power_monitor/power_monitor_device_source_stub.cc" ] - } - - if (!use_glib) { - sources -= [ - "message_loop/message_pump_glib.cc", - "message_loop/message_pump_glib.h", - ] - } - - if (using_sanitizer) { - data += [ "//tools/valgrind/asan/" ] - if (is_win) { - data += - [ "//third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer.exe" ] - } else { - data += [ "//third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer" ] - } - } - - configs += [ "//build/config/compiler:wexit_time_destructors" ] - if (!is_debug) { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_max" ] - } -} - -# Build flags for Control Flow Integrity -# https://www.chromium.org/developers/testing/control-flow-integrity -buildflag_header("cfi_buildflags") { - header = "cfi_buildflags.h" - - # buildflag entries added to this header must also must be manually added to - # tools/gn/bootstrap/bootstrap.py - flags = [ - # TODO(pcc): remove CFI_CAST_CHECK, see https://crbug.com/626794. - "CFI_CAST_CHECK=$is_cfi && $use_cfi_cast", - "CFI_ICALL_CHECK=$is_cfi && $use_cfi_icall", - "CFI_ENFORCEMENT_TRAP=$is_cfi && !$use_cfi_diag", - "CFI_ENFORCEMENT_DIAGNOSTIC=$is_cfi && $use_cfi_diag && !$use_cfi_recover", - ] -} - -buildflag_header("debugging_buildflags") { - header = "debugging_buildflags.h" - header_dir = "base/debug" - - # buildflag entries added to this header must also must be manually added to - # tools/gn/bootstrap/bootstrap.py - flags = [ - "ENABLE_LOCATION_SOURCE=$enable_location_source", - "ENABLE_PROFILING=$enable_profiling", - "CAN_UNWIND_WITH_FRAME_POINTERS=$can_unwind_with_frame_pointers", - "UNSAFE_DEVELOPER_BUILD=$is_unsafe_developer_build", - "CAN_UNWIND_WITH_CFI_TABLE=$can_unwind_with_cfi_table", - ] -} - -buildflag_header("orderfile_buildflags") { - header = "orderfile_buildflags.h" - header_dir = "base/android/orderfile" - using_order_profiling = is_android && use_order_profiling - using_devtools_dumping = is_android && devtools_instrumentation_dumping - flags = [ - "DEVTOOLS_INSTRUMENTATION_DUMPING=$using_devtools_dumping", - "ORDERFILE_INSTRUMENTATION=$using_order_profiling", - ] -} - -# Build flags for ProtectedMemory, temporary workaround for crbug.com/792777 -# TODO(vtsyrklevich): Remove once support for gold on Android/CrOs is dropped -buildflag_header("protected_memory_buildflags") { - header = "protected_memory_buildflags.h" - header_dir = "base/memory" - - # buildflag entries added to this header must also must be manually added to - # tools/gn/bootstrap/bootstrap.py - flags = [ "USE_LLD=$use_lld" ] -} - -buildflag_header("synchronization_buildflags") { - header = "synchronization_buildflags.h" - header_dir = "base/synchronization" - - flags = - [ "ENABLE_MUTEX_PRIORITY_INHERITANCE=$enable_mutex_priority_inheritance" ] -} - -buildflag_header("anchor_functions_buildflags") { - header = "anchor_functions_buildflags.h" - header_dir = "base/android/library_loader" - _supports_code_ordering = current_cpu == "arm" - - # buildflag entries added to this header must also must be manually added to - # tools/gn/bootstrap/bootstrap.py - flags = [ - "USE_LLD=$use_lld", - "SUPPORTS_CODE_ORDERING=$_supports_code_ordering", - ] -} - -buildflag_header("partition_alloc_buildflags") { - header = "partition_alloc_buildflags.h" - header_dir = "base" - - flags = [ "USE_PARTITION_ALLOC=$use_partition_alloc" ] -} - -# 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. -static_library("base_static") { - sources = [ - "base_switches.cc", - "base_switches.h", - ] - - if (is_win) { - public_deps = [ - "//base/win:pe_image", - ] - - # Disable sanitizer coverage in win/pe_image.cc. It is called by the sandbox - # before sanitizer coverage can initialize. http://crbug.com/484711 - configs -= [ "//build/config/sanitizers:default_sanitizer_flags" ] - configs += - [ "//build/config/sanitizers:default_sanitizer_flags_but_coverage" ] - } - - if (!is_debug) { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_max" ] - } -} - -component("i18n") { - output_name = "base_i18n" - sources = [ - "i18n/base_i18n_export.h", - "i18n/base_i18n_switches.cc", - "i18n/base_i18n_switches.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/character_encoding.cc", - "i18n/character_encoding.h", - "i18n/encoding_detection.cc", - "i18n/encoding_detection.h", - "i18n/file_util_icu.cc", - "i18n/file_util_icu.h", - "i18n/i18n_constants.cc", - "i18n/i18n_constants.h", - "i18n/icu_string_conversions.cc", - "i18n/icu_string_conversions.h", - "i18n/icu_util.cc", - "i18n/icu_util.h", - "i18n/message_formatter.cc", - "i18n/message_formatter.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/unicodestring.h", - "i18n/utf8_validator_tables.cc", - "i18n/utf8_validator_tables.h", - ] - defines = [ "BASE_I18N_IMPLEMENTATION" ] - configs += [ "//build/config/compiler:wexit_time_destructors" ] - public_deps = [ - "//third_party/ced", - "//third_party/icu", - ] - deps = [ - ":base", - "//base/third_party/dynamic_annotations", - ] - - if (!is_debug) { - configs -= [ "//build/config/compiler:default_optimization" ] - configs += [ "//build/config/compiler:optimize_max" ] - } - - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] - - if (is_mac) { - libs = [ "CoreFoundation.framework" ] - } -} - -test("base_perftests") { - sources = [ - "message_loop/message_loop_perftest.cc", - "message_loop/message_loop_task_runner_perftest.cc", - "message_loop/message_pump_perftest.cc", - - # "test/run_all_unittests.cc", - "json/json_perftest.cc", - "synchronization/waitable_event_perftest.cc", - "threading/thread_perftest.cc", - ] - deps = [ - ":base", - "//base/test:test_support", - "//base/test:test_support_perf", - "//testing/gtest", - "//testing/perf", - ] - - if (is_android) { - deps += [ "//testing/android/native_test:native_test_native_code" ] - } -} - -test("base_i18n_perftests") { - sources = [ - "i18n/streaming_utf8_validator_perftest.cc", - ] - deps = [ - ":base", - ":i18n", - "//base/test:test_support", - "//base/test:test_support_perf", - "//testing/gtest", - ] -} - -if (!is_ios) { - executable("build_utf8_validator_tables") { - sources = [ - "i18n/build_utf8_validator_tables.cc", - ] - deps = [ - ":base", - "//build/win:default_exe_manifest", - "//third_party/icu:icuuc", - ] - } - - executable("check_example") { - sources = [ - "check_example.cc", - ] - deps = [ - ":base", - "//build/win:default_exe_manifest", - ] - } -} - -if (is_win) { - # Target to manually rebuild pe_image_test.dll which is checked into - # base/test/data/pe_image. - shared_library("pe_image_test") { - sources = [ - "win/pe_image_test.cc", - ] - ldflags = [ - "/DELAYLOAD:cfgmgr32.dll", - "/DELAYLOAD:shell32.dll", - "/SUBSYSTEM:WINDOWS", - ] - libs = [ - "cfgmgr32.lib", - "shell32.lib", - ] - } - - loadable_module("scoped_handle_test_dll") { - sources = [ - "win/scoped_handle_test_dll.cc", - ] - deps = [ - ":base", - "//base/win:base_win_buildflags", - ] - } -} - -if (is_win || is_mac) { - if (current_cpu == "x64") { - # Must be a shared library so that it can be unloaded during testing. - shared_library("base_profiler_test_support_library") { - sources = [ - "profiler/test_support_library.cc", - ] - } - } -} - -bundle_data("base_unittests_bundle_data") { - testonly = true - sources = [ - "//tools/metrics/histograms/enums.xml", - "test/data/file_util/binary_file.bin", - "test/data/file_util/binary_file_diff.bin", - "test/data/file_util/binary_file_same.bin", - "test/data/file_util/blank_line.txt", - "test/data/file_util/blank_line_crlf.txt", - "test/data/file_util/crlf.txt", - "test/data/file_util/different.txt", - "test/data/file_util/different_first.txt", - "test/data/file_util/different_last.txt", - "test/data/file_util/empty1.txt", - "test/data/file_util/empty2.txt", - "test/data/file_util/first1.txt", - "test/data/file_util/first2.txt", - "test/data/file_util/original.txt", - "test/data/file_util/same.txt", - "test/data/file_util/same_length.txt", - "test/data/file_util/shortened.txt", - "test/data/json/bom_feff.json", - "test/data/serializer_nested_test.json", - "test/data/serializer_test.json", - "test/data/serializer_test_nowhitespace.json", - ] - outputs = [ - "{{bundle_resources_dir}}/" + - "{{source_root_relative_dir}}/{{source_file_part}}", - ] -} - -if (is_ios || is_mac) { - source_set("base_unittests_arc") { - testonly = true - set_sources_assignment_filter([]) - sources = [ - "mac/bind_objc_block_unittest_arc.mm", - "mac/scoped_nsobject_unittest_arc.mm", - ] - set_sources_assignment_filter(sources_assignment_filter) - configs += [ "//build/config/compiler:enable_arc" ] - deps = [ - ":base", - "//testing/gtest", - ] - } -} - -if (is_fuchsia) { - fidl_library("test_fidl") { - namespace = "base.fuchsia" - namespace_path = "base/fuchsia" - - sources = [ - "fuchsia/test.fidl", - ] - } -} - -test("base_unittests") { - sources = [ - "allocator/allocator_interception_mac_unittest.mm", - "allocator/malloc_zone_functions_mac_unittest.cc", - "allocator/tcmalloc_unittest.cc", - "android/application_status_listener_unittest.cc", - "android/content_uri_utils_unittest.cc", - "android/jni_android_unittest.cc", - "android/jni_array_unittest.cc", - "android/jni_string_unittest.cc", - "android/library_loader/library_prefetcher_unittest.cc", - "android/path_utils_unittest.cc", - "android/scoped_java_ref_unittest.cc", - "android/sys_utils_unittest.cc", - "android/unguessable_token_android_unittest.cc", - "at_exit_unittest.cc", - "atomicops_unittest.cc", - "barrier_closure_unittest.cc", - "base64_unittest.cc", - "base64url_unittest.cc", - "big_endian_unittest.cc", - "bind_unittest.cc", - "bit_cast_unittest.cc", - "bits_unittest.cc", - "build_time_unittest.cc", - "callback_helpers_unittest.cc", - "callback_list_unittest.cc", - "callback_unittest.cc", - "cancelable_callback_unittest.cc", - "command_line_unittest.cc", - "component_export_unittest.cc", - "containers/adapters_unittest.cc", - "containers/circular_deque_unittest.cc", - "containers/flat_map_unittest.cc", - "containers/flat_set_unittest.cc", - "containers/flat_tree_unittest.cc", - "containers/hash_tables_unittest.cc", - "containers/id_map_unittest.cc", - "containers/linked_list_unittest.cc", - "containers/mru_cache_unittest.cc", - "containers/small_map_unittest.cc", - "containers/span_unittest.cc", - "containers/stack_container_unittest.cc", - "containers/unique_ptr_adapters_unittest.cc", - "containers/vector_buffer_unittest.cc", - "cpu_unittest.cc", - "debug/activity_analyzer_unittest.cc", - "debug/activity_tracker_unittest.cc", - "debug/alias_unittest.cc", - "debug/crash_logging_unittest.cc", - "debug/debugger_unittest.cc", - "debug/elf_reader_linux_unittest.cc", - "debug/leak_tracker_unittest.cc", - "debug/proc_maps_linux_unittest.cc", - "debug/stack_trace_unittest.cc", - "debug/task_annotator_unittest.cc", - "debug/thread_heap_usage_tracker_unittest.cc", - "deferred_sequenced_task_runner_unittest.cc", - "environment_unittest.cc", - "feature_list_unittest.cc", - "file_version_info_win_unittest.cc", - "files/file_enumerator_unittest.cc", - "files/file_path_unittest.cc", - "files/file_path_watcher_unittest.cc", - "files/file_proxy_unittest.cc", - "files/file_unittest.cc", - "files/file_util_unittest.cc", - "files/important_file_writer_unittest.cc", - "files/memory_mapped_file_unittest.cc", - "files/scoped_temp_dir_unittest.cc", - "gmock_unittest.cc", - "guid_unittest.cc", - "hash_unittest.cc", - "i18n/bidi_line_iterator_unittest.cc", - "i18n/break_iterator_unittest.cc", - "i18n/case_conversion_unittest.cc", - "i18n/char_iterator_unittest.cc", - "i18n/character_encoding_unittest.cc", - "i18n/file_util_icu_unittest.cc", - "i18n/icu_string_conversions_unittest.cc", - "i18n/message_formatter_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", - "ios/crb_protocol_observers_unittest.mm", - "ios/device_util_unittest.mm", - "ios/weak_nsobject_unittest.mm", - "json/json_parser_unittest.cc", - "json/json_reader_unittest.cc", - "json/json_value_converter_unittest.cc", - "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/call_with_eh_frame_unittest.mm", - "mac/dispatch_source_mach_unittest.cc", - "mac/foundation_util_unittest.mm", - "mac/mac_util_unittest.mm", - "mac/mach_port_broker_unittest.cc", - "mac/objc_release_properties_unittest.mm", - "mac/scoped_nsobject_unittest.mm", - "mac/scoped_objc_class_swizzler_unittest.mm", - "mac/scoped_sending_event_unittest.mm", - "md5_unittest.cc", - "memory/aligned_memory_unittest.cc", - "memory/discardable_shared_memory_unittest.cc", - "memory/linked_ptr_unittest.cc", - "memory/memory_coordinator_client_registry_unittest.cc", - "memory/memory_pressure_listener_unittest.cc", - "memory/memory_pressure_monitor_chromeos_unittest.cc", - "memory/memory_pressure_monitor_mac_unittest.cc", - "memory/memory_pressure_monitor_unittest.cc", - "memory/memory_pressure_monitor_win_unittest.cc", - "memory/platform_shared_memory_region_unittest.cc", - "memory/protected_memory_unittest.cc", - "memory/ptr_util_unittest.cc", - "memory/ref_counted_memory_unittest.cc", - "memory/ref_counted_unittest.cc", - "memory/shared_memory_mac_unittest.cc", - "memory/shared_memory_region_unittest.cc", - "memory/shared_memory_unittest.cc", - "memory/shared_memory_win_unittest.cc", - "memory/singleton_unittest.cc", - "memory/weak_ptr_unittest.cc", - "message_loop/message_loop_task_runner_unittest.cc", - "message_loop/message_loop_unittest.cc", - "message_loop/message_pump_glib_unittest.cc", - "message_loop/message_pump_io_ios_unittest.cc", - "message_loop/message_pump_mac_unittest.mm", - "metrics/bucket_ranges_unittest.cc", - "metrics/field_trial_params_unittest.cc", - "metrics/field_trial_unittest.cc", - "metrics/histogram_base_unittest.cc", - "metrics/histogram_delta_serialization_unittest.cc", - "metrics/histogram_functions_unittest.cc", - "metrics/histogram_macros_unittest.cc", - "metrics/histogram_samples_unittest.cc", - "metrics/histogram_snapshot_manager_unittest.cc", - "metrics/histogram_unittest.cc", - "metrics/metrics_hashes_unittest.cc", - "metrics/persistent_histogram_allocator_unittest.cc", - "metrics/persistent_histogram_storage_unittest.cc", - "metrics/persistent_memory_allocator_unittest.cc", - "metrics/persistent_sample_map_unittest.cc", - "metrics/sample_map_unittest.cc", - "metrics/sample_vector_unittest.cc", - "metrics/single_sample_metrics_unittest.cc", - "metrics/sparse_histogram_unittest.cc", - "metrics/statistics_recorder_unittest.cc", - "native_library_unittest.cc", - "no_destructor_unittest.cc", - "observer_list_unittest.cc", - "optional_unittest.cc", - "os_compat_android_unittest.cc", - "path_service_unittest.cc", - "pickle_unittest.cc", - "power_monitor/power_monitor_unittest.cc", - "process/launch_unittest_win.cc", - "process/memory_unittest.cc", - "process/memory_unittest_mac.h", - "process/memory_unittest_mac.mm", - "process/process_info_unittest.cc", - "process/process_metrics_unittest.cc", - "process/process_unittest.cc", - "process/process_util_unittest.cc", - "profiler/stack_sampling_profiler_unittest.cc", - "rand_util_unittest.cc", - "run_loop_unittest.cc", - "safe_numerics_unittest.cc", - "sampling_heap_profiler/lock_free_address_hash_set_unittest.cc", - "scoped_clear_errno_unittest.cc", - "scoped_generic_unittest.cc", - "scoped_native_library_unittest.cc", - "security_unittest.cc", - "sequence_checker_unittest.cc", - "sequence_token_unittest.cc", - "sequenced_task_runner_unittest.cc", - "sha1_unittest.cc", - "stl_util_unittest.cc", - "strings/char_traits_unittest.cc", - "strings/nullable_string16_unittest.cc", - "strings/pattern_unittest.cc", - "strings/safe_sprintf_unittest.cc", - "strings/strcat_unittest.cc", - "strings/string16_unittest.cc", - "strings/string_number_conversions_unittest.cc", - "strings/string_piece_unittest.cc", - "strings/string_split_unittest.cc", - "strings/string_tokenizer_unittest.cc", - "strings/string_util_unittest.cc", - "strings/stringize_macros_unittest.cc", - "strings/stringprintf_unittest.cc", - "strings/sys_string_conversions_mac_unittest.mm", - "strings/sys_string_conversions_unittest.cc", - "strings/utf_offset_string_conversions_unittest.cc", - "strings/utf_string_conversions_unittest.cc", - "supports_user_data_unittest.cc", - "sync_socket_unittest.cc", - "synchronization/atomic_flag_unittest.cc", - "synchronization/condition_variable_unittest.cc", - "synchronization/lock_unittest.cc", - "synchronization/waitable_event_unittest.cc", - "synchronization/waitable_event_watcher_unittest.cc", - "sys_byteorder_unittest.cc", - "sys_info_unittest.cc", - "system_monitor/system_monitor_unittest.cc", - "task/cancelable_task_tracker_unittest.cc", - "task_runner_util_unittest.cc", - "task_scheduler/delayed_task_manager_unittest.cc", - "task_scheduler/lazy_task_runner_unittest.cc", - "task_scheduler/priority_queue_unittest.cc", - "task_scheduler/scheduler_lock_unittest.cc", - "task_scheduler/scheduler_single_thread_task_runner_manager_unittest.cc", - "task_scheduler/scheduler_worker_pool_impl_unittest.cc", - "task_scheduler/scheduler_worker_pool_unittest.cc", - "task_scheduler/scheduler_worker_stack_unittest.cc", - "task_scheduler/scheduler_worker_unittest.cc", - "task_scheduler/scoped_set_task_priority_for_current_thread_unittest.cc", - "task_scheduler/sequence_sort_key_unittest.cc", - "task_scheduler/sequence_unittest.cc", - "task_scheduler/service_thread_unittest.cc", - "task_scheduler/task_scheduler_impl_unittest.cc", - "task_scheduler/task_tracker_unittest.cc", - "task_scheduler/task_traits_unittest.cc", - "task_scheduler/task_unittest.cc", - "task_scheduler/test_task_factory.cc", - "task_scheduler/test_task_factory.h", - "task_scheduler/test_utils.cc", - "task_scheduler/test_utils.h", - "task_scheduler/tracked_ref_unittest.cc", - "template_util_unittest.cc", - "test/metrics/histogram_enum_reader_unittest.cc", - "test/metrics/histogram_tester_unittest.cc", - "test/metrics/user_action_tester_unittest.cc", - "test/mock_callback_unittest.cc", - "test/scoped_feature_list_unittest.cc", - "test/scoped_mock_time_message_loop_task_runner_unittest.cc", - "test/scoped_task_environment_unittest.cc", - "test/test_mock_time_task_runner_unittest.cc", - "test/test_pending_task_unittest.cc", - "test/test_reg_util_win_unittest.cc", - "test/trace_event_analyzer_unittest.cc", - "thread_annotations_unittest.cc", - "threading/platform_thread_unittest.cc", - "threading/post_task_and_reply_impl_unittest.cc", - "threading/scoped_blocking_call_unittest.cc", - "threading/sequence_local_storage_map_unittest.cc", - "threading/sequence_local_storage_slot_unittest.cc", - "threading/sequenced_task_runner_handle_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_restrictions_unittest.cc", - "threading/thread_task_runner_handle_unittest.cc", - "threading/thread_unittest.cc", - "threading/watchdog_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", - "trace_event/blame_context_unittest.cc", - "trace_event/event_name_filter_unittest.cc", - "trace_event/heap_profiler_allocation_context_tracker_unittest.cc", - "trace_event/java_heap_dump_provider_android_unittest.cc", - "trace_event/memory_allocator_dump_unittest.cc", - "trace_event/memory_dump_manager_unittest.cc", - "trace_event/memory_dump_scheduler_unittest.cc", - "trace_event/memory_infra_background_whitelist_unittest.cc", - "trace_event/memory_usage_estimator_unittest.cc", - "trace_event/process_memory_dump_unittest.cc", - "trace_event/trace_category_unittest.cc", - "trace_event/trace_config_unittest.cc", - "trace_event/trace_event_argument_unittest.cc", - "trace_event/trace_event_filter_test_utils.cc", - "trace_event/trace_event_filter_test_utils.h", - "trace_event/trace_event_system_stats_monitor_unittest.cc", - "trace_event/trace_event_unittest.cc", - "tuple_unittest.cc", - "unguessable_token_unittest.cc", - "value_iterators_unittest.cc", - "values_unittest.cc", - "version_unittest.cc", - "vlog_unittest.cc", - "win/async_operation_unittest.cc", - "win/com_init_check_hook_unittest.cc", - "win/com_init_util_unittest.cc", - "win/core_winrt_util_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/reference_unittest.cc", - "win/registry_unittest.cc", - "win/scoped_bstr_unittest.cc", - "win/scoped_handle_unittest.cc", - "win/scoped_hstring_unittest.cc", - "win/scoped_process_information_unittest.cc", - "win/scoped_variant_unittest.cc", - "win/scoped_winrt_initializer_unittest.cc", - "win/shortcut_unittest.cc", - "win/startup_information_unittest.cc", - "win/typed_event_handler_unittest.cc", - "win/vector_unittest.cc", - "win/wait_chain_unittest.cc", - "win/win_includes_unittest.cc", - "win/win_util_unittest.cc", - "win/windows_version_unittest.cc", - "win/winrt_storage_util_unittest.cc", - "win/wrapped_window_proc_unittest.cc", - ] - - defines = [] - - deps = [ - ":base", - ":i18n", - "//base/allocator:buildflags", - "//base/test:native_library_test_utils", - "//base/test:run_all_base_unittests", - "//base/test:test_support", - "//base/third_party/dynamic_annotations", - "//testing/gmock", - "//testing/gtest", - "//third_party/icu", - ] - - data_deps = [ - "//base/test:test_child_process", - "//base/test:test_shared_library", - ] - - if (is_ios || is_mac) { - deps += [ ":base_unittests_arc" ] - } - - public_deps = [ - ":base_unittests_bundle_data", - ] - - data = [ - "test/data/", - "//tools/metrics/histograms/enums.xml", - ] - - if (is_posix) { - sources += [ - "files/dir_reader_posix_unittest.cc", - "files/file_descriptor_watcher_posix_unittest.cc", - "message_loop/message_loop_io_posix_unittest.cc", - "posix/file_descriptor_shuffle_unittest.cc", - "posix/unix_domain_socket_unittest.cc", - "task_scheduler/task_tracker_posix_unittest.cc", - ] - } - - # Allow more direct string conversions on platforms with native utf8 - # strings - if (is_mac || is_ios || is_chromeos || is_chromecast || is_fuchsia) { - defines += [ "SYSTEM_NATIVE_UTF8" ] - } - - if (is_android) { - # Add unwind tables in base_unittests_apk test apk. The unwind tables are - # generated from debug info in the binary. Removing "default_symbols" and - # adding symbols config removes the "strip_debug" config that strips the - # debug info, on base unittests apk. - if (can_unwind_with_cfi_table) { - configs -= [ "//build/config/compiler:default_symbols" ] - if (symbol_level == 2) { - configs += [ "//build/config/compiler:symbols" ] - } else { - configs += [ "//build/config/compiler:minimal_symbols" ] - } - add_unwind_tables_in_apk = true - sources += [ "trace_event/cfi_backtrace_android_unittest.cc" ] - } - sources -= [ - "process/process_unittest.cc", - "process/process_util_unittest.cc", - ] - deps += [ - ":base_java", - ":base_java_unittest_support", - "//base/test:test_support_java", - ] - } - - if (is_ios) { - sources -= [ - "files/file_path_watcher_unittest.cc", - "memory/discardable_shared_memory_unittest.cc", - "memory/shared_memory_unittest.cc", - "process/memory_unittest.cc", - "process/process_unittest.cc", - "process/process_util_unittest.cc", - "sync_socket_unittest.cc", - "synchronization/waitable_event_watcher_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_release_properties_unittest.mm", - "mac/scoped_nsobject_unittest.mm", - "strings/sys_string_conversions_mac_unittest.mm", - ] - set_sources_assignment_filter(sources_assignment_filter) - - # TODO(GYP): dep on copy_test_data_ios action. - } - - if (use_partition_alloc) { - sources += [ - "allocator/partition_allocator/address_space_randomization_unittest.cc", - "allocator/partition_allocator/page_allocator_unittest.cc", - "allocator/partition_allocator/partition_alloc_unittest.cc", - "allocator/partition_allocator/spin_lock_unittest.cc", - ] - } - - if (is_mac) { - libs = [ - "CoreFoundation.framework", - "Foundation.framework", - ] - if (current_cpu == "x64") { - data_deps += [ ":base_profiler_test_support_library" ] - } - } - - if (is_linux) { - if (is_desktop_linux) { - sources += [ "nix/xdg_util_unittest.cc" ] - } - - deps += [ "//base/test:malloc_wrapper" ] - defines += [ - # This library is used by ElfReaderTest to test reading elf files. - "MALLOC_WRAPPER_LIB=\"${shlib_prefix}malloc_wrapper${shlib_extension}\"", - ] - - if (!is_component_build) { - # Set rpath to find libmalloc_wrapper.so even in a non-component build. - configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ] - } - } - - if (!use_glib) { - sources -= [ "message_loop/message_pump_glib_unittest.cc" ] - } - - if (use_libevent) { - sources += [ "message_loop/message_pump_libevent_unittest.cc" ] - deps += [ "//base/third_party/libevent" ] - } - - if (is_fuchsia) { - sources += [ - "files/dir_reader_posix_unittest.cc", - "files/file_descriptor_watcher_posix_unittest.cc", - "fuchsia/async_dispatcher_unittest.cc", - "fuchsia/filtered_service_directory_unittest.cc", - "fuchsia/service_directory_test_base.cc", - "fuchsia/service_directory_test_base.h", - "fuchsia/service_directory_unittest.cc", - "message_loop/message_loop_io_posix_unittest.cc", - "posix/file_descriptor_shuffle_unittest.cc", - "task_scheduler/task_tracker_posix_unittest.cc", - ] - - # TODO(crbug.com/851641): FilePatchWatcherImpl is not implemented. - sources -= [ "files/file_path_watcher_unittest.cc" ] - - deps += [ - ":test_fidl", - "//third_party/fuchsia-sdk:async", - "//third_party/fuchsia-sdk:async_default", - "//third_party/fuchsia-sdk:fdio", - ] - } - - if (!is_fuchsia && !is_ios) { - sources += [ "files/file_locking_unittest.cc" ] - } - - if (is_android) { - deps += [ "//testing/android/native_test:native_test_native_code" ] - set_sources_assignment_filter([]) - sources += [ - "debug/elf_reader_linux_unittest.cc", - "debug/proc_maps_linux_unittest.cc", - "trace_event/trace_event_android_unittest.cc", - ] - set_sources_assignment_filter(sources_assignment_filter) - } - - if (is_win) { - deps += [ "//base:scoped_handle_test_dll" ] - if (current_cpu == "x64") { - sources += [ "profiler/win32_stack_frame_unwinder_unittest.cc" ] - data_deps += [ ":base_profiler_test_support_library" ] - } - } - - if (use_allocator_shim) { - sources += [ - "allocator/allocator_shim_unittest.cc", - "sampling_heap_profiler/sampling_heap_profiler_unittest.cc", - ] - } - - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] - - # Symbols for crashes when running tests on swarming. - if (symbol_level > 0) { - if (is_win) { - data += [ "$root_out_dir/base_unittests.exe.pdb" ] - } else if (is_mac) { - # TODO(crbug.com/330301): make this conditional on mac_strip_release. - # data += [ "$root_out_dir/base_unittests.dSYM/" ] - } - } -} - -action("build_date") { - script = "//build/write_build_date_header.py" - - # Force recalculation if there's been a change. - inputs = [ - "//build/util/LASTCHANGE", - ] - outputs = [ - "$target_gen_dir/generated_build_date.h", - ] - - args = - [ rebase_path("$target_gen_dir/generated_build_date.h", root_build_dir) ] - - if (is_official_build) { - args += [ "official" ] - } else { - args += [ "default" ] - } - - if (override_build_date != "N/A") { - args += [ override_build_date ] - } -} - -if (enable_nocompile_tests) { - nocompile_test("base_nocompile_tests") { - sources = [ - "bind_unittest.nc", - "callback_list_unittest.nc", - "callback_unittest.nc", - "containers/span_unittest.nc", - "memory/ref_counted_unittest.nc", - "memory/weak_ptr_unittest.nc", - "metrics/field_trial_params_unittest.nc", - "metrics/histogram_unittest.nc", - "optional_unittest.nc", - "strings/string16_unittest.nc", - "task_scheduler/task_traits_unittest.nc", - "thread_annotations_unittest.nc", - ] - - deps = [ - ":base", - "//base/test:run_all_unittests", - "//testing/gtest", - ] - } -} - -if (is_android) { - generate_jni("base_jni_headers") { - sources = [ - "android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java", - "android/java/src/org/chromium/base/ApkAssets.java", - "android/java/src/org/chromium/base/ApplicationStatus.java", - "android/java/src/org/chromium/base/BuildInfo.java", - "android/java/src/org/chromium/base/Callback.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/EarlyTraceEvent.java", - "android/java/src/org/chromium/base/EventLog.java", - "android/java/src/org/chromium/base/FieldTrialList.java", - "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java", - "android/java/src/org/chromium/base/JNIUtils.java", - "android/java/src/org/chromium/base/JavaExceptionReporter.java", - "android/java/src/org/chromium/base/JavaHandlerThread.java", - "android/java/src/org/chromium/base/LocaleUtils.java", - "android/java/src/org/chromium/base/MemoryPressureListener.java", - "android/java/src/org/chromium/base/PathService.java", - "android/java/src/org/chromium/base/PathUtils.java", - "android/java/src/org/chromium/base/PowerMonitor.java", - "android/java/src/org/chromium/base/SysUtils.java", - "android/java/src/org/chromium/base/ThreadUtils.java", - "android/java/src/org/chromium/base/ThrowUncaughtException.java", - "android/java/src/org/chromium/base/TimeUtils.java", - "android/java/src/org/chromium/base/TimezoneUtils.java", - "android/java/src/org/chromium/base/TraceEvent.java", - "android/java/src/org/chromium/base/UnguessableToken.java", - "android/java/src/org/chromium/base/library_loader/LibraryLoader.java", - "android/java/src/org/chromium/base/metrics/RecordHistogram.java", - "android/java/src/org/chromium/base/metrics/RecordUserAction.java", - "android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessService.java", - ] - - public_deps = [ - ":android_runtime_jni_headers", - ] - - jni_package = "base" - } - - generate_jar_jni("android_runtime_jni_headers") { - jni_package = "base" - classes = [ "java/lang/Runtime.class" ] - } - - android_library("base_java") { - srcjar_deps = [ - ":base_android_java_enums_srcjar", - ":base_build_config_gen", - ":base_java_aidl", - ":base_native_libraries_gen", - ] - - deps = [ - "//third_party/android_tools:android_support_annotations_java", - "//third_party/android_tools:android_support_multidex_java", - "//third_party/android_tools:android_support_v4_java", - "//third_party/jsr-305:jsr_305_javalib", - ] - - java_files = [ - "android/java/src/org/chromium/base/ActivityState.java", - "android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java", - "android/java/src/org/chromium/base/ApiCompatibilityUtils.java", - "android/java/src/org/chromium/base/ApkAssets.java", - "android/java/src/org/chromium/base/ApplicationStatus.java", - "android/java/src/org/chromium/base/BaseSwitches.java", - "android/java/src/org/chromium/base/BuildInfo.java", - "android/java/src/org/chromium/base/Callback.java", - "android/java/src/org/chromium/base/CollectionUtil.java", - "android/java/src/org/chromium/base/CommandLine.java", - "android/java/src/org/chromium/base/CommandLineInitUtil.java", - "android/java/src/org/chromium/base/ContentUriUtils.java", - "android/java/src/org/chromium/base/ContextUtils.java", - "android/java/src/org/chromium/base/CpuFeatures.java", - "android/java/src/org/chromium/base/DiscardableReferencePool.java", - "android/java/src/org/chromium/base/EarlyTraceEvent.java", - "android/java/src/org/chromium/base/EventLog.java", - "android/java/src/org/chromium/base/FieldTrialList.java", - "android/java/src/org/chromium/base/FileUtils.java", - "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java", - "android/java/src/org/chromium/base/JNIUtils.java", - "android/java/src/org/chromium/base/JavaExceptionReporter.java", - "android/java/src/org/chromium/base/JavaHandlerThread.java", - "android/java/src/org/chromium/base/LocaleUtils.java", - "android/java/src/org/chromium/base/Log.java", - "android/java/src/org/chromium/base/MemoryPressureListener.java", - "android/java/src/org/chromium/base/NonThreadSafe.java", - "android/java/src/org/chromium/base/ObserverList.java", - "android/java/src/org/chromium/base/PackageUtils.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/Promise.java", - "android/java/src/org/chromium/base/SecureRandomInitializer.java", - "android/java/src/org/chromium/base/StreamUtil.java", - "android/java/src/org/chromium/base/StrictModeContext.java", - "android/java/src/org/chromium/base/Supplier.java", - "android/java/src/org/chromium/base/SysUtils.java", - "android/java/src/org/chromium/base/ThreadUtils.java", - "android/java/src/org/chromium/base/ThrowUncaughtException.java", - "android/java/src/org/chromium/base/TimeUtils.java", - "android/java/src/org/chromium/base/TimezoneUtils.java", - "android/java/src/org/chromium/base/TraceEvent.java", - "android/java/src/org/chromium/base/UnguessableToken.java", - "android/java/src/org/chromium/base/VisibleForTesting.java", - "android/java/src/org/chromium/base/annotations/AccessedByNative.java", - "android/java/src/org/chromium/base/annotations/CalledByNative.java", - "android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java", - "android/java/src/org/chromium/base/annotations/DoNotInline.java", - "android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java", - "android/java/src/org/chromium/base/annotations/JNINamespace.java", - "android/java/src/org/chromium/base/annotations/MainDex.java", - "android/java/src/org/chromium/base/annotations/NativeCall.java", - "android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java", - "android/java/src/org/chromium/base/annotations/RemovableInRelease.java", - "android/java/src/org/chromium/base/annotations/UsedByReflection.java", - "android/java/src/org/chromium/base/library_loader/LibraryLoader.java", - "android/java/src/org/chromium/base/library_loader/Linker.java", - "android/java/src/org/chromium/base/library_loader/LoaderErrors.java", - "android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java", - "android/java/src/org/chromium/base/library_loader/ProcessInitException.java", - "android/java/src/org/chromium/base/metrics/CachedMetrics.java", - "android/java/src/org/chromium/base/metrics/RecordHistogram.java", - "android/java/src/org/chromium/base/metrics/RecordUserAction.java", - "android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java", - "android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java", - "android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessService.java", - "android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java", - "android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java", - "android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java", - "android/java/src/org/chromium/base/memory/MemoryPressureCallback.java", - "android/java/src/org/chromium/base/memory/MemoryPressureUma.java", - "//third_party/android_async_task/java/src/org/chromium/base/AsyncTask.java", - ] - - # New versions of BuildConfig.java and NativeLibraries.java - # (with the actual correct values) will be created when creating an apk. - jar_excluded_patterns = [ - "*/BuildConfig.class", - "*/NativeLibraries.class", - "*/NativeLibraries##*.class", - ] - } - - android_aidl("base_java_aidl") { - import_include = [ "android/java/src" ] - sources = [ - "android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl", - "android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl", - ] - } - - android_library("base_javatests") { - testonly = true - deps = [ - ":base_java", - ":base_java_test_support", - "//third_party/android_support_test_runner:runner_java", - "//third_party/junit:junit", - ] - java_files = [ - # AssertsTest doesn't really belong in //base but it's preferable to - # stick it here than create another target for a single test. - "android/javatests/src/org/chromium/base/AssertsTest.java", - "android/javatests/src/org/chromium/base/AdvancedMockContextTest.java", - "android/javatests/src/org/chromium/base/ApiCompatibilityUtilsTest.java", - "android/javatests/src/org/chromium/base/CommandLineInitUtilTest.java", - "android/javatests/src/org/chromium/base/CommandLineTest.java", - "android/javatests/src/org/chromium/base/EarlyTraceEventTest.java", - - # TODO(nona): move to Junit once that is built for Android N. - "android/javatests/src/org/chromium/base/LocaleUtilsTest.java", - "android/javatests/src/org/chromium/base/ObserverListTest.java", - "android/javatests/src/org/chromium/base/StrictModeContextTest.java", - "android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java", - ] - } - - android_library("base_java_test_support") { - testonly = true - deps = [ - ":base_java", - "//testing/android/reporter:reporter_java", - "//third_party/android_support_test_runner:exposed_instrumentation_api_publish_java", - "//third_party/android_support_test_runner:rules_java", - "//third_party/android_support_test_runner:runner_java", - "//third_party/android_tools:android_support_annotations_java", - "//third_party/android_tools:android_support_chromium_java", - "//third_party/android_tools:android_support_compat_java", - "//third_party/android_tools:android_test_mock_java", - "//third_party/hamcrest:hamcrest_core_java", - "//third_party/junit", - "//third_party/ub-uiautomator:ub_uiautomator_java", - ] - - java_files = [ - "test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java", - "test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java", - "test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java", - "test/android/javatests/src/org/chromium/base/test/BaseTestResult.java", - "test/android/javatests/src/org/chromium/base/test/ScreenshotOnFailureStatement.java", - "test/android/javatests/src/org/chromium/base/test/SetUpTestRule.java", - "test/android/javatests/src/org/chromium/base/test/SetUpStatement.java", - "test/android/javatests/src/org/chromium/base/test/TestListInstrumentationRunListener.java", - "test/android/javatests/src/org/chromium/base/test/TestTraceEvent.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunner.java", - "test/android/javatests/src/org/chromium/base/test/params/BlockJUnit4RunnerDelegate.java", - "test/android/javatests/src/org/chromium/base/test/params/BaseJUnit4RunnerDelegate.java", - "test/android/javatests/src/org/chromium/base/test/params/MethodParamAnnotationRule.java", - "test/android/javatests/src/org/chromium/base/test/params/MethodParamRule.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterAnnotations.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedFrameworkMethod.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunnerDelegate.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunnerDelegateCommon.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterizedRunnerDelegateFactory.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterProvider.java", - "test/android/javatests/src/org/chromium/base/test/params/ParameterSet.java", - "test/android/javatests/src/org/chromium/base/test/util/AdvancedMockContext.java", - "test/android/javatests/src/org/chromium/base/test/util/AnnotationRule.java", - "test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java", - "test/android/javatests/src/org/chromium/base/test/util/CommandLineFlags.java", - "test/android/javatests/src/org/chromium/base/test/util/DisableIf.java", - "test/android/javatests/src/org/chromium/base/test/util/DisableIfSkipCheck.java", - "test/android/javatests/src/org/chromium/base/test/util/AnnotationProcessingUtils.java", - "test/android/javatests/src/org/chromium/base/test/util/DisabledTest.java", - "test/android/javatests/src/org/chromium/base/test/util/EnormousTest.java", - "test/android/javatests/src/org/chromium/base/test/util/Feature.java", - "test/android/javatests/src/org/chromium/base/test/util/FlakyTest.java", - "test/android/javatests/src/org/chromium/base/test/util/InMemorySharedPreferences.java", - "test/android/javatests/src/org/chromium/base/test/util/InstrumentationUtils.java", - "test/android/javatests/src/org/chromium/base/test/util/IntegrationTest.java", - "test/android/javatests/src/org/chromium/base/test/util/Manual.java", - "test/android/javatests/src/org/chromium/base/test/util/Matchers.java", - "test/android/javatests/src/org/chromium/base/test/util/MetricsUtils.java", - "test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevel.java", - "test/android/javatests/src/org/chromium/base/test/util/MinAndroidSdkLevelSkipCheck.java", - "test/android/javatests/src/org/chromium/base/test/util/Restriction.java", - "test/android/javatests/src/org/chromium/base/test/util/RestrictionSkipCheck.java", - "test/android/javatests/src/org/chromium/base/test/util/RetryOnFailure.java", - "test/android/javatests/src/org/chromium/base/test/util/ScalableTimeout.java", - "test/android/javatests/src/org/chromium/base/test/util/SkipCheck.java", - "test/android/javatests/src/org/chromium/base/test/util/TestFileUtil.java", - "test/android/javatests/src/org/chromium/base/test/util/TestThread.java", - "test/android/javatests/src/org/chromium/base/test/util/TimeoutScale.java", - "test/android/javatests/src/org/chromium/base/test/util/UserActionTester.java", - "test/android/javatests/src/org/chromium/base/test/util/UrlUtils.java", - "test/android/javatests/src/org/chromium/base/test/util/parameter/CommandLineParameter.java", - "test/android/javatests/src/org/chromium/base/test/util/parameter/SkipCommandLineParameterization.java", - ] - } - - android_library("base_java_process_launcher_test_support") { - testonly = true - deps = [ - ":base_java", - ":base_java_test_support", - ] - java_files = [ "test/android/javatests/src/org/chromium/base/test/TestChildProcessConnection.java" ] - } - - android_library("base_junit_test_support") { - # Plaform checks are broken for Robolectric. - bypass_platform_checks = true - testonly = true - java_files = [ - "android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java", - "test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java", - "test/android/junit/src/org/chromium/base/test/asynctask/BackgroundShadowAsyncTask.java", - "test/android/junit/src/org/chromium/base/test/asynctask/CustomShadowAsyncTask.java", - "test/android/junit/src/org/chromium/base/test/util/TestRunnerTestRule.java", - "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/test/asynctask/ShadowAsyncTask.java", - "//third_party/robolectric/custom_asynctask/java/src/org/chromium/base/test/asynctask/ShadowAsyncTaskBridge.java", - ] - deps = [ - ":base_java", - "//testing/android/junit:junit_test_support", - "//third_party/android_support_test_runner:runner_java", - "//third_party/robolectric:robolectric_all_java", - ] - } - - junit_binary("base_junit_tests") { - java_files = [ - "android/junit/src/org/chromium/base/ApplicationStatusTest.java", - "android/junit/src/org/chromium/base/DiscardableReferencePoolTest.java", - "android/junit/src/org/chromium/base/LogTest.java", - "android/junit/src/org/chromium/base/NonThreadSafeTest.java", - "android/junit/src/org/chromium/base/PromiseTest.java", - "android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java", - "android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java", - "android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java", - "test/android/junit/src/org/chromium/base/test/SetUpStatementTest.java", - "test/android/junit/src/org/chromium/base/test/TestListInstrumentationRunListenerTest.java", - "test/android/junit/src/org/chromium/base/test/util/AnnotationProcessingUtilsTest.java", - "test/android/junit/src/org/chromium/base/test/util/DisableIfTest.java", - "test/android/junit/src/org/chromium/base/test/util/MinAndroidSdkLevelSkipCheckTest.java", - "test/android/junit/src/org/chromium/base/test/util/RestrictionSkipCheckTest.java", - "test/android/junit/src/org/chromium/base/test/util/SkipCheckTest.java", - "test/android/junit/src/org/chromium/base/test/params/ExampleParameterizedTest.java", - "test/android/junit/src/org/chromium/base/test/params/ParameterizedRunnerTest.java", - "test/android/junit/src/org/chromium/base/test/params/ParameterizedRunnerDelegateCommonTest.java", - "test/android/junit/src/org/chromium/base/test/params/ParameterizedRunnerDelegateFactoryTest.java", - "test/android/junit/src/org/chromium/base/test/params/ParameterizedTestNameTest.java", - ] - deps = [ - ":base_java", - ":base_java_process_launcher_test_support", - ":base_java_test_support", - ":base_junit_test_support", - "//third_party/hamcrest:hamcrest_java", - ] - } - - java_cpp_enum("base_android_java_enums_srcjar") { - sources = [ - "android/application_status_listener.h", - "android/child_process_binding_types.h", - "android/library_loader/library_load_from_apk_status_codes.h", - "android/library_loader/library_loader_hooks.h", - "memory/memory_pressure_listener.h", - "metrics/histogram_base.h", - "trace_event/trace_config.h", - ] - } - - generate_build_config_srcjar("base_build_config_gen") { - use_final_fields = false - } - - java_cpp_template("base_native_libraries_gen") { - sources = [ - "android/java/templates/NativeLibraries.template", - ] - package_path = "org/chromium/base/library_loader" - } - - android_library("base_java_unittest_support") { - testonly = true - deps = [ - ":base_java", - ] - java_files = [ - "test/android/java/src/org/chromium/base/ContentUriTestUtils.java", - "test/android/java/src/org/chromium/base/JavaHandlerThreadHelpers.java", - ] - } -} - -# Keep the list of fuzzer_tests in alphabetical order. -fuzzer_test("base64_decode_fuzzer") { - sources = [ - "base64_decode_fuzzer.cc", - ] - deps = [ - "//base", - ] -} - -fuzzer_test("base64_encode_fuzzer") { - sources = [ - "base64_encode_fuzzer.cc", - ] - deps = [ - "//base", - ] -} - -fuzzer_test("base_json_correctness_fuzzer") { - sources = [ - "json/json_correctness_fuzzer.cc", - ] - deps = [ - ":base", - ] - dict = "//testing/libfuzzer/fuzzers/dicts/json.dict" -} - -fuzzer_test("base_json_reader_fuzzer") { - sources = [ - "json/json_reader_fuzzer.cc", - ] - deps = [ - "//base", - ] - dict = "//testing/libfuzzer/fuzzers/dicts/json.dict" -} - -fuzzer_test("base_json_string_escape_fuzzer") { - sources = [ - "json/string_escape_fuzzer.cc", - ] - deps = [ - "//base", - ] -} - -fuzzer_test("string_number_conversions_fuzzer") { - sources = [ - "strings/string_number_conversions_fuzzer.cc", - ] - deps = [ - "//base", - ] -} - -fuzzer_test("string_tokenizer_fuzzer") { - sources = [ - "strings/string_tokenizer_fuzzer.cc", - ] - deps = [ - "//base", - ] -} - -fuzzer_test("utf_string_conversions_fuzzer") { - sources = [ - "strings/utf_string_conversions_fuzzer.cc", - ] - deps = [ - "//base", - ] -} - -# TODO(dyaroshev): remove regression fuzzer, after we run it for a few days -# and are confident that the transition was ok. -fuzzer_test("utf_string_conversions_regression_fuzzer") { - sources = [ - "strings/old_utf_string_conversions.cc", - "strings/old_utf_string_conversions.h", - "strings/utf_string_conversions_regression_fuzzer.cc", - ] - deps = [ - ":base", - ] - - libfuzzer_options = [ "max_len=32" ] -} diff --git a/DEPS b/DEPS deleted file mode 100644 index 4b25f3f31..000000000 --- a/DEPS +++ /dev/null @@ -1,16 +0,0 @@ -include_rules = [ - "+jni", - "+third_party/ashmem", - "+third_party/apple_apsl", - "+third_party/ced", - "+third_party/lss", - "+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/OWNERS b/OWNERS deleted file mode 100644 index 77641a8cd..000000000 --- a/OWNERS +++ /dev/null @@ -1,54 +0,0 @@ -# About src/base: -# -# Chromium is a very mature project, most things that are generally useful are -# already here, and that things not here aren't generally useful. -# -# Base is pulled into many projects. For example, various ChromeOS daemons. So -# the bar for adding stuff is that it must have demonstrated wide -# applicability. Prefer to add things closer to where they're used (i.e. "not -# base"), and pull into base only when needed. In a project our size, -# sometimes even duplication is OK and inevitable. -# -# Adding a new logging macro DPVELOG_NE is not more clear than just -# writing the stuff you want to log in a regular logging statement, even -# if it makes your calling code longer. Just add it to your own code. -# -# If the code in question does not need to be used inside base, but will have -# multiple consumers across the codebase, consider placing it in a new directory -# under components/ instead. - -ajwong@chromium.org -danakj@chromium.org -dcheng@chromium.org -gab@chromium.org -kylechar@chromium.org -mark@chromium.org -thakis@chromium.org -thestig@chromium.org - -# For Bind/Callback: -per-file bind*=tzik@chromium.org -per-file callback*=tzik@chromium.org - -# For Android-specific changes: -per-file *android*=file://base/android/OWNERS -per-file BUILD.gn=file://base/android/OWNERS - -# For Fuchsia-specific changes: -per-file *_fuchsia*=file://build/fuchsia/OWNERS - -# For FeatureList API: -per-file feature_list*=asvitkine@chromium.org -per-file feature_list*=isherman@chromium.org - -# Restricted since rand_util.h also backs the cryptographically secure RNG. -per-file rand_util*=set noparent -per-file rand_util*=file://ipc/SECURITY_OWNERS - -# For TCMalloc tests: -per-file security_unittest.cc=jln@chromium.org - -# For Value: -per-file values*=jdoerrie@chromium.org - -# COMPONENT: Internals>Core diff --git a/PRESUBMIT.py b/PRESUBMIT.py deleted file mode 100644 index 7fc810765..000000000 --- a/PRESUBMIT.py +++ /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. - -"""Chromium presubmit script for src/base. - -See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts -for more details on the presubmit API built into depot_tools. -""" - -def _CheckNoInterfacesInBase(input_api, output_api): - """Checks to make sure no files in libbase.a have |@interface|.""" - pattern = input_api.re.compile(r'^\s*@interface', input_api.re.MULTILINE) - files = [] - for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile): - if (f.LocalPath().startswith('base/') and - not "/ios/" in f.LocalPath() and - not "/test/" in f.LocalPath() and - not f.LocalPath().endswith('_unittest.mm') and - not f.LocalPath().endswith('mac/sdk_forward_declarations.h')): - contents = input_api.ReadFile(f) - if pattern.search(contents): - files.append(f) - - if len(files): - return [ output_api.PresubmitError( - 'Objective-C interfaces or categories are forbidden in libbase. ' + - 'See http://groups.google.com/a/chromium.org/group/chromium-dev/' + - 'browse_thread/thread/efb28c10435987fd', - files) ] - return [] - - -def _CommonChecks(input_api, output_api): - """Checks common to both upload and commit.""" - results = [] - results.extend(_CheckNoInterfacesInBase(input_api, output_api)) - return results - -def CheckChangeOnUpload(input_api, output_api): - results = [] - results.extend(_CommonChecks(input_api, output_api)) - return results - - -def CheckChangeOnCommit(input_api, output_api): - results = [] - results.extend(_CommonChecks(input_api, output_api)) - return results diff --git a/allocator/BUILD.gn b/allocator/BUILD.gn deleted file mode 100644 index 82489ba08..000000000 --- a/allocator/BUILD.gn +++ /dev/null @@ -1,290 +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. - -import("//build/buildflag_header.gni") -import("//build/config/allocator.gni") -import("//build/config/compiler/compiler.gni") - -declare_args() { - # Provide a way to force disable debugallocation in Debug builds, - # e.g. for profiling (it's more rare to profile Debug builds, - # but people sometimes need to do that). - enable_debugallocation = is_debug - - # Provide a way to build tcmalloc with a low memory footprint. - use_tcmalloc_small_but_slow = false -} - -# This "allocator" meta-target will forward to the default allocator according -# to the build settings. -group("allocator") { - public_deps = [] - deps = [] - - if (use_allocator == "tcmalloc") { - deps += [ ":tcmalloc" ] - } -} - -config("tcmalloc_flags") { - defines = [] - if (enable_debugallocation) { - defines += [ - # Use debugallocation for Debug builds to catch problems early - # and cleanly, http://crbug.com/30715 . - "TCMALLOC_FOR_DEBUGALLOCATION", - ] - } - if (use_allocator_shim) { - defines += [ "TCMALLOC_DONT_REPLACE_SYSTEM_ALLOC" ] - } - if (use_tcmalloc_small_but_slow) { - defines += [ "TCMALLOC_SMALL_BUT_SLOW" ] - } - if (is_clang) { - cflags = [ - # tcmalloc initializes some fields in the wrong order. - "-Wno-reorder", - - # tcmalloc contains some unused local template specializations. - "-Wno-unused-function", - - # tcmalloc uses COMPILE_ASSERT without static_assert but with typedefs. - "-Wno-unused-local-typedefs", - - # for magic2_ in debugallocation.cc (only built in Debug builds) typedefs. - "-Wno-unused-private-field", - ] - } else { - cflags = [] - } - - if (is_linux || is_android) { - # 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", - ] - } -} - -if (use_allocator == "tcmalloc") { - # tcmalloc currently won't compile on Android. - source_set("tcmalloc") { - tcmalloc_dir = "//third_party/tcmalloc/chromium" - - # Don't check tcmalloc's includes. These files include various files like - # base/foo.h and they actually refer to tcmalloc's forked copy of base - # rather than the regular one, which confuses the header checker. - check_includes = false - - sources = [ - # Generated for our configuration from tcmalloc's build - # and checked in. - "$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", - "$tcmalloc_dir/src/base/atomicops-internals-arm-generic.h", - "$tcmalloc_dir/src/base/atomicops-internals-arm-v6plus.h", - "$tcmalloc_dir/src/base/atomicops-internals-linuxppc.h", - "$tcmalloc_dir/src/base/atomicops-internals-macosx.h", - "$tcmalloc_dir/src/base/atomicops-internals-windows.h", - "$tcmalloc_dir/src/base/atomicops-internals-x86.cc", - "$tcmalloc_dir/src/base/atomicops-internals-x86.h", - "$tcmalloc_dir/src/base/atomicops.h", - "$tcmalloc_dir/src/base/commandlineflags.h", - "$tcmalloc_dir/src/base/cycleclock.h", - - # We don't list dynamic_annotations.c since its copy is already - # present in the dynamic_annotations target. - "$tcmalloc_dir/src/base/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/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/free_list.cc", - "$tcmalloc_dir/src/free_list.h", - "$tcmalloc_dir/src/gperftools/heap-profiler.h", - "$tcmalloc_dir/src/gperftools/malloc_extension.h", - "$tcmalloc_dir/src/gperftools/malloc_hook.h", - "$tcmalloc_dir/src/gperftools/stacktrace.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/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/tcmalloc.h", - "$tcmalloc_dir/src/thread_cache.cc", - "$tcmalloc_dir/src/thread_cache.h", - "$tcmalloc_dir/src/windows/port.cc", - "$tcmalloc_dir/src/windows/port.h", - "debugallocation_shim.cc", - - # These are both #included by allocator_shim for maximal linking. - #"generic_allocators.cc", - #"win_allocator.cc", - ] - - # Not included on mips64el. - if (current_cpu == "mips64el") { - sources -= [ - "$tcmalloc_dir/src/base/linuxthreads.cc", - "$tcmalloc_dir/src/base/linuxthreads.h", - ] - } - - # 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", - ":tcmalloc_flags", - ] - - # Thumb mode disabled due to bug in clang integrated assembler - # TODO(https://llvm.org/bugs/show_bug.cgi?id=31058) - configs -= [ "//build/config/compiler:compiler_arm_thumb" ] - configs += [ "//build/config/compiler:compiler_arm" ] - - # TODO(crbug.com/633719) Make tcmalloc work with AFDO on GCC if possible. - if (!is_clang) { - configs -= [ "//build/config/compiler:afdo" ] - } - - deps = [] - - if (enable_profiling) { - sources += [ - "$tcmalloc_dir/src/base/thread_lister.c", - "$tcmalloc_dir/src/base/thread_lister.h", - "$tcmalloc_dir/src/profile-handler.cc", - "$tcmalloc_dir/src/profile-handler.h", - "$tcmalloc_dir/src/profiledata.cc", - "$tcmalloc_dir/src/profiledata.h", - "$tcmalloc_dir/src/profiler.cc", - ] - defines += [ "ENABLE_PROFILING=1" ] - } - - if (is_linux || is_android) { - sources -= [ - "$tcmalloc_dir/src/system-alloc.h", - "$tcmalloc_dir/src/windows/port.cc", - "$tcmalloc_dir/src/windows/port.h", - ] - - # Compiling tcmalloc with -fvisibility=default is only necessary when - # not using the allocator shim, which provides the correct visibility - # annotations for those symbols which need to be exported (see - # //base/allocator/allocator_shim_override_glibc_weak_symbols.h and - # //base/allocator/allocator_shim_internals.h for the definition of - # SHIM_ALWAYS_EXPORT). - if (!use_allocator_shim) { - configs -= [ "//build/config/gcc:symbol_visibility_hidden" ] - configs += [ "//build/config/gcc:symbol_visibility_default" ] - } - - 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:default_optimization" ] - configs += [ "//build/config/compiler:optimize_max" ] - } - - deps += [ "//base/third_party/dynamic_annotations" ] - } -} # use_allocator == "tcmalloc" - -buildflag_header("buildflags") { - header = "buildflags.h" - flags = [ "USE_ALLOCATOR_SHIM=$use_allocator_shim" ] -} - -# Used to shim malloc symbols on Android. see //base/allocator/README.md. -config("wrap_malloc_symbols") { - ldflags = [ - "-Wl,-wrap,calloc", - "-Wl,-wrap,free", - "-Wl,-wrap,malloc", - "-Wl,-wrap,memalign", - "-Wl,-wrap,posix_memalign", - "-Wl,-wrap,pvalloc", - "-Wl,-wrap,realloc", - "-Wl,-wrap,valloc", - ] -} diff --git a/allocator/OWNERS b/allocator/OWNERS deleted file mode 100644 index de658d09c..000000000 --- a/allocator/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -primiano@chromium.org -wfh@chromium.org - -# COMPONENT: Internals diff --git a/allocator/README.md b/allocator/README.md deleted file mode 100644 index 62b9be616..000000000 --- a/allocator/README.md +++ /dev/null @@ -1,197 +0,0 @@ -This document describes how malloc / new calls are routed in the various Chrome -platforms. - -Bare in mind that the chromium codebase does not always just use `malloc()`. -Some examples: - - Large parts of the renderer (Blink) use two home-brewed allocators, - PartitionAlloc and BlinkGC (Oilpan). - - Some subsystems, such as the V8 JavaScript engine, handle memory management - autonomously. - - Various parts of the codebase use abstractions such as `SharedMemory` or - `DiscardableMemory` which, similarly to the above, have their own page-level - memory management. - -Background ----------- -The `allocator` target defines at compile-time the platform-specific choice of -the allocator and extra-hooks which services calls to malloc/new. The relevant -build-time flags involved are `use_allocator` and `use_allocator_shim`. - -The default choices are as follows: - -**Windows** -`use_allocator: winheap`, the default Windows heap. -Additionally, `static_library` (i.e. non-component) builds have a shim -layer wrapping malloc/new, which is controlled by `use_allocator_shim`. -The shim layer provides extra security features, such as preventing large -allocations that can hit signed vs. unsigned bugs in third_party code. - -**Linux Desktop / CrOS** -`use_allocator: tcmalloc`, a forked copy of tcmalloc which resides in -`third_party/tcmalloc/chromium`. Setting `use_allocator: none` causes the build -to fall back to the system (Glibc) symbols. - -**Android** -`use_allocator: none`, always use the allocator symbols coming from Android's -libc (Bionic). As it is developed as part of the OS, it is considered to be -optimized for small devices and more memory-efficient than other choices. -The actual implementation backing malloc symbols in Bionic is up to the board -config and can vary (typically *dlmalloc* or *jemalloc* on most Nexus devices). - -**Mac/iOS** -`use_allocator: none`, we always use the system's allocator implementation. - -In addition, when building for `asan` / `msan` both the allocator and the shim -layer are disabled. - -Layering and build deps ------------------------ -The `allocator` target provides both the source files for tcmalloc (where -applicable) and the linker flags required for the Windows shim layer. -The `base` target is (almost) the only one depending on `allocator`. No other -targets should depend on it, with the exception of the very few executables / -dynamic libraries that don't depend, either directly or indirectly, on `base` -within the scope of a linker unit. - -More importantly, **no other place outside of `/base` should depend on the -specific allocator** (e.g., directly include `third_party/tcmalloc`). -If such a functional dependency is required that should be achieved using -abstractions in `base` (see `/base/allocator/allocator_extension.h` and -`/base/memory/`) - -**Why `base` depends on `allocator`?** -Because it needs to provide services that depend on the actual allocator -implementation. In the past `base` used to pretend to be allocator-agnostic -and get the dependencies injected by other layers. This ended up being an -inconsistent mess. -See the [allocator cleanup doc][url-allocator-cleanup] for more context. - -Linker unit targets (executables and shared libraries) that depend in some way -on `base` (most of the targets in the codebase) get automatically the correct -set of linker flags to pull in tcmalloc or the Windows shim-layer. - - -Source code ------------ -This directory contains just the allocator (i.e. shim) layer that switches -between the different underlying memory allocation implementations. - -The tcmalloc library originates outside of Chromium and exists in -`../../third_party/tcmalloc` (currently, the actual location is defined in the -allocator.gyp file). The third party sources use a vendor-branch SCM pattern to -track Chromium-specific changes independently from upstream changes. - -The general intent is to push local changes upstream so that over -time we no longer need any forked files. - - -Unified allocator shim ----------------------- -On most platforms, Chrome overrides the malloc / operator new symbols (and -corresponding free / delete and other variants). This is to enforce security -checks and lately to enable the -[memory-infra heap profiler][url-memory-infra-heap-profiler]. -Historically each platform had its special logic for defining the allocator -symbols in different places of the codebase. The unified allocator shim is -a project aimed to unify the symbol definition and allocator routing logic in -a central place. - - - Full documentation: [Allocator shim design doc][url-allocator-shim]. - - Current state: Available and enabled by default on Android, CrOS, Linux, - Mac OS and Windows. - - Tracking bug: [https://crbug.com/550886][crbug.com/550886]. - - Build-time flag: `use_allocator_shim`. - -**Overview of the unified allocator shim** -The allocator shim consists of three stages: -``` -+-------------------------+ +-----------------------+ +----------------+ -| malloc & friends | -> | shim layer | -> | Routing to | -| symbols definition | | implementation | | allocator | -+-------------------------+ +-----------------------+ +----------------+ -| - libc symbols (malloc, | | - Security checks | | - tcmalloc | -| calloc, free, ...) | | - Chain of dispatchers| | - glibc | -| - C++ symbols (operator | | that can intercept | | - Android | -| new, delete, ...) | | and override | | bionic | -| - glibc weak symbols | | allocations | | - WinHeap | -| (__libc_malloc, ...) | +-----------------------+ +----------------+ -+-------------------------+ -``` - -**1. malloc symbols definition** -This stage takes care of overriding the symbols `malloc`, `free`, -`operator new`, `operator delete` and friends and routing those calls inside the -allocator shim (next point). -This is taken care of by the headers in `allocator_shim_override_*`. - -*On Linux/CrOS*: the allocator symbols are defined as exported global symbols -in `allocator_shim_override_libc_symbols.h` (for `malloc`, `free` and friends) -and in `allocator_shim_override_cpp_symbols.h` (for `operator new`, -`operator delete` and friends). -This enables proper interposition of malloc symbols referenced by the main -executable and any third party libraries. Symbol resolution on Linux is a breadth first search that starts from the root link unit, that is the executable -(see EXECUTABLE AND LINKABLE FORMAT (ELF) - Portable Formats Specification). -Additionally, when tcmalloc is the default allocator, some extra glibc symbols -are also defined in `allocator_shim_override_glibc_weak_symbols.h`, for subtle -reasons explained in that file. -The Linux/CrOS shim was introduced by -[crrev.com/1675143004](https://crrev.com/1675143004). - -*On Android*: load-time symbol interposition (unlike the Linux/CrOS case) is not -possible. This is because Android processes are `fork()`-ed from the Android -zygote, which pre-loads libc.so and only later native code gets loaded via -`dlopen()` (symbols from `dlopen()`-ed libraries get a different resolution -scope). -In this case, the approach instead of wrapping symbol resolution at link time -(i.e. during the build), via the `--Wl,-wrap,malloc` linker flag. -The use of this wrapping flag causes: - - All references to allocator symbols in the Chrome codebase to be rewritten as - references to `__wrap_malloc` and friends. The `__wrap_malloc` symbols are - defined in the `allocator_shim_override_linker_wrapped_symbols.h` and - route allocator calls inside the shim layer. - - The reference to the original `malloc` symbols (which typically is defined by - the system's libc.so) are accessible via the special `__real_malloc` and - friends symbols (which will be relocated, at load time, against `malloc`). - -In summary, this approach is transparent to the dynamic loader, which still sees -undefined symbol references to malloc symbols. -These symbols will be resolved against libc.so as usual. -More details in [crrev.com/1719433002](https://crrev.com/1719433002). - -**2. Shim layer implementation** -This stage contains the actual shim implementation. This consists of: -- A singly linked list of dispatchers (structs with function pointers to `malloc`-like functions). Dispatchers can be dynamically inserted at runtime -(using the `InsertAllocatorDispatch` API). They can intercept and override -allocator calls. -- The security checks (suicide on malloc-failure via `std::new_handler`, etc). -This happens inside `allocator_shim.cc` - -**3. Final allocator routing** -The final element of the aforementioned dispatcher chain is statically defined -at build time and ultimately routes the allocator calls to the actual allocator -(as described in the *Background* section above). This is taken care of by the -headers in `allocator_shim_default_dispatch_to_*` files. - - -Appendixes ----------- -**How does the Windows shim layer replace the malloc symbols?** -The mechanism for hooking LIBCMT in Windows is rather tricky. The core -problem is that by default, the Windows library does not declare malloc and -free as weak symbols. Because of this, they cannot be overridden. To work -around this, we start with the LIBCMT.LIB, and manually remove all allocator -related functions from it using the visual studio library tool. Once removed, -we can now link against the library and provide custom versions of the -allocator related functionality. -See the script `preb_libc.py` in this folder. - -Related links -------------- -- [Unified allocator shim doc - Feb 2016][url-allocator-shim] -- [Allocator cleanup doc - Jan 2016][url-allocator-cleanup] -- [Proposal to use PartitionAlloc as default allocator](https://crbug.com/339604) -- [Memory-Infra: Tools to profile memory usage in Chrome](/docs/memory-infra/README.md) - -[url-allocator-cleanup]: https://docs.google.com/document/d/1V77Kgp_4tfaaWPEZVxNevoD02wXiatnAv7Ssgr0hmjg/edit?usp=sharing -[url-memory-infra-heap-profiler]: /docs/memory-infra/heap_profiler.md -[url-allocator-shim]: https://docs.google.com/document/d/1yKlO1AO4XjpDad9rjcBOI15EKdAGsuGO_IeZy0g0kxo/edit?usp=sharing diff --git a/allocator/allocator_check.cc b/allocator/allocator_check.cc deleted file mode 100644 index 5fb86467d..000000000 --- a/allocator/allocator_check.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/allocator_check.h" - -#include "base/allocator/buildflags.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/allocator/winheap_stubs_win.h" -#endif - -#if defined(OS_LINUX) -#include -#endif - -#if defined(OS_MACOSX) -#include "base/allocator/allocator_interception_mac.h" -#endif - -namespace base { -namespace allocator { - -bool IsAllocatorInitialized() { -#if defined(OS_WIN) && BUILDFLAG(USE_ALLOCATOR_SHIM) - // Set by allocator_shim_override_ucrt_symbols_win.h when the - // shimmed _set_new_mode() is called. - return g_is_win_shim_layer_initialized; -#elif defined(OS_LINUX) && defined(USE_TCMALLOC) && \ - !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) -// From third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h. -// TODO(primiano): replace with an include once base can depend on allocator. -#define TC_MALLOPT_IS_OVERRIDDEN_BY_TCMALLOC 0xbeef42 - return (mallopt(TC_MALLOPT_IS_OVERRIDDEN_BY_TCMALLOC, 0) == - TC_MALLOPT_IS_OVERRIDDEN_BY_TCMALLOC); -#elif defined(OS_MACOSX) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - // From allocator_interception_mac.mm. - return base::allocator::g_replaced_default_zone; -#else - return true; -#endif -} - -} // namespace allocator -} // namespace base diff --git a/allocator/allocator_check.h b/allocator/allocator_check.h deleted file mode 100644 index cf519fd89..000000000 --- a/allocator/allocator_check.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_ALLOCATOR_ALLOCATOR_CHECK_H_ -#define BASE_ALLOCATOR_ALLOCATOR_ALLOCATOR_CHECK_H_ - -#include "base/base_export.h" - -namespace base { -namespace allocator { - -BASE_EXPORT bool IsAllocatorInitialized(); - -} // namespace allocator -} // namespace base - -#endif // BASE_ALLOCATOR_ALLOCATOR_ALLOCATOR_CHECK_H_ diff --git a/allocator/allocator_extension.cc b/allocator/allocator_extension.cc deleted file mode 100644 index 9a3d114f7..000000000 --- a/allocator/allocator_extension.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/allocator_extension.h" - -#include "base/logging.h" - -#if defined(USE_TCMALLOC) -#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" -#include "third_party/tcmalloc/chromium/src/gperftools/malloc_extension.h" -#include "third_party/tcmalloc/chromium/src/gperftools/malloc_hook.h" -#endif - -namespace base { -namespace allocator { - -void ReleaseFreeMemory() { -#if defined(USE_TCMALLOC) - ::MallocExtension::instance()->ReleaseFreeMemory(); -#endif -} - -bool GetNumericProperty(const char* name, size_t* value) { -#if defined(USE_TCMALLOC) - return ::MallocExtension::instance()->GetNumericProperty(name, value); -#endif - return false; -} - -bool IsHeapProfilerRunning() { -#if defined(USE_TCMALLOC) - return ::IsHeapProfilerRunning(); -#endif - return false; -} - -void SetHooks(AllocHookFunc alloc_hook, FreeHookFunc free_hook) { -// TODO(sque): Use allocator shim layer instead. -#if defined(USE_TCMALLOC) - // Make sure no hooks get overwritten. - auto prev_alloc_hook = MallocHook::SetNewHook(alloc_hook); - if (alloc_hook) - DCHECK(!prev_alloc_hook); - - auto prev_free_hook = MallocHook::SetDeleteHook(free_hook); - if (free_hook) - DCHECK(!prev_free_hook); -#endif -} - -int GetCallStack(void** stack, int max_stack_size) { -#if defined(USE_TCMALLOC) - return MallocHook::GetCallerStackTrace(stack, max_stack_size, 0); -#endif - return 0; -} - -} // namespace allocator -} // namespace base diff --git a/allocator/allocator_extension.h b/allocator/allocator_extension.h deleted file mode 100644 index 9f2775a42..000000000 --- a/allocator/allocator_extension.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H_ -#define BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H_ - -#include // for size_t - -#include "base/base_export.h" -#include "build/build_config.h" - -namespace base { -namespace allocator { - -// Callback types for alloc and free. -using AllocHookFunc = void (*)(const void*, size_t); -using FreeHookFunc = void (*)(const void*); - -// Request that the allocator release any free memory it knows about to the -// system. -BASE_EXPORT void ReleaseFreeMemory(); - -// Get the named property's |value|. Returns true if the property is known. -// Returns false if the property is not a valid property name for the current -// allocator implementation. -// |name| or |value| cannot be NULL -BASE_EXPORT bool GetNumericProperty(const char* name, size_t* value); - -BASE_EXPORT bool IsHeapProfilerRunning(); - -// Register callbacks for alloc and free. Can only store one callback at a time -// for each of alloc and free. -BASE_EXPORT void SetHooks(AllocHookFunc alloc_hook, FreeHookFunc free_hook); - -// Attempts to unwind the call stack from the current location where this -// function is being called from. Must be called from a hook function registered -// by calling SetSingle{Alloc,Free}Hook, directly or indirectly. -// -// Arguments: -// stack: pointer to a pre-allocated array of void*'s. -// max_stack_size: indicates the size of the array in |stack|. -// -// Returns the number of call stack frames stored in |stack|, or 0 if no call -// stack information is available. -BASE_EXPORT int GetCallStack(void** stack, int max_stack_size); - -} // namespace allocator -} // namespace base - -#endif // BASE_ALLOCATOR_ALLOCATOR_EXTENSION_H_ diff --git a/allocator/allocator_interception_mac.h b/allocator/allocator_interception_mac.h deleted file mode 100644 index 68f1d53bb..000000000 --- a/allocator/allocator_interception_mac.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_ALLOCATOR_INTERCEPTION_MAC_H_ -#define BASE_ALLOCATOR_ALLOCATOR_INTERCEPTION_MAC_H_ - -#include - -#include "base/base_export.h" -#include "third_party/apple_apsl/malloc.h" - -namespace base { -namespace allocator { - -struct MallocZoneFunctions; - -// Saves the function pointers currently used by the default zone. -void StoreFunctionsForDefaultZone(); - -// Same as StoreFunctionsForDefaultZone, but for all malloc zones. -void StoreFunctionsForAllZones(); - -// For all malloc zones that have been stored, replace their functions with -// |functions|. -void ReplaceFunctionsForStoredZones(const MallocZoneFunctions* functions); - -extern bool g_replaced_default_zone; - -// Calls the original implementation of malloc/calloc prior to interception. -bool UncheckedMallocMac(size_t size, void** result); -bool UncheckedCallocMac(size_t num_items, size_t size, void** result); - -// Intercepts calls to default and purgeable malloc zones. Intercepts Core -// Foundation and Objective-C allocations. -// Has no effect on the default malloc zone if the allocator shim already -// performs that interception. -BASE_EXPORT void InterceptAllocationsMac(); - -// Updates all malloc zones to use their original functions. -// Also calls ClearAllMallocZonesForTesting. -BASE_EXPORT void UninterceptMallocZonesForTesting(); - -// Periodically checks for, and shims new malloc zones. Stops checking after 1 -// minute. -BASE_EXPORT void PeriodicallyShimNewMallocZones(); - -// Exposed for testing. -BASE_EXPORT void ShimNewMallocZones(); -BASE_EXPORT void ReplaceZoneFunctions(ChromeMallocZone* zone, - const MallocZoneFunctions* functions); - -} // namespace allocator -} // namespace base - -#endif // BASE_ALLOCATOR_ALLOCATOR_INTERCEPTION_MAC_H_ diff --git a/allocator/allocator_interception_mac.mm b/allocator/allocator_interception_mac.mm deleted file mode 100644 index 2e40e87e6..000000000 --- a/allocator/allocator_interception_mac.mm +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright 2017 The Chromium 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 all the logic necessary to intercept allocations on -// macOS. "malloc zones" are an abstraction that allows the process to intercept -// all malloc-related functions. There is no good mechanism [short of -// interposition] to determine new malloc zones are added, so there's no clean -// mechanism to intercept all malloc zones. This file contains logic to -// intercept the default and purgeable zones, which always exist. A cursory -// review of Chrome seems to imply that non-default zones are almost never used. -// -// This file also contains logic to intercept Core Foundation and Objective-C -// allocations. The implementations forward to the default malloc zone, so the -// only reason to intercept these calls is to re-label OOM crashes with slightly -// more details. - -#include "base/allocator/allocator_interception_mac.h" - -#include -#import -#include -#include -#include -#import -#include - -#include - -#include "base/allocator/buildflags.h" -#include "base/allocator/malloc_zone_functions_mac.h" -#include "base/bind.h" -#include "base/logging.h" -#include "base/mac/mac_util.h" -#include "base/mac/mach_logging.h" -#include "base/process/memory.h" -#include "base/scoped_clear_errno.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "build/build_config.h" -#include "third_party/apple_apsl/CFBase.h" - -namespace base { -namespace allocator { - -bool g_replaced_default_zone = false; - -namespace { - -bool g_oom_killer_enabled; - -// Starting with Mac OS X 10.7, the zone allocators set up by the system are -// read-only, to prevent them from being overwritten in an attack. However, -// blindly unprotecting and reprotecting the zone allocators fails with -// GuardMalloc because GuardMalloc sets up its zone allocator using a block of -// memory in its bss. Explicit saving/restoring of the protection is required. -// -// This function takes a pointer to a malloc zone, de-protects it if necessary, -// and returns (in the out parameters) a region of memory (if any) to be -// re-protected when modifications are complete. This approach assumes that -// there is no contention for the protection of this memory. -void DeprotectMallocZone(ChromeMallocZone* default_zone, - mach_vm_address_t* reprotection_start, - mach_vm_size_t* reprotection_length, - vm_prot_t* reprotection_value) { - mach_port_t unused; - *reprotection_start = reinterpret_cast(default_zone); - struct vm_region_basic_info_64 info; - mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; - kern_return_t result = mach_vm_region( - mach_task_self(), reprotection_start, reprotection_length, - VM_REGION_BASIC_INFO_64, reinterpret_cast(&info), - &count, &unused); - MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_region"; - - // 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 - // than the actual smaller size if the passed-in zone is not version 8. - CHECK(*reprotection_start <= - reinterpret_cast(default_zone)); - mach_vm_size_t zone_offset = - reinterpret_cast(default_zone) - - reinterpret_cast(*reprotection_start); - CHECK(zone_offset + sizeof(ChromeMallocZone) <= *reprotection_length); - - if (info.protection & VM_PROT_WRITE) { - // No change needed; the zone is already writable. - *reprotection_start = 0; - *reprotection_length = 0; - *reprotection_value = VM_PROT_NONE; - } else { - *reprotection_value = info.protection; - result = mach_vm_protect(mach_task_self(), *reprotection_start, - *reprotection_length, false, - info.protection | VM_PROT_WRITE); - MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; - } -} - -#if !defined(ADDRESS_SANITIZER) - -MallocZoneFunctions g_old_zone; -MallocZoneFunctions g_old_purgeable_zone; - -void* oom_killer_malloc(struct _malloc_zone_t* zone, size_t size) { - void* result = g_old_zone.malloc(zone, size); - if (!result && size) - TerminateBecauseOutOfMemory(size); - return result; -} - -void* oom_killer_calloc(struct _malloc_zone_t* zone, - size_t num_items, - size_t size) { - void* result = g_old_zone.calloc(zone, num_items, size); - if (!result && num_items && size) - TerminateBecauseOutOfMemory(num_items * size); - return result; -} - -void* oom_killer_valloc(struct _malloc_zone_t* zone, size_t size) { - void* result = g_old_zone.valloc(zone, size); - if (!result && size) - TerminateBecauseOutOfMemory(size); - return result; -} - -void oom_killer_free(struct _malloc_zone_t* zone, void* ptr) { - g_old_zone.free(zone, ptr); -} - -void* oom_killer_realloc(struct _malloc_zone_t* zone, void* ptr, size_t size) { - void* result = g_old_zone.realloc(zone, ptr, size); - if (!result && size) - TerminateBecauseOutOfMemory(size); - return result; -} - -void* oom_killer_memalign(struct _malloc_zone_t* zone, - size_t alignment, - size_t size) { - void* result = g_old_zone.memalign(zone, alignment, size); - // Only die if posix_memalign would have returned ENOMEM, since there are - // other reasons why NULL might be returned (see - // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ). - if (!result && size && alignment >= sizeof(void*) && - (alignment & (alignment - 1)) == 0) { - TerminateBecauseOutOfMemory(size); - } - return result; -} - -void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone, size_t size) { - void* result = g_old_purgeable_zone.malloc(zone, size); - if (!result && size) - TerminateBecauseOutOfMemory(size); - return result; -} - -void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone, - size_t num_items, - size_t size) { - void* result = g_old_purgeable_zone.calloc(zone, num_items, size); - if (!result && num_items && size) - TerminateBecauseOutOfMemory(num_items * size); - return result; -} - -void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone, size_t size) { - void* result = g_old_purgeable_zone.valloc(zone, size); - if (!result && size) - TerminateBecauseOutOfMemory(size); - return result; -} - -void oom_killer_free_purgeable(struct _malloc_zone_t* zone, void* ptr) { - g_old_purgeable_zone.free(zone, ptr); -} - -void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone, - void* ptr, - size_t size) { - void* result = g_old_purgeable_zone.realloc(zone, ptr, size); - if (!result && size) - TerminateBecauseOutOfMemory(size); - return result; -} - -void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone, - size_t alignment, - size_t size) { - void* result = g_old_purgeable_zone.memalign(zone, alignment, size); - // Only die if posix_memalign would have returned ENOMEM, since there are - // other reasons why NULL might be returned (see - // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ). - if (!result && size && alignment >= sizeof(void*) && - (alignment & (alignment - 1)) == 0) { - TerminateBecauseOutOfMemory(size); - } - return result; -} - -#endif // !defined(ADDRESS_SANITIZER) - -#if !defined(ADDRESS_SANITIZER) - -// === Core Foundation CFAllocators === - -bool CanGetContextForCFAllocator() { - return !base::mac::IsOSLaterThan10_14_DontCallThis(); -} - -CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) { - ChromeCFAllocatorLions* our_allocator = const_cast( - reinterpret_cast(allocator)); - return &our_allocator->_context; -} - -CFAllocatorAllocateCallBack g_old_cfallocator_system_default; -CFAllocatorAllocateCallBack g_old_cfallocator_malloc; -CFAllocatorAllocateCallBack g_old_cfallocator_malloc_zone; - -void* oom_killer_cfallocator_system_default(CFIndex alloc_size, - CFOptionFlags hint, - void* info) { - void* result = g_old_cfallocator_system_default(alloc_size, hint, info); - if (!result) - TerminateBecauseOutOfMemory(alloc_size); - return result; -} - -void* oom_killer_cfallocator_malloc(CFIndex alloc_size, - CFOptionFlags hint, - void* info) { - void* result = g_old_cfallocator_malloc(alloc_size, hint, info); - if (!result) - TerminateBecauseOutOfMemory(alloc_size); - return result; -} - -void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size, - CFOptionFlags hint, - void* info) { - void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info); - if (!result) - TerminateBecauseOutOfMemory(alloc_size); - return result; -} - -#endif // !defined(ADDRESS_SANITIZER) - -// === Cocoa NSObject allocation === - -typedef id (*allocWithZone_t)(id, SEL, NSZone*); -allocWithZone_t g_old_allocWithZone; - -id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone) { - id result = g_old_allocWithZone(self, _cmd, zone); - if (!result) - TerminateBecauseOutOfMemory(0); - return result; -} - -void UninterceptMallocZoneForTesting(struct _malloc_zone_t* zone) { - ChromeMallocZone* chrome_zone = reinterpret_cast(zone); - if (!IsMallocZoneAlreadyStored(chrome_zone)) - return; - MallocZoneFunctions& functions = GetFunctionsForZone(zone); - ReplaceZoneFunctions(chrome_zone, &functions); -} - -} // namespace - -bool UncheckedMallocMac(size_t size, void** result) { -#if defined(ADDRESS_SANITIZER) - *result = malloc(size); -#else - if (g_old_zone.malloc) { - *result = g_old_zone.malloc(malloc_default_zone(), size); - } else { - *result = malloc(size); - } -#endif // defined(ADDRESS_SANITIZER) - - return *result != NULL; -} - -bool UncheckedCallocMac(size_t num_items, size_t size, void** result) { -#if defined(ADDRESS_SANITIZER) - *result = calloc(num_items, size); -#else - if (g_old_zone.calloc) { - *result = g_old_zone.calloc(malloc_default_zone(), num_items, size); - } else { - *result = calloc(num_items, size); - } -#endif // defined(ADDRESS_SANITIZER) - - return *result != NULL; -} - -void StoreFunctionsForDefaultZone() { - ChromeMallocZone* default_zone = reinterpret_cast( - malloc_default_zone()); - StoreMallocZone(default_zone); -} - -void StoreFunctionsForAllZones() { - // This ensures that the default zone is always at the front of the array, - // which is important for performance. - StoreFunctionsForDefaultZone(); - - vm_address_t* zones; - unsigned int count; - kern_return_t kr = malloc_get_all_zones(mach_task_self(), 0, &zones, &count); - if (kr != KERN_SUCCESS) - return; - for (unsigned int i = 0; i < count; ++i) { - ChromeMallocZone* zone = reinterpret_cast(zones[i]); - StoreMallocZone(zone); - } -} - -void ReplaceFunctionsForStoredZones(const MallocZoneFunctions* functions) { - // The default zone does not get returned in malloc_get_all_zones(). - ChromeMallocZone* default_zone = - reinterpret_cast(malloc_default_zone()); - if (DoesMallocZoneNeedReplacing(default_zone, functions)) { - ReplaceZoneFunctions(default_zone, functions); - } - - vm_address_t* zones; - unsigned int count; - kern_return_t kr = - malloc_get_all_zones(mach_task_self(), nullptr, &zones, &count); - if (kr != KERN_SUCCESS) - return; - for (unsigned int i = 0; i < count; ++i) { - ChromeMallocZone* zone = reinterpret_cast(zones[i]); - if (DoesMallocZoneNeedReplacing(zone, functions)) { - ReplaceZoneFunctions(zone, functions); - } - } - g_replaced_default_zone = true; -} - -void InterceptAllocationsMac() { - if (g_oom_killer_enabled) - return; - - g_oom_killer_enabled = true; - -// === C malloc/calloc/valloc/realloc/posix_memalign === - -// This approach is not perfect, as requests for amounts of memory larger than -// MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will -// still fail with a NULL rather than dying (see -// http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for details). -// Unfortunately, it's the best we can do. Also note that this does not affect -// allocations from non-default zones. - -#if !defined(ADDRESS_SANITIZER) - // Don't do anything special on OOM for the malloc zones replaced by - // AddressSanitizer, as modifying or protecting them may not work correctly. - ChromeMallocZone* default_zone = - reinterpret_cast(malloc_default_zone()); - if (!IsMallocZoneAlreadyStored(default_zone)) { - StoreZoneFunctions(default_zone, &g_old_zone); - MallocZoneFunctions new_functions = {}; - new_functions.malloc = oom_killer_malloc; - new_functions.calloc = oom_killer_calloc; - new_functions.valloc = oom_killer_valloc; - new_functions.free = oom_killer_free; - new_functions.realloc = oom_killer_realloc; - new_functions.memalign = oom_killer_memalign; - - ReplaceZoneFunctions(default_zone, &new_functions); - g_replaced_default_zone = true; - } - - ChromeMallocZone* purgeable_zone = - reinterpret_cast(malloc_default_purgeable_zone()); - if (purgeable_zone && !IsMallocZoneAlreadyStored(purgeable_zone)) { - StoreZoneFunctions(purgeable_zone, &g_old_purgeable_zone); - MallocZoneFunctions new_functions = {}; - new_functions.malloc = oom_killer_malloc_purgeable; - new_functions.calloc = oom_killer_calloc_purgeable; - new_functions.valloc = oom_killer_valloc_purgeable; - new_functions.free = oom_killer_free_purgeable; - new_functions.realloc = oom_killer_realloc_purgeable; - new_functions.memalign = oom_killer_memalign_purgeable; - ReplaceZoneFunctions(purgeable_zone, &new_functions); - } -#endif - - // === C malloc_zone_batch_malloc === - - // batch_malloc is omitted because the default malloc zone's implementation - // only supports batch_malloc for "tiny" allocations from the free list. It - // will fail for allocations larger than "tiny", and will only allocate as - // many blocks as it's able to from the free list. These factors mean that it - // can return less than the requested memory even in a non-out-of-memory - // situation. There's no good way to detect whether a batch_malloc failure is - // due to these other factors, or due to genuine memory or address space - // exhaustion. The fact that it only allocates space from the "tiny" free list - // means that it's likely that a failure will not be due to memory exhaustion. - // Similarly, these constraints on batch_malloc mean that callers must always - // be expecting to receive less memory than was requested, even in situations - // where memory pressure is not a concern. Finally, the only public interface - // to batch_malloc is malloc_zone_batch_malloc, which is specific to the - // system's malloc implementation. It's unlikely that anyone's even heard of - // it. - -#ifndef ADDRESS_SANITIZER - // === Core Foundation CFAllocators === - - // This will not catch allocation done by custom allocators, but will catch - // all allocation done by system-provided ones. - - CHECK(!g_old_cfallocator_system_default && !g_old_cfallocator_malloc && - !g_old_cfallocator_malloc_zone) - << "Old allocators unexpectedly non-null"; - - bool cf_allocator_internals_known = CanGetContextForCFAllocator(); - - if (cf_allocator_internals_known) { - CFAllocatorContext* context = - ContextForCFAllocator(kCFAllocatorSystemDefault); - CHECK(context) << "Failed to get context for kCFAllocatorSystemDefault."; - g_old_cfallocator_system_default = context->allocate; - CHECK(g_old_cfallocator_system_default) - << "Failed to get kCFAllocatorSystemDefault allocation function."; - context->allocate = oom_killer_cfallocator_system_default; - - context = ContextForCFAllocator(kCFAllocatorMalloc); - CHECK(context) << "Failed to get context for kCFAllocatorMalloc."; - g_old_cfallocator_malloc = context->allocate; - CHECK(g_old_cfallocator_malloc) - << "Failed to get kCFAllocatorMalloc allocation function."; - context->allocate = oom_killer_cfallocator_malloc; - - context = ContextForCFAllocator(kCFAllocatorMallocZone); - CHECK(context) << "Failed to get context for kCFAllocatorMallocZone."; - g_old_cfallocator_malloc_zone = context->allocate; - CHECK(g_old_cfallocator_malloc_zone) - << "Failed to get kCFAllocatorMallocZone allocation function."; - context->allocate = oom_killer_cfallocator_malloc_zone; - } else { - DLOG(WARNING) << "Internals of CFAllocator not known; out-of-memory " - "failures via CFAllocator will not result in termination. " - "http://crbug.com/45650"; - } -#endif - - // === Cocoa NSObject allocation === - - // Note that both +[NSObject new] and +[NSObject alloc] call through to - // +[NSObject allocWithZone:]. - - CHECK(!g_old_allocWithZone) << "Old allocator unexpectedly non-null"; - - Class nsobject_class = [NSObject class]; - Method orig_method = - class_getClassMethod(nsobject_class, @selector(allocWithZone:)); - g_old_allocWithZone = - reinterpret_cast(method_getImplementation(orig_method)); - CHECK(g_old_allocWithZone) - << "Failed to get allocWithZone allocation function."; - method_setImplementation(orig_method, - reinterpret_cast(oom_killer_allocWithZone)); -} - -void UninterceptMallocZonesForTesting() { - UninterceptMallocZoneForTesting(malloc_default_zone()); - vm_address_t* zones; - unsigned int count; - kern_return_t kr = malloc_get_all_zones(mach_task_self(), 0, &zones, &count); - CHECK(kr == KERN_SUCCESS); - for (unsigned int i = 0; i < count; ++i) { - UninterceptMallocZoneForTesting( - reinterpret_cast(zones[i])); - } - - ClearAllMallocZonesForTesting(); -} - -namespace { - -void ShimNewMallocZonesAndReschedule(base::Time end_time, - base::TimeDelta delay) { - ShimNewMallocZones(); - - if (base::Time::Now() > end_time) - return; - - base::TimeDelta next_delay = delay * 2; - SequencedTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, - base::Bind(&ShimNewMallocZonesAndReschedule, end_time, next_delay), - delay); -} - -} // namespace - -void PeriodicallyShimNewMallocZones() { - base::Time end_time = base::Time::Now() + base::TimeDelta::FromMinutes(1); - base::TimeDelta initial_delay = base::TimeDelta::FromSeconds(1); - ShimNewMallocZonesAndReschedule(end_time, initial_delay); -} - -void ShimNewMallocZones() { - StoreFunctionsForAllZones(); - - // Use the functions for the default zone as a template to replace those - // new zones. - ChromeMallocZone* default_zone = - reinterpret_cast(malloc_default_zone()); - DCHECK(IsMallocZoneAlreadyStored(default_zone)); - - MallocZoneFunctions new_functions; - StoreZoneFunctions(default_zone, &new_functions); - ReplaceFunctionsForStoredZones(&new_functions); -} - -void ReplaceZoneFunctions(ChromeMallocZone* zone, - const MallocZoneFunctions* functions) { - // Remove protection. - mach_vm_address_t reprotection_start = 0; - mach_vm_size_t reprotection_length = 0; - vm_prot_t reprotection_value = VM_PROT_NONE; - DeprotectMallocZone(zone, &reprotection_start, &reprotection_length, - &reprotection_value); - - CHECK(functions->malloc && functions->calloc && functions->valloc && - functions->free && functions->realloc); - zone->malloc = functions->malloc; - zone->calloc = functions->calloc; - zone->valloc = functions->valloc; - zone->free = functions->free; - zone->realloc = functions->realloc; - if (functions->batch_malloc) - zone->batch_malloc = functions->batch_malloc; - if (functions->batch_free) - zone->batch_free = functions->batch_free; - if (functions->size) - zone->size = functions->size; - if (zone->version >= 5 && functions->memalign) { - zone->memalign = functions->memalign; - } - if (zone->version >= 6 && functions->free_definite_size) { - zone->free_definite_size = functions->free_definite_size; - } - - // Restore protection if it was active. - if (reprotection_start) { - kern_return_t result = - mach_vm_protect(mach_task_self(), reprotection_start, - reprotection_length, false, reprotection_value); - MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; - } -} - -} // namespace allocator -} // namespace base diff --git a/allocator/allocator_interception_mac_unittest.mm b/allocator/allocator_interception_mac_unittest.mm deleted file mode 100644 index c919ca0e8..000000000 --- a/allocator/allocator_interception_mac_unittest.mm +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2017 The Chromium 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 - -#include "base/allocator/allocator_interception_mac.h" -#include "base/allocator/allocator_shim.h" -#include "base/allocator/malloc_zone_functions_mac.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace allocator { - -namespace { -void ResetMallocZone(ChromeMallocZone* zone) { - MallocZoneFunctions& functions = GetFunctionsForZone(zone); - ReplaceZoneFunctions(zone, &functions); -} - -void ResetAllMallocZones() { - ChromeMallocZone* default_malloc_zone = - reinterpret_cast(malloc_default_zone()); - ResetMallocZone(default_malloc_zone); - - vm_address_t* zones; - unsigned int count; - kern_return_t kr = malloc_get_all_zones(mach_task_self(), 0, &zones, &count); - if (kr != KERN_SUCCESS) - return; - for (unsigned int i = 0; i < count; ++i) { - ChromeMallocZone* zone = reinterpret_cast(zones[i]); - ResetMallocZone(zone); - } -} -} // namespace - -class AllocatorInterceptionTest : public testing::Test { - protected: - void TearDown() override { - ResetAllMallocZones(); - ClearAllMallocZonesForTesting(); - } -}; - -#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) -TEST_F(AllocatorInterceptionTest, ShimNewMallocZones) { - InitializeAllocatorShim(); - ChromeMallocZone* default_malloc_zone = - reinterpret_cast(malloc_default_zone()); - - malloc_zone_t new_zone; - memset(&new_zone, 1, sizeof(malloc_zone_t)); - malloc_zone_register(&new_zone); - EXPECT_NE(new_zone.malloc, default_malloc_zone->malloc); - ShimNewMallocZones(); - EXPECT_EQ(new_zone.malloc, default_malloc_zone->malloc); - - malloc_zone_unregister(&new_zone); -} -#endif - -} // namespace allocator -} // namespace base diff --git a/allocator/allocator_shim.cc b/allocator/allocator_shim.cc deleted file mode 100644 index e919f094c..000000000 --- a/allocator/allocator_shim.cc +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/allocator_shim.h" - -#include - -#include - -#include "base/atomicops.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/process/process_metrics.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" - -#if !defined(OS_WIN) -#include -#else -#include "base/allocator/winheap_stubs_win.h" -#endif - -#if defined(OS_MACOSX) -#include - -#include "base/allocator/allocator_interception_mac.h" -#endif - -// No calls to malloc / new in this file. They would would cause re-entrancy of -// the shim, which is hard to deal with. Keep this code as simple as possible -// and don't use any external C++ object here, not even //base ones. Even if -// they are safe to use today, in future they might be refactored. - -namespace { - -using namespace base; - -subtle::AtomicWord g_chain_head = reinterpret_cast( - &allocator::AllocatorDispatch::default_dispatch); - -bool g_call_new_handler_on_malloc_failure = false; - -inline size_t GetCachedPageSize() { - static size_t pagesize = 0; - if (!pagesize) - pagesize = base::GetPageSize(); - return pagesize; -} - -// Calls the std::new handler thread-safely. Returns true if a new_handler was -// set and called, false if no new_handler was set. -bool CallNewHandler(size_t size) { -#if defined(OS_WIN) - return base::allocator::WinCallNewHandler(size); -#else - std::new_handler nh = std::get_new_handler(); - if (!nh) - return false; - (*nh)(); - // Assume the new_handler will abort if it fails. Exception are disabled and - // we don't support the case of a new_handler throwing std::bad_balloc. - return true; -#endif -} - -inline const allocator::AllocatorDispatch* GetChainHead() { - // TODO(primiano): Just use NoBarrier_Load once crbug.com/593344 is fixed. - // Unfortunately due to that bug NoBarrier_Load() is mistakenly fully - // barriered on Linux+Clang, and that causes visible perf regressons. - return reinterpret_cast( -#if defined(OS_LINUX) && defined(__clang__) - *static_cast(&g_chain_head) -#else - subtle::NoBarrier_Load(&g_chain_head) -#endif - ); -} - -} // namespace - -namespace base { -namespace allocator { - -void SetCallNewHandlerOnMallocFailure(bool value) { - g_call_new_handler_on_malloc_failure = value; -} - -void* UncheckedAlloc(size_t size) { - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->alloc_function(chain_head, size, nullptr); -} - -void InsertAllocatorDispatch(AllocatorDispatch* dispatch) { - // Loop in case of (an unlikely) race on setting the list head. - size_t kMaxRetries = 7; - for (size_t i = 0; i < kMaxRetries; ++i) { - const AllocatorDispatch* chain_head = GetChainHead(); - dispatch->next = chain_head; - - // This function guarantees to be thread-safe w.r.t. concurrent - // insertions. It also has to guarantee that all the threads always - // see a consistent chain, hence the MemoryBarrier() below. - // InsertAllocatorDispatch() is NOT a fastpath, as opposite to malloc(), so - // we don't really want this to be a release-store with a corresponding - // acquire-load during malloc(). - subtle::MemoryBarrier(); - subtle::AtomicWord old_value = - reinterpret_cast(chain_head); - // Set the chain head to the new dispatch atomically. If we lose the race, - // the comparison will fail, and the new head of chain will be returned. - if (subtle::NoBarrier_CompareAndSwap( - &g_chain_head, old_value, - reinterpret_cast(dispatch)) == old_value) { - // Success. - return; - } - } - - CHECK(false); // Too many retries, this shouldn't happen. -} - -void RemoveAllocatorDispatchForTesting(AllocatorDispatch* dispatch) { - DCHECK_EQ(GetChainHead(), dispatch); - subtle::NoBarrier_Store(&g_chain_head, - reinterpret_cast(dispatch->next)); -} - -} // namespace allocator -} // namespace base - -// The Shim* functions below are the entry-points into the shim-layer and -// are supposed to be invoked by the allocator_shim_override_* -// headers to route the malloc / new symbols through the shim layer. -// They are defined as ALWAYS_INLINE in order to remove a level of indirection -// between the system-defined entry points and the shim implementations. -extern "C" { - -// The general pattern for allocations is: -// - Try to allocate, if succeded return the pointer. -// - If the allocation failed: -// - Call the std::new_handler if it was a C++ allocation. -// - Call the std::new_handler if it was a malloc() (or calloc() or similar) -// AND SetCallNewHandlerOnMallocFailure(true). -// - If the std::new_handler is NOT set just return nullptr. -// - If the std::new_handler is set: -// - Assume it will abort() if it fails (very likely the new_handler will -// just suicide priting a message). -// - Assume it did succeed if it returns, in which case reattempt the alloc. - -ALWAYS_INLINE void* ShimCppNew(size_t size) { - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - void* context = nullptr; -#if defined(OS_MACOSX) - context = malloc_default_zone(); -#endif - ptr = chain_head->alloc_function(chain_head, size, context); - } while (!ptr && CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void ShimCppDelete(void* address) { - void* context = nullptr; -#if defined(OS_MACOSX) - context = malloc_default_zone(); -#endif - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->free_function(chain_head, address, context); -} - -ALWAYS_INLINE void* ShimMalloc(size_t size, void* context) { - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - ptr = chain_head->alloc_function(chain_head, size, context); - } while (!ptr && g_call_new_handler_on_malloc_failure && - CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void* ShimCalloc(size_t n, size_t size, void* context) { - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - ptr = chain_head->alloc_zero_initialized_function(chain_head, n, size, - context); - } while (!ptr && g_call_new_handler_on_malloc_failure && - CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void* ShimRealloc(void* address, size_t size, void* context) { - // realloc(size == 0) means free() and might return a nullptr. We should - // not call the std::new_handler in that case, though. - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - ptr = chain_head->realloc_function(chain_head, address, size, context); - } while (!ptr && size && g_call_new_handler_on_malloc_failure && - CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE void* ShimMemalign(size_t alignment, size_t size, void* context) { - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - void* ptr; - do { - ptr = chain_head->alloc_aligned_function(chain_head, alignment, size, - context); - } while (!ptr && g_call_new_handler_on_malloc_failure && - CallNewHandler(size)); - return ptr; -} - -ALWAYS_INLINE int ShimPosixMemalign(void** res, size_t alignment, size_t size) { - // posix_memalign is supposed to check the arguments. See tc_posix_memalign() - // in tc_malloc.cc. - if (((alignment % sizeof(void*)) != 0) || - ((alignment & (alignment - 1)) != 0) || (alignment == 0)) { - return EINVAL; - } - void* ptr = ShimMemalign(alignment, size, nullptr); - *res = ptr; - return ptr ? 0 : ENOMEM; -} - -ALWAYS_INLINE void* ShimValloc(size_t size, void* context) { - return ShimMemalign(GetCachedPageSize(), size, context); -} - -ALWAYS_INLINE void* ShimPvalloc(size_t size) { - // pvalloc(0) should allocate one page, according to its man page. - if (size == 0) { - size = GetCachedPageSize(); - } else { - size = (size + GetCachedPageSize() - 1) & ~(GetCachedPageSize() - 1); - } - // The third argument is nullptr because pvalloc is glibc only and does not - // exist on OSX/BSD systems. - return ShimMemalign(GetCachedPageSize(), size, nullptr); -} - -ALWAYS_INLINE void ShimFree(void* address, void* context) { - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->free_function(chain_head, address, context); -} - -ALWAYS_INLINE size_t ShimGetSizeEstimate(const void* address, void* context) { - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->get_size_estimate_function( - chain_head, const_cast(address), context); -} - -ALWAYS_INLINE unsigned ShimBatchMalloc(size_t size, - void** results, - unsigned num_requested, - void* context) { - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->batch_malloc_function(chain_head, size, results, - num_requested, context); -} - -ALWAYS_INLINE void ShimBatchFree(void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->batch_free_function(chain_head, to_be_freed, - num_to_be_freed, context); -} - -ALWAYS_INLINE void ShimFreeDefiniteSize(void* ptr, size_t size, void* context) { - const allocator::AllocatorDispatch* const chain_head = GetChainHead(); - return chain_head->free_definite_size_function(chain_head, ptr, size, - context); -} - -} // extern "C" - -#if !defined(OS_WIN) && !defined(OS_MACOSX) -// Cpp symbols (new / delete) should always be routed through the shim layer -// except on Windows and macOS where the malloc intercept is deep enough that it -// also catches the cpp calls. -#include "base/allocator/allocator_shim_override_cpp_symbols.h" -#endif - -#if defined(OS_ANDROID) -// Android does not support symbol interposition. The way malloc symbols are -// intercepted on Android is by using link-time -wrap flags. -#include "base/allocator/allocator_shim_override_linker_wrapped_symbols.h" -#elif defined(OS_WIN) -// On Windows we use plain link-time overriding of the CRT symbols. -#include "base/allocator/allocator_shim_override_ucrt_symbols_win.h" -#elif defined(OS_MACOSX) -#include "base/allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.h" -#include "base/allocator/allocator_shim_override_mac_symbols.h" -#else -#include "base/allocator/allocator_shim_override_libc_symbols.h" -#endif - -// In the case of tcmalloc we also want to plumb into the glibc hooks -// to avoid that allocations made in glibc itself (e.g., strdup()) get -// accidentally performed on the glibc heap instead of the tcmalloc one. -#if defined(USE_TCMALLOC) -#include "base/allocator/allocator_shim_override_glibc_weak_symbols.h" -#endif - -#if defined(OS_MACOSX) -namespace base { -namespace allocator { -void InitializeAllocatorShim() { - // Prepares the default dispatch. After the intercepted malloc calls have - // traversed the shim this will route them to the default malloc zone. - InitializeDefaultDispatchToMacAllocator(); - - MallocZoneFunctions functions = MallocZoneFunctionsToReplaceDefault(); - - // This replaces the default malloc zone, causing calls to malloc & friends - // from the codebase to be routed to ShimMalloc() above. - base::allocator::ReplaceFunctionsForStoredZones(&functions); -} -} // namespace allocator -} // namespace base -#endif - -// Cross-checks. - -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) -#error The allocator shim should not be compiled when building for memory tools. -#endif - -#if (defined(__GNUC__) && defined(__EXCEPTIONS)) || \ - (defined(_MSC_VER) && defined(_CPPUNWIND)) -#error This code cannot be used when exceptions are turned on. -#endif diff --git a/allocator/allocator_shim.h b/allocator/allocator_shim.h deleted file mode 100644 index 527e414de..000000000 --- a/allocator/allocator_shim.h +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_ALLOCATOR_SHIM_H_ -#define BASE_ALLOCATOR_ALLOCATOR_SHIM_H_ - -#include - -#include "base/base_export.h" -#include "build/build_config.h" - -namespace base { -namespace allocator { - -// Allocator Shim API. Allows to: -// - Configure the behavior of the allocator (what to do on OOM failures). -// - Install new hooks (AllocatorDispatch) in the allocator chain. - -// When this shim layer is enabled, the route of an allocation is as-follows: -// -// [allocator_shim_override_*.h] Intercept malloc() / operator new calls: -// The override_* headers define the symbols required to intercept calls to -// malloc() and operator new (if not overridden by specific C++ classes). -// -// [allocator_shim.cc] Routing allocation calls to the shim: -// The headers above route the calls to the internal ShimMalloc(), ShimFree(), -// ShimCppNew() etc. methods defined in allocator_shim.cc. -// These methods will: (1) forward the allocation call to the front of the -// AllocatorDispatch chain. (2) perform security hardenings (e.g., might -// call std::new_handler on OOM failure). -// -// [allocator_shim_default_dispatch_to_*.cc] The AllocatorDispatch chain: -// It is a singly linked list where each element is a struct with function -// pointers (|malloc_function|, |free_function|, etc). Normally the chain -// consists of a single AllocatorDispatch element, herein called -// the "default dispatch", which is statically defined at build time and -// ultimately routes the calls to the actual allocator defined by the build -// config (tcmalloc, glibc, ...). -// -// It is possible to dynamically insert further AllocatorDispatch stages -// to the front of the chain, for debugging / profiling purposes. -// -// All the functions must be thred safe. The shim does not enforce any -// serialization. This is to route to thread-aware allocators (e.g, tcmalloc) -// wihout introducing unnecessary perf hits. - -struct AllocatorDispatch { - using AllocFn = void*(const AllocatorDispatch* self, - size_t size, - void* context); - using AllocZeroInitializedFn = void*(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context); - using AllocAlignedFn = void*(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context); - using ReallocFn = void*(const AllocatorDispatch* self, - void* address, - size_t size, - void* context); - using FreeFn = void(const AllocatorDispatch* self, - void* address, - void* context); - // Returns the best available estimate for the actual amount of memory - // consumed by the allocation |address|. If possible, this should include - // heap overhead or at least a decent estimate of the full cost of the - // allocation. If no good estimate is possible, returns zero. - using GetSizeEstimateFn = size_t(const AllocatorDispatch* self, - void* address, - void* context); - using BatchMallocFn = unsigned(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context); - using BatchFreeFn = void(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context); - using FreeDefiniteSizeFn = void(const AllocatorDispatch* self, - void* ptr, - size_t size, - void* context); - - AllocFn* const alloc_function; - AllocZeroInitializedFn* const alloc_zero_initialized_function; - AllocAlignedFn* const alloc_aligned_function; - ReallocFn* const realloc_function; - FreeFn* const free_function; - GetSizeEstimateFn* const get_size_estimate_function; - BatchMallocFn* const batch_malloc_function; - BatchFreeFn* const batch_free_function; - FreeDefiniteSizeFn* const free_definite_size_function; - - const AllocatorDispatch* next; - - // |default_dispatch| is statically defined by one (and only one) of the - // allocator_shim_default_dispatch_to_*.cc files, depending on the build - // configuration. - static const AllocatorDispatch default_dispatch; -}; - -// When true makes malloc behave like new, w.r.t calling the new_handler if -// the allocation fails (see set_new_mode() in Windows). -BASE_EXPORT void SetCallNewHandlerOnMallocFailure(bool value); - -// Allocates |size| bytes or returns nullptr. It does NOT call the new_handler, -// regardless of SetCallNewHandlerOnMallocFailure(). -BASE_EXPORT void* UncheckedAlloc(size_t size); - -// Inserts |dispatch| in front of the allocator chain. This method is -// thread-safe w.r.t concurrent invocations of InsertAllocatorDispatch(). -// The callers have responsibility for inserting a single dispatch no more -// than once. -BASE_EXPORT void InsertAllocatorDispatch(AllocatorDispatch* dispatch); - -// Test-only. Rationale: (1) lack of use cases; (2) dealing safely with a -// removal of arbitrary elements from a singly linked list would require a lock -// in malloc(), which we really don't want. -BASE_EXPORT void RemoveAllocatorDispatchForTesting(AllocatorDispatch* dispatch); - -#if defined(OS_MACOSX) -// On macOS, the allocator shim needs to be turned on during runtime. -BASE_EXPORT void InitializeAllocatorShim(); -#endif // defined(OS_MACOSX) - -} // namespace allocator -} // namespace base - -#endif // BASE_ALLOCATOR_ALLOCATOR_SHIM_H_ diff --git a/allocator/allocator_shim_default_dispatch_to_glibc.cc b/allocator/allocator_shim_default_dispatch_to_glibc.cc deleted file mode 100644 index 8574da3eb..000000000 --- a/allocator/allocator_shim_default_dispatch_to_glibc.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/allocator_shim.h" - -#include - -// This translation unit defines a default dispatch for the allocator shim which -// routes allocations to libc functions. -// The code here is strongly inspired from tcmalloc's libc_override_glibc.h. - -extern "C" { -void* __libc_malloc(size_t size); -void* __libc_calloc(size_t n, size_t size); -void* __libc_realloc(void* address, size_t size); -void* __libc_memalign(size_t alignment, size_t size); -void __libc_free(void* ptr); -} // extern "C" - -namespace { - -using base::allocator::AllocatorDispatch; - -void* GlibcMalloc(const AllocatorDispatch*, size_t size, void* context) { - return __libc_malloc(size); -} - -void* GlibcCalloc(const AllocatorDispatch*, - size_t n, - size_t size, - void* context) { - return __libc_calloc(n, size); -} - -void* GlibcRealloc(const AllocatorDispatch*, - void* address, - size_t size, - void* context) { - return __libc_realloc(address, size); -} - -void* GlibcMemalign(const AllocatorDispatch*, - size_t alignment, - size_t size, - void* context) { - return __libc_memalign(alignment, size); -} - -void GlibcFree(const AllocatorDispatch*, void* address, void* context) { - __libc_free(address); -} - -size_t GlibcGetSizeEstimate(const AllocatorDispatch*, - void* address, - void* context) { - // TODO(siggi, primiano): malloc_usable_size may need redirection in the - // presence of interposing shims that divert allocations. - return malloc_usable_size(address); -} - -} // namespace - -const AllocatorDispatch AllocatorDispatch::default_dispatch = { - &GlibcMalloc, /* alloc_function */ - &GlibcCalloc, /* alloc_zero_initialized_function */ - &GlibcMemalign, /* alloc_aligned_function */ - &GlibcRealloc, /* realloc_function */ - &GlibcFree, /* free_function */ - &GlibcGetSizeEstimate, /* get_size_estimate_function */ - nullptr, /* batch_malloc_function */ - nullptr, /* batch_free_function */ - nullptr, /* free_definite_size_function */ - nullptr, /* next */ -}; diff --git a/allocator/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc b/allocator/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc deleted file mode 100644 index c351a7c92..000000000 --- a/allocator/allocator_shim_default_dispatch_to_linker_wrapped_symbols.cc +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2016 The Chromium 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 - -#include "base/allocator/allocator_shim.h" -#include "build/build_config.h" - -#if defined(OS_ANDROID) && __ANDROID_API__ < 17 -#include -// This is defined in malloc.h on other platforms. We just need the definition -// for the decltype(malloc_usable_size)* call to work. -size_t malloc_usable_size(const void*); -#endif - -// This translation unit defines a default dispatch for the allocator shim which -// routes allocations to the original libc functions when using the link-time -// -Wl,-wrap,malloc approach (see README.md). -// The __real_X functions here are special symbols that the linker will relocate -// against the real "X" undefined symbol, so that __real_malloc becomes the -// equivalent of what an undefined malloc symbol reference would have been. -// This is the counterpart of allocator_shim_override_linker_wrapped_symbols.h, -// which routes the __wrap_X functions into the shim. - -extern "C" { -void* __real_malloc(size_t); -void* __real_calloc(size_t, size_t); -void* __real_realloc(void*, size_t); -void* __real_memalign(size_t, size_t); -void* __real_free(void*); -} // extern "C" - -namespace { - -using base::allocator::AllocatorDispatch; - -void* RealMalloc(const AllocatorDispatch*, size_t size, void* context) { - return __real_malloc(size); -} - -void* RealCalloc(const AllocatorDispatch*, - size_t n, - size_t size, - void* context) { - return __real_calloc(n, size); -} - -void* RealRealloc(const AllocatorDispatch*, - void* address, - size_t size, - void* context) { - return __real_realloc(address, size); -} - -void* RealMemalign(const AllocatorDispatch*, - size_t alignment, - size_t size, - void* context) { - return __real_memalign(alignment, size); -} - -void RealFree(const AllocatorDispatch*, void* address, void* context) { - __real_free(address); -} - -#if defined(OS_ANDROID) && __ANDROID_API__ < 17 -size_t DummyMallocUsableSize(const void*) { return 0; } -#endif - -size_t RealSizeEstimate(const AllocatorDispatch*, - void* address, - void* context) { -#if defined(OS_ANDROID) -#if __ANDROID_API__ < 17 - // malloc_usable_size() is available only starting from API 17. - // TODO(dskiba): remove once we start building against 17+. - using MallocUsableSizeFunction = decltype(malloc_usable_size)*; - static MallocUsableSizeFunction usable_size_function = nullptr; - if (!usable_size_function) { - void* function_ptr = dlsym(RTLD_DEFAULT, "malloc_usable_size"); - if (function_ptr) { - usable_size_function = reinterpret_cast( - function_ptr); - } else { - usable_size_function = &DummyMallocUsableSize; - } - } - return usable_size_function(address); -#else - return malloc_usable_size(address); -#endif -#endif // OS_ANDROID - - // TODO(primiano): This should be redirected to malloc_usable_size or - // the like. - return 0; -} - -} // namespace - -const AllocatorDispatch AllocatorDispatch::default_dispatch = { - &RealMalloc, /* alloc_function */ - &RealCalloc, /* alloc_zero_initialized_function */ - &RealMemalign, /* alloc_aligned_function */ - &RealRealloc, /* realloc_function */ - &RealFree, /* free_function */ - &RealSizeEstimate, /* get_size_estimate_function */ - nullptr, /* batch_malloc_function */ - nullptr, /* batch_free_function */ - nullptr, /* free_definite_size_function */ - nullptr, /* next */ -}; diff --git a/allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.cc b/allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.cc deleted file mode 100644 index 32898ef37..000000000 --- a/allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.cc +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.h" - -#include - -#include "base/allocator/allocator_interception_mac.h" -#include "base/allocator/allocator_shim.h" -#include "base/allocator/malloc_zone_functions_mac.h" - -namespace base { -namespace allocator { -namespace { - -void* MallocImpl(const AllocatorDispatch*, size_t size, void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.malloc(reinterpret_cast(context), - size); -} - -void* CallocImpl(const AllocatorDispatch*, - size_t n, - size_t size, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.calloc(reinterpret_cast(context), n, - size); -} - -void* MemalignImpl(const AllocatorDispatch*, - size_t alignment, - size_t size, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.memalign(reinterpret_cast(context), - alignment, size); -} - -void* ReallocImpl(const AllocatorDispatch*, - void* ptr, - size_t size, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.realloc(reinterpret_cast(context), - ptr, size); -} - -void FreeImpl(const AllocatorDispatch*, void* ptr, void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - functions.free(reinterpret_cast(context), ptr); -} - -size_t GetSizeEstimateImpl(const AllocatorDispatch*, void* ptr, void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.size(reinterpret_cast(context), ptr); -} - -unsigned BatchMallocImpl(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - return functions.batch_malloc( - reinterpret_cast(context), size, results, - num_requested); -} - -void BatchFreeImpl(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - functions.batch_free(reinterpret_cast(context), - to_be_freed, num_to_be_freed); -} - -void FreeDefiniteSizeImpl(const AllocatorDispatch* self, - void* ptr, - size_t size, - void* context) { - MallocZoneFunctions& functions = GetFunctionsForZone(context); - functions.free_definite_size( - reinterpret_cast(context), ptr, size); -} - -} // namespace - -void InitializeDefaultDispatchToMacAllocator() { - StoreFunctionsForAllZones(); -} - -const AllocatorDispatch AllocatorDispatch::default_dispatch = { - &MallocImpl, /* alloc_function */ - &CallocImpl, /* alloc_zero_initialized_function */ - &MemalignImpl, /* alloc_aligned_function */ - &ReallocImpl, /* realloc_function */ - &FreeImpl, /* free_function */ - &GetSizeEstimateImpl, /* get_size_estimate_function */ - &BatchMallocImpl, /* batch_malloc_function */ - &BatchFreeImpl, /* batch_free_function */ - &FreeDefiniteSizeImpl, /* free_definite_size_function */ - nullptr, /* next */ -}; - -} // namespace allocator -} // namespace base diff --git a/allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.h b/allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.h deleted file mode 100644 index 77d533c19..000000000 --- a/allocator/allocator_shim_default_dispatch_to_mac_zoned_malloc.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_ALLOCATOR_SHIM_DEFAULT_DISPATCH_TO_ZONED_MALLOC_H_ -#define BASE_ALLOCATOR_ALLOCATOR_SHIM_DEFAULT_DISPATCH_TO_ZONED_MALLOC_H_ - -namespace base { -namespace allocator { - -// This initializes AllocatorDispatch::default_dispatch by saving pointers to -// the functions in the current default malloc zone. This must be called before -// the default malloc zone is changed to have its intended effect. -void InitializeDefaultDispatchToMacAllocator(); - -} // namespace allocator -} // namespace base - -#endif // BASE_ALLOCATOR_ALLOCATOR_SHIM_DEFAULT_DISPATCH_TO_ZONED_MALLOC_H_ diff --git a/allocator/allocator_shim_default_dispatch_to_tcmalloc.cc b/allocator/allocator_shim_default_dispatch_to_tcmalloc.cc deleted file mode 100644 index 878e8a725..000000000 --- a/allocator/allocator_shim_default_dispatch_to_tcmalloc.cc +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/allocator_shim.h" -#include "base/allocator/allocator_shim_internals.h" -#include "third_party/tcmalloc/chromium/src/config.h" -#include "third_party/tcmalloc/chromium/src/gperftools/tcmalloc.h" - -namespace { - -using base::allocator::AllocatorDispatch; - -void* TCMalloc(const AllocatorDispatch*, size_t size, void* context) { - return tc_malloc(size); -} - -void* TCCalloc(const AllocatorDispatch*, size_t n, size_t size, void* context) { - return tc_calloc(n, size); -} - -void* TCMemalign(const AllocatorDispatch*, - size_t alignment, - size_t size, - void* context) { - return tc_memalign(alignment, size); -} - -void* TCRealloc(const AllocatorDispatch*, - void* address, - size_t size, - void* context) { - return tc_realloc(address, size); -} - -void TCFree(const AllocatorDispatch*, void* address, void* context) { - tc_free(address); -} - -size_t TCGetSizeEstimate(const AllocatorDispatch*, - void* address, - void* context) { - return tc_malloc_size(address); -} - -} // namespace - -const AllocatorDispatch AllocatorDispatch::default_dispatch = { - &TCMalloc, /* alloc_function */ - &TCCalloc, /* alloc_zero_initialized_function */ - &TCMemalign, /* alloc_aligned_function */ - &TCRealloc, /* realloc_function */ - &TCFree, /* free_function */ - &TCGetSizeEstimate, /* get_size_estimate_function */ - nullptr, /* batch_malloc_function */ - nullptr, /* batch_free_function */ - nullptr, /* free_definite_size_function */ - nullptr, /* next */ -}; - -// In the case of tcmalloc we have also to route the diagnostic symbols, -// which are not part of the unified shim layer, to tcmalloc for consistency. - -extern "C" { - -SHIM_ALWAYS_EXPORT void malloc_stats(void) __THROW { - return tc_malloc_stats(); -} - -SHIM_ALWAYS_EXPORT int mallopt(int cmd, int value) __THROW { - return tc_mallopt(cmd, value); -} - -#ifdef HAVE_STRUCT_MALLINFO -SHIM_ALWAYS_EXPORT struct mallinfo mallinfo(void) __THROW { - return tc_mallinfo(); -} -#endif - -SHIM_ALWAYS_EXPORT size_t malloc_size(void* address) __THROW { - return tc_malloc_size(address); -} - -#if defined(__ANDROID__) -SHIM_ALWAYS_EXPORT size_t malloc_usable_size(const void* address) __THROW { -#else -SHIM_ALWAYS_EXPORT size_t malloc_usable_size(void* address) __THROW { -#endif - return tc_malloc_size(address); -} - -} // extern "C" diff --git a/allocator/allocator_shim_default_dispatch_to_winheap.cc b/allocator/allocator_shim_default_dispatch_to_winheap.cc deleted file mode 100644 index 6aba5a30c..000000000 --- a/allocator/allocator_shim_default_dispatch_to_winheap.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/allocator_shim.h" - -#include "base/allocator/winheap_stubs_win.h" -#include "base/logging.h" - -namespace { - -using base::allocator::AllocatorDispatch; - -void* DefaultWinHeapMallocImpl(const AllocatorDispatch*, - size_t size, - void* context) { - return base::allocator::WinHeapMalloc(size); -} - -void* DefaultWinHeapCallocImpl(const AllocatorDispatch* self, - size_t n, - size_t elem_size, - void* context) { - // Overflow check. - const size_t size = n * elem_size; - if (elem_size != 0 && size / elem_size != n) - return nullptr; - - void* result = DefaultWinHeapMallocImpl(self, size, context); - if (result) { - memset(result, 0, size); - } - return result; -} - -void* DefaultWinHeapMemalignImpl(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - CHECK(false) << "The windows heap does not support memalign."; - return nullptr; -} - -void* DefaultWinHeapReallocImpl(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - return base::allocator::WinHeapRealloc(address, size); -} - -void DefaultWinHeapFreeImpl(const AllocatorDispatch*, - void* address, - void* context) { - base::allocator::WinHeapFree(address); -} - -size_t DefaultWinHeapGetSizeEstimateImpl(const AllocatorDispatch*, - void* address, - void* context) { - return base::allocator::WinHeapGetSizeEstimate(address); -} - -} // namespace - -// Guarantee that default_dispatch is compile-time initialized to avoid using -// it before initialization (allocations before main in release builds with -// optimizations disabled). -constexpr AllocatorDispatch AllocatorDispatch::default_dispatch = { - &DefaultWinHeapMallocImpl, - &DefaultWinHeapCallocImpl, - &DefaultWinHeapMemalignImpl, - &DefaultWinHeapReallocImpl, - &DefaultWinHeapFreeImpl, - &DefaultWinHeapGetSizeEstimateImpl, - nullptr, /* batch_malloc_function */ - nullptr, /* batch_free_function */ - nullptr, /* free_definite_size_function */ - nullptr, /* next */ -}; diff --git a/allocator/allocator_shim_internals.h b/allocator/allocator_shim_internals.h deleted file mode 100644 index 0196f899a..000000000 --- a/allocator/allocator_shim_internals.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_ALLOCATOR_SHIM_INTERNALS_H_ -#define BASE_ALLOCATOR_ALLOCATOR_SHIM_INTERNALS_H_ - -#if defined(__GNUC__) - -#include // for __THROW - -#ifndef __THROW // Not a glibc system -#ifdef _NOEXCEPT // LLVM libc++ uses noexcept instead -#define __THROW _NOEXCEPT -#else -#define __THROW -#endif // !_NOEXCEPT -#endif - -// Shim layer symbols need to be ALWAYS exported, regardless of component build. -// -// If an exported symbol is linked into a DSO, it may be preempted by a -// definition in the main executable. If this happens to an allocator symbol, it -// will mean that the DSO will use the main executable's allocator. This is -// normally relatively harmless -- regular allocations should all use the same -// allocator, but if the DSO tries to hook the allocator it will not see any -// allocations. -// -// However, if LLVM LTO is enabled, the compiler may inline the shim layer -// symbols into callers. The end result is that allocator calls in DSOs may use -// either the main executable's allocator or the DSO's allocator, depending on -// whether the call was inlined. This is arguably a bug in LLVM caused by its -// somewhat irregular handling of symbol interposition (see llvm.org/PR23501). -// To work around the bug we use noinline to prevent the symbols from being -// inlined. -// -// In the long run we probably want to avoid linking the allocator bits into -// DSOs altogether. This will save a little space and stop giving DSOs the false -// impression that they can hook the allocator. -#define SHIM_ALWAYS_EXPORT __attribute__((visibility("default"), noinline)) - -#endif // __GNUC__ - -#endif // BASE_ALLOCATOR_ALLOCATOR_SHIM_INTERNALS_H_ diff --git a/allocator/allocator_shim_override_cpp_symbols.h b/allocator/allocator_shim_override_cpp_symbols.h deleted file mode 100644 index b1e6ee250..000000000 --- a/allocator/allocator_shim_override_cpp_symbols.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifdef BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_CPP_SYMBOLS_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif -#define BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_CPP_SYMBOLS_H_ - -// Preempt the default new/delete C++ symbols so they call the shim entry -// points. This file is strongly inspired by tcmalloc's -// libc_override_redefine.h. - -#include - -#include "base/allocator/allocator_shim_internals.h" - -SHIM_ALWAYS_EXPORT void* operator new(size_t size) { - return ShimCppNew(size); -} - -SHIM_ALWAYS_EXPORT void operator delete(void* p) __THROW { - ShimCppDelete(p); -} - -SHIM_ALWAYS_EXPORT void* operator new[](size_t size) { - return ShimCppNew(size); -} - -SHIM_ALWAYS_EXPORT void operator delete[](void* p) __THROW { - ShimCppDelete(p); -} - -SHIM_ALWAYS_EXPORT void* operator new(size_t size, - const std::nothrow_t&) __THROW { - return ShimCppNew(size); -} - -SHIM_ALWAYS_EXPORT void* operator new[](size_t size, - const std::nothrow_t&) __THROW { - return ShimCppNew(size); -} - -SHIM_ALWAYS_EXPORT void operator delete(void* p, const std::nothrow_t&) __THROW { - ShimCppDelete(p); -} - -SHIM_ALWAYS_EXPORT void operator delete[](void* p, - const std::nothrow_t&) __THROW { - ShimCppDelete(p); -} - -SHIM_ALWAYS_EXPORT void operator delete(void* p, size_t) __THROW { - ShimCppDelete(p); -} - -SHIM_ALWAYS_EXPORT void operator delete[](void* p, size_t) __THROW { - ShimCppDelete(p); -} diff --git a/allocator/allocator_shim_override_glibc_weak_symbols.h b/allocator/allocator_shim_override_glibc_weak_symbols.h deleted file mode 100644 index 9142bdaf2..000000000 --- a/allocator/allocator_shim_override_glibc_weak_symbols.h +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifdef BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_GLIBC_WEAK_SYMBOLS_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif -#define BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_GLIBC_WEAK_SYMBOLS_H_ - -// Alias the internal Glibc symbols to the shim entry points. -// This file is strongly inspired by tcmalloc's libc_override_glibc.h. -// Effectively this file does two things: -// 1) Re-define the __malloc_hook & co symbols. Those symbols are defined as -// weak in glibc and are meant to be defined strongly by client processes -// to hook calls initiated from within glibc. -// 2) Re-define Glibc-specific symbols (__libc_malloc). The historical reason -// is that in the past (in RedHat 9) we had instances of libraries that were -// allocating via malloc() and freeing using __libc_free(). -// See tcmalloc's libc_override_glibc.h for more context. - -#include // for __GLIBC__ -#include -#include - -#include - -#include "base/allocator/allocator_shim_internals.h" - -// __MALLOC_HOOK_VOLATILE not defined in all Glibc headers. -#if !defined(__MALLOC_HOOK_VOLATILE) -#define MALLOC_HOOK_MAYBE_VOLATILE /**/ -#else -#define MALLOC_HOOK_MAYBE_VOLATILE __MALLOC_HOOK_VOLATILE -#endif - -extern "C" { - -// 1) Re-define malloc_hook weak symbols. -namespace { - -void* GlibcMallocHook(size_t size, const void* caller) { - return ShimMalloc(size, nullptr); -} - -void* GlibcReallocHook(void* ptr, size_t size, const void* caller) { - return ShimRealloc(ptr, size, nullptr); -} - -void GlibcFreeHook(void* ptr, const void* caller) { - return ShimFree(ptr, nullptr); -} - -void* GlibcMemalignHook(size_t align, size_t size, const void* caller) { - return ShimMemalign(align, size, nullptr); -} - -} // namespace - -__attribute__((visibility("default"))) void* ( - *MALLOC_HOOK_MAYBE_VOLATILE __malloc_hook)(size_t, - const void*) = &GlibcMallocHook; - -__attribute__((visibility("default"))) void* ( - *MALLOC_HOOK_MAYBE_VOLATILE __realloc_hook)(void*, size_t, const void*) = - &GlibcReallocHook; - -__attribute__((visibility("default"))) void ( - *MALLOC_HOOK_MAYBE_VOLATILE __free_hook)(void*, - const void*) = &GlibcFreeHook; - -__attribute__((visibility("default"))) void* ( - *MALLOC_HOOK_MAYBE_VOLATILE __memalign_hook)(size_t, size_t, const void*) = - &GlibcMemalignHook; - -// 2) Redefine libc symbols themselves. - -SHIM_ALWAYS_EXPORT void* __libc_malloc(size_t size) { - return ShimMalloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT void __libc_free(void* ptr) { - ShimFree(ptr, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __libc_realloc(void* ptr, size_t size) { - return ShimRealloc(ptr, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __libc_calloc(size_t n, size_t size) { - return ShimCalloc(n, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void __libc_cfree(void* ptr) { - return ShimFree(ptr, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __libc_memalign(size_t align, size_t s) { - return ShimMemalign(align, s, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __libc_valloc(size_t size) { - return ShimValloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __libc_pvalloc(size_t size) { - return ShimPvalloc(size); -} - -SHIM_ALWAYS_EXPORT int __posix_memalign(void** r, size_t a, size_t s) { - return ShimPosixMemalign(r, a, s); -} - -} // extern "C" - -// Safety check. -#if !defined(__GLIBC__) -#error The target platform does not seem to use Glibc. Disable the allocator \ -shim by setting use_allocator_shim=false in GN args. -#endif diff --git a/allocator/allocator_shim_override_libc_symbols.h b/allocator/allocator_shim_override_libc_symbols.h deleted file mode 100644 index b77cbb1fe..000000000 --- a/allocator/allocator_shim_override_libc_symbols.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Its purpose is to preempt the Libc symbols for malloc/new so they call the -// shim layer entry points. - -#ifdef BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_LIBC_SYMBOLS_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif -#define BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_LIBC_SYMBOLS_H_ - -#include - -#include "base/allocator/allocator_shim_internals.h" - -extern "C" { - -SHIM_ALWAYS_EXPORT void* malloc(size_t size) __THROW { - return ShimMalloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT void free(void* ptr) __THROW { - ShimFree(ptr, nullptr); -} - -SHIM_ALWAYS_EXPORT void* realloc(void* ptr, size_t size) __THROW { - return ShimRealloc(ptr, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* calloc(size_t n, size_t size) __THROW { - return ShimCalloc(n, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void cfree(void* ptr) __THROW { - ShimFree(ptr, nullptr); -} - -SHIM_ALWAYS_EXPORT void* memalign(size_t align, size_t s) __THROW { - return ShimMemalign(align, s, nullptr); -} - -SHIM_ALWAYS_EXPORT void* valloc(size_t size) __THROW { - return ShimValloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* pvalloc(size_t size) __THROW { - return ShimPvalloc(size); -} - -SHIM_ALWAYS_EXPORT int posix_memalign(void** r, size_t a, size_t s) __THROW { - return ShimPosixMemalign(r, a, s); -} - -// The default dispatch translation unit has to define also the following -// symbols (unless they are ultimately routed to the system symbols): -// void malloc_stats(void); -// int mallopt(int, int); -// struct mallinfo mallinfo(void); -// size_t malloc_size(void*); -// size_t malloc_usable_size(const void*); - -} // extern "C" diff --git a/allocator/allocator_shim_override_linker_wrapped_symbols.h b/allocator/allocator_shim_override_linker_wrapped_symbols.h deleted file mode 100644 index 6bf73c39f..000000000 --- a/allocator/allocator_shim_override_linker_wrapped_symbols.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifdef BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_LINKER_WRAPPED_SYMBOLS_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif -#define BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_LINKER_WRAPPED_SYMBOLS_H_ - -// This header overrides the __wrap_X symbols when using the link-time -// -Wl,-wrap,malloc shim-layer approach (see README.md). -// All references to malloc, free, etc. within the linker unit that gets the -// -wrap linker flags (e.g., libchrome.so) will be rewritten to the -// linker as references to __wrap_malloc, __wrap_free, which are defined here. - -#include "base/allocator/allocator_shim_internals.h" - -extern "C" { - -SHIM_ALWAYS_EXPORT void* __wrap_calloc(size_t n, size_t size) { - return ShimCalloc(n, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void __wrap_free(void* ptr) { - ShimFree(ptr, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __wrap_malloc(size_t size) { - return ShimMalloc(size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __wrap_memalign(size_t align, size_t size) { - return ShimMemalign(align, size, nullptr); -} - -SHIM_ALWAYS_EXPORT int __wrap_posix_memalign(void** res, - size_t align, - size_t size) { - return ShimPosixMemalign(res, align, size); -} - -SHIM_ALWAYS_EXPORT void* __wrap_pvalloc(size_t size) { - return ShimPvalloc(size); -} - -SHIM_ALWAYS_EXPORT void* __wrap_realloc(void* address, size_t size) { - return ShimRealloc(address, size, nullptr); -} - -SHIM_ALWAYS_EXPORT void* __wrap_valloc(size_t size) { - return ShimValloc(size, nullptr); -} - -} // extern "C" diff --git a/allocator/allocator_shim_override_mac_symbols.h b/allocator/allocator_shim_override_mac_symbols.h deleted file mode 100644 index 0b65edb15..000000000 --- a/allocator/allocator_shim_override_mac_symbols.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifdef BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_MAC_SYMBOLS_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif -#define BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_MAC_SYMBOLS_H_ - -#include "base/allocator/malloc_zone_functions_mac.h" -#include "third_party/apple_apsl/malloc.h" - -namespace base { -namespace allocator { - -MallocZoneFunctions MallocZoneFunctionsToReplaceDefault() { - MallocZoneFunctions new_functions; - memset(&new_functions, 0, sizeof(MallocZoneFunctions)); - new_functions.size = [](malloc_zone_t* zone, const void* ptr) -> size_t { - return ShimGetSizeEstimate(ptr, zone); - }; - new_functions.malloc = [](malloc_zone_t* zone, size_t size) -> void* { - return ShimMalloc(size, zone); - }; - new_functions.calloc = [](malloc_zone_t* zone, size_t n, - size_t size) -> void* { - return ShimCalloc(n, size, zone); - }; - new_functions.valloc = [](malloc_zone_t* zone, size_t size) -> void* { - return ShimValloc(size, zone); - }; - new_functions.free = [](malloc_zone_t* zone, void* ptr) { - ShimFree(ptr, zone); - }; - new_functions.realloc = [](malloc_zone_t* zone, void* ptr, - size_t size) -> void* { - return ShimRealloc(ptr, size, zone); - }; - new_functions.batch_malloc = [](struct _malloc_zone_t* zone, size_t size, - void** results, - unsigned num_requested) -> unsigned { - return ShimBatchMalloc(size, results, num_requested, zone); - }; - new_functions.batch_free = [](struct _malloc_zone_t* zone, void** to_be_freed, - unsigned num_to_be_freed) -> void { - ShimBatchFree(to_be_freed, num_to_be_freed, zone); - }; - new_functions.memalign = [](malloc_zone_t* zone, size_t alignment, - size_t size) -> void* { - return ShimMemalign(alignment, size, zone); - }; - new_functions.free_definite_size = [](malloc_zone_t* zone, void* ptr, - size_t size) { - ShimFreeDefiniteSize(ptr, size, zone); - }; - return new_functions; -} - -} // namespace allocator -} // namespace base diff --git a/allocator/allocator_shim_override_ucrt_symbols_win.h b/allocator/allocator_shim_override_ucrt_symbols_win.h deleted file mode 100644 index ed0265633..000000000 --- a/allocator/allocator_shim_override_ucrt_symbols_win.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2016 The Chromium 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 header defines symbols to override the same functions in the Visual C++ -// CRT implementation. - -#ifdef BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_UCRT_SYMBOLS_WIN_H_ -#error This header is meant to be included only once by allocator_shim.cc -#endif -#define BASE_ALLOCATOR_ALLOCATOR_SHIM_OVERRIDE_UCRT_SYMBOLS_WIN_H_ - -#include - -#include - -extern "C" { - -void* (*malloc_unchecked)(size_t) = &base::allocator::UncheckedAlloc; - -namespace { - -int win_new_mode = 0; - -} // namespace - -// This function behaves similarly to MSVC's _set_new_mode. -// If flag is 0 (default), calls to malloc will behave normally. -// If flag is 1, calls to malloc will behave like calls to new, -// and the std_new_handler will be invoked on failure. -// Returns the previous mode. -// -// Replaces _set_new_mode in ucrt\heap\new_mode.cpp -int _set_new_mode(int flag) { - // The MS CRT calls this function early on in startup, so this serves as a low - // overhead proof that the allocator shim is in place for this process. - base::allocator::g_is_win_shim_layer_initialized = true; - int old_mode = win_new_mode; - win_new_mode = flag; - - base::allocator::SetCallNewHandlerOnMallocFailure(win_new_mode != 0); - - return old_mode; -} - -// Replaces _query_new_mode in ucrt\heap\new_mode.cpp -int _query_new_mode() { - return win_new_mode; -} - -// These symbols override the CRT's implementation of the same functions. -__declspec(restrict) void* malloc(size_t size) { - return ShimMalloc(size, nullptr); -} - -void free(void* ptr) { - ShimFree(ptr, nullptr); -} - -__declspec(restrict) void* realloc(void* ptr, size_t size) { - return ShimRealloc(ptr, size, nullptr); -} - -__declspec(restrict) void* calloc(size_t n, size_t size) { - return ShimCalloc(n, size, nullptr); -} - -// The symbols -// * __acrt_heap -// * __acrt_initialize_heap -// * __acrt_uninitialize_heap -// * _get_heap_handle -// must be overridden all or none, as they are otherwise supplied -// by heap_handle.obj in the ucrt.lib file. -HANDLE __acrt_heap = nullptr; - -bool __acrt_initialize_heap() { - __acrt_heap = ::HeapCreate(0, 0, 0); - return true; -} - -bool __acrt_uninitialize_heap() { - ::HeapDestroy(__acrt_heap); - __acrt_heap = nullptr; - return true; -} - -intptr_t _get_heap_handle(void) { - return reinterpret_cast(__acrt_heap); -} - -// The default dispatch translation unit has to define also the following -// symbols (unless they are ultimately routed to the system symbols): -// void malloc_stats(void); -// int mallopt(int, int); -// struct mallinfo mallinfo(void); -// size_t malloc_size(void*); -// size_t malloc_usable_size(const void*); - -} // extern "C" diff --git a/allocator/allocator_shim_unittest.cc b/allocator/allocator_shim_unittest.cc deleted file mode 100644 index 3be8f2cab..000000000 --- a/allocator/allocator_shim_unittest.cc +++ /dev/null @@ -1,467 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/allocator_shim.h" - -#include -#include - -#include -#include -#include - -#include "base/allocator/buildflags.h" -#include "base/allocator/partition_allocator/partition_alloc.h" -#include "base/atomicops.h" -#include "base/process/process_metrics.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread_local.h" -#include "build/build_config.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_WIN) -#include -#elif defined(OS_MACOSX) -#include -#include "base/allocator/allocator_interception_mac.h" -#include "base/mac/mac_util.h" -#include "third_party/apple_apsl/malloc.h" -#else -#include -#endif - -#if !defined(OS_WIN) -#include -#endif - -// Some new Android NDKs (64 bit) does not expose (p)valloc anymore. These -// functions are implemented at the shim-layer level. -#if defined(OS_ANDROID) -extern "C" { -void* valloc(size_t size); -void* pvalloc(size_t size); -} -#endif - -namespace base { -namespace allocator { -namespace { - -using testing::MockFunction; -using testing::_; - -class AllocatorShimTest : public testing::Test { - public: - static const size_t kMaxSizeTracked = 2 * base::kSystemPageSize; - AllocatorShimTest() : testing::Test() {} - - static size_t Hash(const void* ptr) { - return reinterpret_cast(ptr) % kMaxSizeTracked; - } - - static void* MockAlloc(const AllocatorDispatch* self, - size_t size, - void* context) { - if (instance_ && size < kMaxSizeTracked) - ++(instance_->allocs_intercepted_by_size[size]); - return self->next->alloc_function(self->next, size, context); - } - - static void* MockAllocZeroInit(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context) { - const size_t real_size = n * size; - if (instance_ && real_size < kMaxSizeTracked) - ++(instance_->zero_allocs_intercepted_by_size[real_size]); - return self->next->alloc_zero_initialized_function(self->next, n, size, - context); - } - - static void* MockAllocAligned(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - if (instance_) { - if (size < kMaxSizeTracked) - ++(instance_->aligned_allocs_intercepted_by_size[size]); - if (alignment < kMaxSizeTracked) - ++(instance_->aligned_allocs_intercepted_by_alignment[alignment]); - } - return self->next->alloc_aligned_function(self->next, alignment, size, - context); - } - - static void* MockRealloc(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - if (instance_) { - // Size 0xFEED a special sentinel for the NewHandlerConcurrency test. - // Hitting it for the first time will cause a failure, causing the - // invocation of the std::new_handler. - if (size == 0xFEED) { - if (!instance_->did_fail_realloc_0xfeed_once->Get()) { - instance_->did_fail_realloc_0xfeed_once->Set(true); - return nullptr; - } else { - return address; - } - } - - if (size < kMaxSizeTracked) - ++(instance_->reallocs_intercepted_by_size[size]); - ++instance_->reallocs_intercepted_by_addr[Hash(address)]; - } - return self->next->realloc_function(self->next, address, size, context); - } - - static void MockFree(const AllocatorDispatch* self, - void* address, - void* context) { - if (instance_) { - ++instance_->frees_intercepted_by_addr[Hash(address)]; - } - self->next->free_function(self->next, address, context); - } - - static size_t MockGetSizeEstimate(const AllocatorDispatch* self, - void* address, - void* context) { - return self->next->get_size_estimate_function(self->next, address, context); - } - - static unsigned MockBatchMalloc(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context) { - if (instance_) { - instance_->batch_mallocs_intercepted_by_size[size] = - instance_->batch_mallocs_intercepted_by_size[size] + num_requested; - } - return self->next->batch_malloc_function(self->next, size, results, - num_requested, context); - } - - static void MockBatchFree(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - if (instance_) { - for (unsigned i = 0; i < num_to_be_freed; ++i) { - ++instance_->batch_frees_intercepted_by_addr[Hash(to_be_freed[i])]; - } - } - self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed, - context); - } - - static void MockFreeDefiniteSize(const AllocatorDispatch* self, - void* ptr, - size_t size, - void* context) { - if (instance_) { - ++instance_->frees_intercepted_by_addr[Hash(ptr)]; - ++instance_->free_definite_sizes_intercepted_by_size[size]; - } - self->next->free_definite_size_function(self->next, ptr, size, context); - } - - static void NewHandler() { - if (!instance_) - return; - subtle::Barrier_AtomicIncrement(&instance_->num_new_handler_calls, 1); - } - - int32_t GetNumberOfNewHandlerCalls() { - return subtle::Acquire_Load(&instance_->num_new_handler_calls); - } - - void SetUp() override { - const size_t array_size = kMaxSizeTracked * sizeof(size_t); - memset(&allocs_intercepted_by_size, 0, array_size); - memset(&zero_allocs_intercepted_by_size, 0, array_size); - memset(&aligned_allocs_intercepted_by_size, 0, array_size); - memset(&aligned_allocs_intercepted_by_alignment, 0, array_size); - memset(&reallocs_intercepted_by_size, 0, array_size); - memset(&frees_intercepted_by_addr, 0, array_size); - memset(&batch_mallocs_intercepted_by_size, 0, array_size); - memset(&batch_frees_intercepted_by_addr, 0, array_size); - memset(&free_definite_sizes_intercepted_by_size, 0, array_size); - did_fail_realloc_0xfeed_once.reset(new ThreadLocalBoolean()); - subtle::Release_Store(&num_new_handler_calls, 0); - instance_ = this; - -#if defined(OS_MACOSX) - InitializeAllocatorShim(); -#endif - } - - void TearDown() override { - instance_ = nullptr; -#if defined(OS_MACOSX) - UninterceptMallocZonesForTesting(); -#endif - } - - protected: - size_t allocs_intercepted_by_size[kMaxSizeTracked]; - size_t zero_allocs_intercepted_by_size[kMaxSizeTracked]; - size_t aligned_allocs_intercepted_by_size[kMaxSizeTracked]; - size_t aligned_allocs_intercepted_by_alignment[kMaxSizeTracked]; - size_t reallocs_intercepted_by_size[kMaxSizeTracked]; - size_t reallocs_intercepted_by_addr[kMaxSizeTracked]; - size_t frees_intercepted_by_addr[kMaxSizeTracked]; - size_t batch_mallocs_intercepted_by_size[kMaxSizeTracked]; - size_t batch_frees_intercepted_by_addr[kMaxSizeTracked]; - size_t free_definite_sizes_intercepted_by_size[kMaxSizeTracked]; - std::unique_ptr did_fail_realloc_0xfeed_once; - subtle::Atomic32 num_new_handler_calls; - - private: - static AllocatorShimTest* instance_; -}; - -struct TestStruct1 { - uint32_t ignored; - uint8_t ignored_2; -}; - -struct TestStruct2 { - uint64_t ignored; - uint8_t ignored_3; -}; - -class ThreadDelegateForNewHandlerTest : public PlatformThread::Delegate { - public: - ThreadDelegateForNewHandlerTest(WaitableEvent* event) : event_(event) {} - - void ThreadMain() override { - event_->Wait(); - void* temp = malloc(1); - void* res = realloc(temp, 0xFEED); - EXPECT_EQ(temp, res); - } - - private: - WaitableEvent* event_; -}; - -AllocatorShimTest* AllocatorShimTest::instance_ = nullptr; - -AllocatorDispatch g_mock_dispatch = { - &AllocatorShimTest::MockAlloc, /* alloc_function */ - &AllocatorShimTest::MockAllocZeroInit, /* alloc_zero_initialized_function */ - &AllocatorShimTest::MockAllocAligned, /* alloc_aligned_function */ - &AllocatorShimTest::MockRealloc, /* realloc_function */ - &AllocatorShimTest::MockFree, /* free_function */ - &AllocatorShimTest::MockGetSizeEstimate, /* get_size_estimate_function */ - &AllocatorShimTest::MockBatchMalloc, /* batch_malloc_function */ - &AllocatorShimTest::MockBatchFree, /* batch_free_function */ - &AllocatorShimTest::MockFreeDefiniteSize, /* free_definite_size_function */ - nullptr, /* next */ -}; - -TEST_F(AllocatorShimTest, InterceptLibcSymbols) { - InsertAllocatorDispatch(&g_mock_dispatch); - - void* alloc_ptr = malloc(19); - ASSERT_NE(nullptr, alloc_ptr); - ASSERT_GE(allocs_intercepted_by_size[19], 1u); - - void* zero_alloc_ptr = calloc(2, 23); - ASSERT_NE(nullptr, zero_alloc_ptr); - ASSERT_GE(zero_allocs_intercepted_by_size[2 * 23], 1u); - -#if !defined(OS_WIN) - const size_t kPageSize = base::GetPageSize(); - void* posix_memalign_ptr = nullptr; - int res = posix_memalign(&posix_memalign_ptr, 256, 59); - ASSERT_EQ(0, res); - ASSERT_NE(nullptr, posix_memalign_ptr); - ASSERT_EQ(0u, reinterpret_cast(posix_memalign_ptr) % 256); - ASSERT_GE(aligned_allocs_intercepted_by_alignment[256], 1u); - ASSERT_GE(aligned_allocs_intercepted_by_size[59], 1u); - - void* valloc_ptr = valloc(61); - ASSERT_NE(nullptr, valloc_ptr); - ASSERT_EQ(0u, reinterpret_cast(valloc_ptr) % kPageSize); - ASSERT_GE(aligned_allocs_intercepted_by_alignment[kPageSize], 1u); - ASSERT_GE(aligned_allocs_intercepted_by_size[61], 1u); -#endif // !OS_WIN - -#if !defined(OS_WIN) && !defined(OS_MACOSX) - void* memalign_ptr = memalign(128, 53); - ASSERT_NE(nullptr, memalign_ptr); - ASSERT_EQ(0u, reinterpret_cast(memalign_ptr) % 128); - ASSERT_GE(aligned_allocs_intercepted_by_alignment[128], 1u); - ASSERT_GE(aligned_allocs_intercepted_by_size[53], 1u); - - void* pvalloc_ptr = pvalloc(67); - ASSERT_NE(nullptr, pvalloc_ptr); - ASSERT_EQ(0u, reinterpret_cast(pvalloc_ptr) % kPageSize); - ASSERT_GE(aligned_allocs_intercepted_by_alignment[kPageSize], 1u); - // pvalloc rounds the size up to the next page. - ASSERT_GE(aligned_allocs_intercepted_by_size[kPageSize], 1u); -#endif // !OS_WIN && !OS_MACOSX - - char* realloc_ptr = static_cast(malloc(10)); - strcpy(realloc_ptr, "foobar"); - void* old_realloc_ptr = realloc_ptr; - realloc_ptr = static_cast(realloc(realloc_ptr, 73)); - ASSERT_GE(reallocs_intercepted_by_size[73], 1u); - ASSERT_GE(reallocs_intercepted_by_addr[Hash(old_realloc_ptr)], 1u); - ASSERT_EQ(0, strcmp(realloc_ptr, "foobar")); - - free(alloc_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(alloc_ptr)], 1u); - - free(zero_alloc_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(zero_alloc_ptr)], 1u); - -#if !defined(OS_WIN) && !defined(OS_MACOSX) - free(memalign_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(memalign_ptr)], 1u); - - free(pvalloc_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(pvalloc_ptr)], 1u); -#endif // !OS_WIN && !OS_MACOSX - -#if !defined(OS_WIN) - free(posix_memalign_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(posix_memalign_ptr)], 1u); - - free(valloc_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(valloc_ptr)], 1u); -#endif // !OS_WIN - - free(realloc_ptr); - ASSERT_GE(frees_intercepted_by_addr[Hash(realloc_ptr)], 1u); - - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); - - void* non_hooked_ptr = malloc(4095); - ASSERT_NE(nullptr, non_hooked_ptr); - ASSERT_EQ(0u, allocs_intercepted_by_size[4095]); - free(non_hooked_ptr); -} - -#if defined(OS_MACOSX) -TEST_F(AllocatorShimTest, InterceptLibcSymbolsBatchMallocFree) { - InsertAllocatorDispatch(&g_mock_dispatch); - - unsigned count = 13; - std::vector results; - results.resize(count); - unsigned result_count = malloc_zone_batch_malloc(malloc_default_zone(), 99, - results.data(), count); - ASSERT_EQ(count, result_count); - - // TODO(erikchen): On macOS 10.12+, batch_malloc in the default zone may - // forward to another zone, which we've also shimmed, resulting in - // MockBatchMalloc getting called twice as often as we'd expect. This - // re-entrancy into the allocator shim is a bug that needs to be fixed. - // https://crbug.com/693237. - // ASSERT_EQ(count, batch_mallocs_intercepted_by_size[99]); - - std::vector results_copy(results); - malloc_zone_batch_free(malloc_default_zone(), results.data(), count); - for (void* result : results_copy) { - ASSERT_GE(batch_frees_intercepted_by_addr[Hash(result)], 1u); - } - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); -} - -TEST_F(AllocatorShimTest, InterceptLibcSymbolsFreeDefiniteSize) { - InsertAllocatorDispatch(&g_mock_dispatch); - - void* alloc_ptr = malloc(19); - ASSERT_NE(nullptr, alloc_ptr); - ASSERT_GE(allocs_intercepted_by_size[19], 1u); - - ChromeMallocZone* default_zone = - reinterpret_cast(malloc_default_zone()); - default_zone->free_definite_size(malloc_default_zone(), alloc_ptr, 19); - ASSERT_GE(free_definite_sizes_intercepted_by_size[19], 1u); - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); -} -#endif // defined(OS_MACOSX) - -TEST_F(AllocatorShimTest, InterceptCppSymbols) { - InsertAllocatorDispatch(&g_mock_dispatch); - - TestStruct1* new_ptr = new TestStruct1; - ASSERT_NE(nullptr, new_ptr); - ASSERT_GE(allocs_intercepted_by_size[sizeof(TestStruct1)], 1u); - - TestStruct1* new_array_ptr = new TestStruct1[3]; - ASSERT_NE(nullptr, new_array_ptr); - ASSERT_GE(allocs_intercepted_by_size[sizeof(TestStruct1) * 3], 1u); - - TestStruct2* new_nt_ptr = new (std::nothrow) TestStruct2; - ASSERT_NE(nullptr, new_nt_ptr); - ASSERT_GE(allocs_intercepted_by_size[sizeof(TestStruct2)], 1u); - - TestStruct2* new_array_nt_ptr = new TestStruct2[3]; - ASSERT_NE(nullptr, new_array_nt_ptr); - ASSERT_GE(allocs_intercepted_by_size[sizeof(TestStruct2) * 3], 1u); - - delete new_ptr; - ASSERT_GE(frees_intercepted_by_addr[Hash(new_ptr)], 1u); - - delete[] new_array_ptr; - ASSERT_GE(frees_intercepted_by_addr[Hash(new_array_ptr)], 1u); - - delete new_nt_ptr; - ASSERT_GE(frees_intercepted_by_addr[Hash(new_nt_ptr)], 1u); - - delete[] new_array_nt_ptr; - ASSERT_GE(frees_intercepted_by_addr[Hash(new_array_nt_ptr)], 1u); - - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); -} - -// This test exercises the case of concurrent OOM failure, which would end up -// invoking std::new_handler concurrently. This is to cover the CallNewHandler() -// paths of allocator_shim.cc and smoke-test its thread safey. -// The test creates kNumThreads threads. Each of them mallocs some memory, and -// then does a realloc(, 0xFEED). -// The shim intercepts such realloc and makes it fail only once on each thread. -// We expect to see excactly kNumThreads invocations of the new_handler. -TEST_F(AllocatorShimTest, NewHandlerConcurrency) { - const int kNumThreads = 32; - PlatformThreadHandle threads[kNumThreads]; - - // The WaitableEvent here is used to attempt to trigger all the threads at - // the same time, after they have been initialized. - WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - - ThreadDelegateForNewHandlerTest mock_thread_main(&event); - - for (int i = 0; i < kNumThreads; ++i) - PlatformThread::Create(0, &mock_thread_main, &threads[i]); - - std::set_new_handler(&AllocatorShimTest::NewHandler); - SetCallNewHandlerOnMallocFailure(true); // It's going to fail on realloc(). - InsertAllocatorDispatch(&g_mock_dispatch); - event.Signal(); - for (int i = 0; i < kNumThreads; ++i) - PlatformThread::Join(threads[i]); - RemoveAllocatorDispatchForTesting(&g_mock_dispatch); - ASSERT_EQ(kNumThreads, GetNumberOfNewHandlerCalls()); -} - -#if defined(OS_WIN) && BUILDFLAG(USE_ALLOCATOR_SHIM) -TEST_F(AllocatorShimTest, ShimReplacesCRTHeapWhenEnabled) { - ASSERT_NE(::GetProcessHeap(), reinterpret_cast(_get_heap_handle())); -} -#endif // defined(OS_WIN) && BUILDFLAG(USE_ALLOCATOR_SHIM) - -} // namespace -} // namespace allocator -} // namespace base diff --git a/allocator/debugallocation_shim.cc b/allocator/debugallocation_shim.cc deleted file mode 100644 index 479cfcad7..000000000 --- a/allocator/debugallocation_shim.cc +++ /dev/null @@ -1,20 +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. - -// Workaround for crosbug:629593. Using AFDO on the tcmalloc files is -// causing problems. The tcmalloc files depend on stack layouts and -// AFDO can mess with them. Better not to use AFDO there. This is a -// temporary hack. We will add a mechanism in the build system to -// avoid using -fauto-profile for tcmalloc files. -#if !defined(__clang__) && (defined(OS_CHROMEOS) || __GNUC__ > 5) -// Note that this option only seems to be available in the chromeos GCC 4.9 -// toolchain, and stock GCC 5 and up. -#pragma GCC optimize ("no-auto-profile") -#endif - -#if defined(TCMALLOC_FOR_DEBUGALLOCATION) -#include "third_party/tcmalloc/chromium/src/debugallocation.cc" -#else -#include "third_party/tcmalloc/chromium/src/tcmalloc.cc" -#endif diff --git a/allocator/malloc_zone_functions_mac.cc b/allocator/malloc_zone_functions_mac.cc deleted file mode 100644 index 9a4149603..000000000 --- a/allocator/malloc_zone_functions_mac.cc +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/malloc_zone_functions_mac.h" - -#include "base/atomicops.h" -#include "base/synchronization/lock.h" - -namespace base { -namespace allocator { - -MallocZoneFunctions g_malloc_zones[kMaxZoneCount]; -static_assert(std::is_pod::value, - "MallocZoneFunctions must be POD"); - -void StoreZoneFunctions(const ChromeMallocZone* zone, - MallocZoneFunctions* functions) { - memset(functions, 0, sizeof(MallocZoneFunctions)); - functions->malloc = zone->malloc; - functions->calloc = zone->calloc; - functions->valloc = zone->valloc; - functions->free = zone->free; - functions->realloc = zone->realloc; - functions->size = zone->size; - CHECK(functions->malloc && functions->calloc && functions->valloc && - functions->free && functions->realloc && functions->size); - - // These functions might be nullptr. - functions->batch_malloc = zone->batch_malloc; - functions->batch_free = zone->batch_free; - - if (zone->version >= 5) { - // Not all custom malloc zones have a memalign. - functions->memalign = zone->memalign; - } - if (zone->version >= 6) { - // This may be nullptr. - functions->free_definite_size = zone->free_definite_size; - } - - functions->context = zone; -} - -namespace { - -// All modifications to g_malloc_zones are gated behind this lock. -// Dispatch to a malloc zone does not need to acquire this lock. -base::Lock& GetLock() { - static base::Lock* g_lock = new base::Lock; - return *g_lock; -} - -void EnsureMallocZonesInitializedLocked() { - GetLock().AssertAcquired(); -} - -int g_zone_count = 0; - -bool IsMallocZoneAlreadyStoredLocked(ChromeMallocZone* zone) { - EnsureMallocZonesInitializedLocked(); - GetLock().AssertAcquired(); - for (int i = 0; i < g_zone_count; ++i) { - if (g_malloc_zones[i].context == reinterpret_cast(zone)) - return true; - } - return false; -} - -} // namespace - -bool StoreMallocZone(ChromeMallocZone* zone) { - base::AutoLock l(GetLock()); - EnsureMallocZonesInitializedLocked(); - if (IsMallocZoneAlreadyStoredLocked(zone)) - return false; - - if (g_zone_count == kMaxZoneCount) - return false; - - StoreZoneFunctions(zone, &g_malloc_zones[g_zone_count]); - ++g_zone_count; - - // No other thread can possibly see these stores at this point. The code that - // reads these values is triggered after this function returns. so we want to - // guarantee that they are committed at this stage" - base::subtle::MemoryBarrier(); - return true; -} - -bool IsMallocZoneAlreadyStored(ChromeMallocZone* zone) { - base::AutoLock l(GetLock()); - return IsMallocZoneAlreadyStoredLocked(zone); -} - -bool DoesMallocZoneNeedReplacing(ChromeMallocZone* zone, - const MallocZoneFunctions* functions) { - return IsMallocZoneAlreadyStored(zone) && zone->malloc != functions->malloc; -} - -int GetMallocZoneCountForTesting() { - base::AutoLock l(GetLock()); - return g_zone_count; -} - -void ClearAllMallocZonesForTesting() { - base::AutoLock l(GetLock()); - EnsureMallocZonesInitializedLocked(); - memset(g_malloc_zones, 0, kMaxZoneCount * sizeof(MallocZoneFunctions)); - g_zone_count = 0; -} - -} // namespace allocator -} // namespace base diff --git a/allocator/malloc_zone_functions_mac.h b/allocator/malloc_zone_functions_mac.h deleted file mode 100644 index a7f554337..000000000 --- a/allocator/malloc_zone_functions_mac.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_MALLOC_ZONE_FUNCTIONS_MAC_H_ -#define BASE_ALLOCATOR_MALLOC_ZONE_FUNCTIONS_MAC_H_ - -#include -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "third_party/apple_apsl/malloc.h" - -namespace base { -namespace allocator { - -typedef void* (*malloc_type)(struct _malloc_zone_t* zone, size_t size); -typedef void* (*calloc_type)(struct _malloc_zone_t* zone, - size_t num_items, - size_t size); -typedef void* (*valloc_type)(struct _malloc_zone_t* zone, size_t size); -typedef void (*free_type)(struct _malloc_zone_t* zone, void* ptr); -typedef void* (*realloc_type)(struct _malloc_zone_t* zone, - void* ptr, - size_t size); -typedef void* (*memalign_type)(struct _malloc_zone_t* zone, - size_t alignment, - size_t size); -typedef unsigned (*batch_malloc_type)(struct _malloc_zone_t* zone, - size_t size, - void** results, - unsigned num_requested); -typedef void (*batch_free_type)(struct _malloc_zone_t* zone, - void** to_be_freed, - unsigned num_to_be_freed); -typedef void (*free_definite_size_type)(struct _malloc_zone_t* zone, - void* ptr, - size_t size); -typedef size_t (*size_fn_type)(struct _malloc_zone_t* zone, const void* ptr); - -struct MallocZoneFunctions { - malloc_type malloc; - calloc_type calloc; - valloc_type valloc; - free_type free; - realloc_type realloc; - memalign_type memalign; - batch_malloc_type batch_malloc; - batch_free_type batch_free; - free_definite_size_type free_definite_size; - size_fn_type size; - const ChromeMallocZone* context; -}; - -BASE_EXPORT void StoreZoneFunctions(const ChromeMallocZone* zone, - MallocZoneFunctions* functions); -static constexpr int kMaxZoneCount = 30; -BASE_EXPORT extern MallocZoneFunctions g_malloc_zones[kMaxZoneCount]; - -// The array g_malloc_zones stores all information about malloc zones before -// they are shimmed. This information needs to be accessed during dispatch back -// into the zone, and additional zones may be added later in the execution fo -// the program, so the array needs to be both thread-safe and high-performance. -// -// We begin by creating an array of MallocZoneFunctions of fixed size. We will -// never modify the container, which provides thread-safety to iterators. When -// we want to add a MallocZoneFunctions to the container, we: -// 1. Fill in all the fields. -// 2. Update the total zone count. -// 3. Insert a memory barrier. -// 4. Insert our shim. -// -// Each MallocZoneFunctions is uniquely identified by |context|, which is a -// pointer to the original malloc zone. When we wish to dispatch back to the -// original malloc zones, we iterate through the array, looking for a matching -// |context|. -// -// Most allocations go through the default allocator. We will ensure that the -// default allocator is stored as the first MallocZoneFunctions. -// -// Returns whether the zone was successfully stored. -BASE_EXPORT bool StoreMallocZone(ChromeMallocZone* zone); -BASE_EXPORT bool IsMallocZoneAlreadyStored(ChromeMallocZone* zone); -BASE_EXPORT bool DoesMallocZoneNeedReplacing( - ChromeMallocZone* zone, - const MallocZoneFunctions* functions); - -BASE_EXPORT int GetMallocZoneCountForTesting(); -BASE_EXPORT void ClearAllMallocZonesForTesting(); - -inline MallocZoneFunctions& GetFunctionsForZone(void* zone) { - for (unsigned int i = 0; i < kMaxZoneCount; ++i) { - if (g_malloc_zones[i].context == zone) - return g_malloc_zones[i]; - } - IMMEDIATE_CRASH(); -} - -} // namespace allocator -} // namespace base - -#endif // BASE_ALLOCATOR_MALLOC_ZONE_FUNCTIONS_MAC_H_ diff --git a/allocator/malloc_zone_functions_mac_unittest.cc b/allocator/malloc_zone_functions_mac_unittest.cc deleted file mode 100644 index 09aa42936..000000000 --- a/allocator/malloc_zone_functions_mac_unittest.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/malloc_zone_functions_mac.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace allocator { - -class MallocZoneFunctionsTest : public testing::Test { - protected: - void TearDown() override { ClearAllMallocZonesForTesting(); } -}; - -TEST_F(MallocZoneFunctionsTest, TestDefaultZoneMallocFree) { - ChromeMallocZone* malloc_zone = - reinterpret_cast(malloc_default_zone()); - StoreMallocZone(malloc_zone); - int* test = reinterpret_cast( - g_malloc_zones[0].malloc(malloc_default_zone(), 33)); - test[0] = 1; - test[1] = 2; - g_malloc_zones[0].free(malloc_default_zone(), test); -} - -TEST_F(MallocZoneFunctionsTest, IsZoneAlreadyStored) { - ChromeMallocZone* malloc_zone = - reinterpret_cast(malloc_default_zone()); - EXPECT_FALSE(IsMallocZoneAlreadyStored(malloc_zone)); - StoreMallocZone(malloc_zone); - EXPECT_TRUE(IsMallocZoneAlreadyStored(malloc_zone)); -} - -TEST_F(MallocZoneFunctionsTest, CannotDoubleStoreZone) { - ChromeMallocZone* malloc_zone = - reinterpret_cast(malloc_default_zone()); - StoreMallocZone(malloc_zone); - StoreMallocZone(malloc_zone); - EXPECT_EQ(1, GetMallocZoneCountForTesting()); -} - -TEST_F(MallocZoneFunctionsTest, CannotStoreMoreThanMaxZones) { - std::vector zones; - zones.resize(kMaxZoneCount * 2); - for (int i = 0; i < kMaxZoneCount * 2; ++i) { - ChromeMallocZone& zone = zones[i]; - memcpy(&zone, malloc_default_zone(), sizeof(ChromeMallocZone)); - StoreMallocZone(&zone); - } - - int max_zone_count = kMaxZoneCount; - EXPECT_EQ(max_zone_count, GetMallocZoneCountForTesting()); -} - -} // namespace allocator -} // namespace base diff --git a/allocator/partition_allocator/OWNERS b/allocator/partition_allocator/OWNERS deleted file mode 100644 index b0a2a850f..000000000 --- a/allocator/partition_allocator/OWNERS +++ /dev/null @@ -1,8 +0,0 @@ -ajwong@chromium.org -haraken@chromium.org -palmer@chromium.org -tsepez@chromium.org - -# TEAM: platform-architecture-dev@chromium.org -# Also: security-dev@chromium.org -# COMPONENT: Blink>MemoryAllocator>Partition diff --git a/allocator/partition_allocator/PartitionAlloc.md b/allocator/partition_allocator/PartitionAlloc.md deleted file mode 100644 index 982d91f27..000000000 --- a/allocator/partition_allocator/PartitionAlloc.md +++ /dev/null @@ -1,102 +0,0 @@ -# PartitionAlloc Design - -This document describes PartitionAlloc at a high level. For documentation about -its implementation, see the comments in `partition_alloc.h`. - -[TOC] - -## Overview - -PartitionAlloc is a memory allocator optimized for security, low allocation -latency (when called appropriately), and good space efficiency (when called -appropriately). This document aims to help you understand how PartitionAlloc -works so that you can use it effectively. - -## Partitions And Buckets - -A *partition* is a heap that contains certain object types, objects of certain -sizes, or objects of a certain lifetime (as the caller prefers). Callers can -create as many partitions as they need. Each partition is separate and protected -from any other partitions. - -Each partition holds multiple buckets. A *bucket* is a region in a partition -that contains similar-sized objects. - -PartitionAlloc aligns each object allocation with the closest bucket size. For -example, if a partition has 3 buckets for 64 bytes, 256 bytes, and 1024 bytes, -then PartitionAlloc will satisfy an allocation request for 128 bytes by rounding -it up to 256 bytes and allocating from the second bucket. - -The special allocator class `template class -SizeSpecificPartitionAllocator` will satisfy allocations only of size -`kMaxAllocation = N - kAllocationGranularity` or less, and contains buckets for -all `n * kAllocationGranularity` (n = 1, 2, ..., `kMaxAllocation`). Attempts to -allocate more than `kMaxAllocation` will fail. - -## Performance - -The current implementation is optimized for the main thread use-case. For -example, PartitionAlloc doesn't have threaded caches. - -PartitionAlloc is designed to be extremely fast in its fast paths. The fast -paths of allocation and deallocation require just 2 (reasonably predictable) -branches. The number of operations in the fast paths is minimal, leading to the -possibility of inlining. - -For an example of how to use partitions to get good performance and good safety, -see Blink's usage, as described in `wtf/allocator/Allocator.md`. - -Large allocations (> kGenericMaxBucketed == 960KB) are realized by direct -memory mmapping. This size makes sense because 960KB = 0xF0000. The next larger -bucket size is 1MB = 0x100000 which is greater than 1/2 the available space in -a SuperPage meaning it would not be possible to pack even 2 sequential -alloctions in a SuperPage. - -`PartitionRootGeneric::Alloc()` acquires a lock for thread safety. (The current -implementation uses a spin lock on the assumption that thread contention will be -rare in its callers. The original caller was Blink, where this is generally -true. Spin locks also have the benefit of simplicity.) - -Callers can get thread-unsafe performance using a -`SizeSpecificPartitionAllocator` or otherwise using `PartitionAlloc` (instead of -`PartitionRootGeneric::Alloc()`). Callers can also arrange for low contention, -such as by using a dedicated partition for single-threaded, latency-critical -allocations. - -Because PartitionAlloc guarantees that address space regions used for one -partition are never reused for other partitions, partitions can eat a large -amount of virtual address space (even if not of actual memory). - -Mixing various random objects in the same partition will generally lead to lower -efficiency. For good performance, group similar objects into the same partition. - -## Security - -Security is one of the most important goals of PartitionAlloc. - -PartitionAlloc guarantees that different partitions exist in different regions -of the process' address space. When the caller has freed all objects contained -in a page in a partition, PartitionAlloc returns the physical memory to the -operating system, but continues to reserve the region of address space. -PartitionAlloc will only reuse an address space region for the same partition. - -PartitionAlloc also guarantees that: - -* Linear overflows cannot corrupt into the partition. (There is a guard page at -the beginning of each partition.) - -* Linear overflows cannot corrupt out of the partition. (There is a guard page -at the end of each partition.) - -* Linear overflow or underflow cannot corrupt the allocation metadata. -PartitionAlloc records metadata in a dedicated region out-of-line (not adjacent -to objects). - -* Objects of different sizes will likely be allocated in different buckets, and -hence at different addresses. One page can contain only similar-sized objects. - -* Dereference of a freelist pointer should fault. - -* Partial pointer overwrite of freelist pointer should fault. - -* Large allocations have guard pages at the beginning and end. diff --git a/allocator/partition_allocator/address_space_randomization.cc b/allocator/partition_allocator/address_space_randomization.cc deleted file mode 100644 index a7e17c7c6..000000000 --- a/allocator/partition_allocator/address_space_randomization.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/address_space_randomization.h" - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/spin_lock.h" -#include "base/lazy_instance.h" -#include "base/rand_util.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include // Must be in front of other Windows header files. - -#include -#endif - -namespace base { - -namespace { - -// This is the same PRNG as used by tcmalloc for mapping address randomness; -// see http://burtleburtle.net/bob/rand/smallprng.html -struct ranctx { - subtle::SpinLock lock; - bool initialized; - uint32_t a; - uint32_t b; - uint32_t c; - uint32_t d; -}; - -static LazyInstance::Leaky s_ranctx = LAZY_INSTANCE_INITIALIZER; - -#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) - -uint32_t ranvalInternal(ranctx* x) { - uint32_t e = x->a - rot(x->b, 27); - x->a = x->b ^ rot(x->c, 17); - x->b = x->c + x->d; - x->c = x->d + e; - x->d = e + x->a; - return x->d; -} - -#undef rot - -uint32_t ranval(ranctx* x) { - subtle::SpinLock::Guard guard(x->lock); - if (UNLIKELY(!x->initialized)) { - const uint64_t r1 = RandUint64(); - const uint64_t r2 = RandUint64(); - - x->a = static_cast(r1); - x->b = static_cast(r1 >> 32); - x->c = static_cast(r2); - x->d = static_cast(r2 >> 32); - - x->initialized = true; - } - - return ranvalInternal(x); -} - -} // namespace - -void SetRandomPageBaseSeed(int64_t seed) { - ranctx* x = s_ranctx.Pointer(); - subtle::SpinLock::Guard guard(x->lock); - // Set RNG to initial state. - x->initialized = true; - x->a = x->b = static_cast(seed); - x->c = x->d = static_cast(seed >> 32); -} - -void* GetRandomPageBase() { - uintptr_t random = static_cast(ranval(s_ranctx.Pointer())); - -#if defined(ARCH_CPU_64_BITS) - random <<= 32ULL; - random |= static_cast(ranval(s_ranctx.Pointer())); - -// The kASLRMask and kASLROffset constants will be suitable for the -// OS and build configuration. -#if defined(OS_WIN) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - // Windows >= 8.1 has the full 47 bits. Use them where available. - static bool windows_81 = false; - static bool windows_81_initialized = false; - if (!windows_81_initialized) { - windows_81 = IsWindows8Point1OrGreater(); - windows_81_initialized = true; - } - if (!windows_81) { - random &= internal::kASLRMaskBefore8_10; - } else { - random &= internal::kASLRMask; - } - random += internal::kASLROffset; -#else - random &= internal::kASLRMask; - random += internal::kASLROffset; -#endif // defined(OS_WIN) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) -#else // defined(ARCH_CPU_32_BITS) -#if defined(OS_WIN) - // On win32 host systems the randomization plus huge alignment causes - // excessive fragmentation. Plus most of these systems lack ASLR, so the - // randomization isn't buying anything. In that case we just skip it. - // TODO(jschuh): Just dump the randomization when HE-ASLR is present. - static BOOL is_wow64 = -1; - if (is_wow64 == -1 && !IsWow64Process(GetCurrentProcess(), &is_wow64)) - is_wow64 = FALSE; - if (!is_wow64) - return nullptr; -#endif // defined(OS_WIN) - random &= internal::kASLRMask; - random += internal::kASLROffset; -#endif // defined(ARCH_CPU_32_BITS) - - DCHECK_EQ(0ULL, (random & kPageAllocationGranularityOffsetMask)); - return reinterpret_cast(random); -} - -} // namespace base diff --git a/allocator/partition_allocator/address_space_randomization.h b/allocator/partition_allocator/address_space_randomization.h deleted file mode 100644 index 3f65a87c8..000000000 --- a/allocator/partition_allocator/address_space_randomization.h +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/base_export.h" -#include "build/build_config.h" - -namespace base { - -// Sets the seed for the random number generator used by GetRandomPageBase in -// order to generate a predictable sequence of addresses. May be called multiple -// times. -BASE_EXPORT void SetRandomPageBaseSeed(int64_t seed); - -// Calculates a random preferred mapping address. In calculating an address, we -// balance good ASLR against not fragmenting the address space too badly. -BASE_EXPORT void* GetRandomPageBase(); - -namespace internal { - -constexpr uintptr_t AslrAddress(uintptr_t mask) { - return mask & kPageAllocationGranularityBaseMask; -} -constexpr uintptr_t AslrMask(uintptr_t bits) { - return AslrAddress((1ULL << bits) - 1ULL); -} - -// Turn off formatting, because the thicket of nested ifdefs below is -// incomprehensible without indentation. It is also incomprehensible with -// indentation, but the only other option is a combinatorial explosion of -// *_{win,linux,mac,foo}_{32,64}.h files. -// -// clang-format off - -#if defined(ARCH_CPU_64_BITS) - - #if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - - // We shouldn't allocate system pages at all for sanitizer builds. However, - // we do, and if random hint addresses interfere with address ranges - // hard-coded in those tools, bad things happen. This address range is - // copied from TSAN source but works with all tools. See - // https://crbug.com/539863. - constexpr uintptr_t kASLRMask = AslrAddress(0x007fffffffffULL); - constexpr uintptr_t kASLROffset = AslrAddress(0x7e8000000000ULL); - - #elif defined(OS_WIN) - - // Windows 8.10 and newer support the full 48 bit address range. Older - // versions of Windows only support 44 bits. Since kASLROffset is non-zero - // and may cause a carry, use 47 and 43 bit masks. See - // http://www.alex-ionescu.com/?p=246 - constexpr uintptr_t kASLRMask = AslrMask(47); - constexpr uintptr_t kASLRMaskBefore8_10 = AslrMask(43); - // Try not to map pages into the range where Windows loads DLLs by default. - constexpr uintptr_t kASLROffset = 0x80000000ULL; - - #elif defined(OS_MACOSX) - - // macOS as of 10.12.5 does not clean up entries in page map levels 3/4 - // [PDP/PML4] created from mmap or mach_vm_allocate, even after the region - // is destroyed. Using a virtual address space that is too large causes a - // leak of about 1 wired [can never be paged out] page per call to mmap. The - // page is only reclaimed when the process is killed. Confine the hint to a - // 39-bit section of the virtual address space. - // - // This implementation adapted from - // https://chromium-review.googlesource.com/c/v8/v8/+/557958. The difference - // is that here we clamp to 39 bits, not 32. - // - // TODO(crbug.com/738925): Remove this limitation if/when the macOS behavior - // changes. - constexpr uintptr_t kASLRMask = AslrMask(38); - constexpr uintptr_t kASLROffset = AslrAddress(0x1000000000ULL); - - #elif defined(OS_POSIX) || defined(OS_FUCHSIA) - - #if defined(ARCH_CPU_X86_64) - - // Linux (and macOS) support the full 47-bit user space of x64 processors. - // Use only 46 to allow the kernel a chance to fulfill the request. - constexpr uintptr_t kASLRMask = AslrMask(46); - constexpr uintptr_t kASLROffset = AslrAddress(0); - - #elif defined(ARCH_CPU_ARM64) - - #if defined(OS_ANDROID) - - // Restrict the address range on Android to avoid a large performance - // regression in single-process WebViews. See https://crbug.com/837640. - constexpr uintptr_t kASLRMask = AslrMask(30); - constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL); - - #else - - // ARM64 on Linux has 39-bit user space. Use 38 bits since kASLROffset - // could cause a carry. - constexpr uintptr_t kASLRMask = AslrMask(38); - constexpr uintptr_t kASLROffset = AslrAddress(0x1000000000ULL); - - #endif - - #elif defined(ARCH_CPU_PPC64) - - #if defined(OS_AIX) - - // AIX has 64 bits of virtual addressing, but we limit the address range - // to (a) minimize segment lookaside buffer (SLB) misses; and (b) use - // extra address space to isolate the mmap regions. - constexpr uintptr_t kASLRMask = AslrMask(30); - constexpr uintptr_t kASLROffset = AslrAddress(0x400000000000ULL); - - #elif defined(ARCH_CPU_BIG_ENDIAN) - - // Big-endian Linux PPC has 44 bits of virtual addressing. Use 42. - constexpr uintptr_t kASLRMask = AslrMask(42); - constexpr uintptr_t kASLROffset = AslrAddress(0); - - #else // !defined(OS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN) - - // Little-endian Linux PPC has 48 bits of virtual addressing. Use 46. - constexpr uintptr_t kASLRMask = AslrMask(46); - constexpr uintptr_t kASLROffset = AslrAddress(0); - - #endif // !defined(OS_AIX) && !defined(ARCH_CPU_BIG_ENDIAN) - - #elif defined(ARCH_CPU_S390X) - - // Linux on Z uses bits 22 - 32 for Region Indexing, which translates to - // 42 bits of virtual addressing. Truncate to 40 bits to allow kernel a - // chance to fulfill the request. - constexpr uintptr_t kASLRMask = AslrMask(40); - constexpr uintptr_t kASLROffset = AslrAddress(0); - - #elif defined(ARCH_CPU_S390) - - // 31 bits of virtual addressing. Truncate to 29 bits to allow the kernel - // a chance to fulfill the request. - constexpr uintptr_t kASLRMask = AslrMask(29); - constexpr uintptr_t kASLROffset = AslrAddress(0); - - #else // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) && - // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390) - - // For all other POSIX variants, use 30 bits. - constexpr uintptr_t kASLRMask = AslrMask(30); - - #if defined(OS_SOLARIS) - - // For our Solaris/illumos mmap hint, we pick a random address in the - // bottom half of the top half of the address space (that is, the third - // quarter). Because we do not MAP_FIXED, this will be treated only as a - // hint -- the system will not fail to mmap because something else - // happens to already be mapped at our random address. We deliberately - // set the hint high enough to get well above the system's break (that - // is, the heap); Solaris and illumos will try the hint and if that - // fails allocate as if there were no hint at all. The high hint - // prevents the break from getting hemmed in at low values, ceding half - // of the address space to the system heap. - constexpr uintptr_t kASLROffset = AslrAddress(0x80000000ULL); - - #elif defined(OS_AIX) - - // The range 0x30000000 - 0xD0000000 is available on AIX; choose the - // upper range. - constexpr uintptr_t kASLROffset = AslrAddress(0x90000000ULL); - - #else // !defined(OS_SOLARIS) && !defined(OS_AIX) - - // The range 0x20000000 - 0x60000000 is relatively unpopulated across a - // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macOS - // 10.6 and 10.7. - constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL); - - #endif // !defined(OS_SOLARIS) && !defined(OS_AIX) - - #endif // !defined(ARCH_CPU_X86_64) && !defined(ARCH_CPU_PPC64) && - // !defined(ARCH_CPU_S390X) && !defined(ARCH_CPU_S390) - - #endif // defined(OS_POSIX) - -#elif defined(ARCH_CPU_32_BITS) - - // This is a good range on 32-bit Windows and Android (the only platforms on - // which we support 32-bitness). Allocates in the 0.5 - 1.5 GiB region. There - // is no issue with carries here. - constexpr uintptr_t kASLRMask = AslrMask(30); - constexpr uintptr_t kASLROffset = AslrAddress(0x20000000ULL); - -#else - - #error Please tell us about your exotic hardware! Sounds interesting. - -#endif // defined(ARCH_CPU_32_BITS) - -// clang-format on - -} // namespace internal - -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_ADDRESS_SPACE_RANDOMIZATION diff --git a/allocator/partition_allocator/address_space_randomization_unittest.cc b/allocator/partition_allocator/address_space_randomization_unittest.cc deleted file mode 100644 index 787460706..000000000 --- a/allocator/partition_allocator/address_space_randomization_unittest.cc +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/address_space_randomization.h" - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/bit_cast.h" -#include "base/bits.h" -#include "base/sys_info.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_WIN) -#include -#include "base/win/windows_version.h" -// VersionHelpers.h must be included after windows.h. -#include -#endif - -namespace base { - -namespace { - -uintptr_t GetMask() { - uintptr_t mask = internal::kASLRMask; -#if defined(ARCH_CPU_64_BITS) -// Sanitizers use their own kASLRMask constant. -#if defined(OS_WIN) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - if (!IsWindows8Point1OrGreater()) { - mask = internal::kASLRMaskBefore8_10; - } -#endif // defined(OS_WIN) && !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)) -#elif defined(ARCH_CPU_32_BITS) -#if defined(OS_WIN) - BOOL is_wow64 = FALSE; - if (!IsWow64Process(GetCurrentProcess(), &is_wow64)) - is_wow64 = FALSE; - if (!is_wow64) { - mask = 0; - } -#endif // defined(OS_WIN) -#endif // defined(ARCH_CPU_32_BITS) - return mask; -} - -const size_t kSamples = 100; - -uintptr_t GetAddressBits() { - return reinterpret_cast(base::GetRandomPageBase()); -} - -uintptr_t GetRandomBits() { - return GetAddressBits() - internal::kASLROffset; -} - -} // namespace - -// Configurations without ASLR are tested here. -TEST(AddressSpaceRandomizationTest, DisabledASLR) { - uintptr_t mask = GetMask(); - if (!mask) { -#if defined(OS_WIN) && defined(ARCH_CPU_32_BITS) - // ASLR should be turned off on 32-bit Windows. - EXPECT_EQ(nullptr, base::GetRandomPageBase()); -#else - // Otherwise, nullptr is very unexpected. - EXPECT_NE(nullptr, base::GetRandomPageBase()); -#endif - } -} - -TEST(AddressSpaceRandomizationTest, Alignment) { - uintptr_t mask = GetMask(); - if (!mask) - return; - - for (size_t i = 0; i < kSamples; ++i) { - uintptr_t address = GetAddressBits(); - EXPECT_EQ(0ULL, (address & kPageAllocationGranularityOffsetMask)); - } -} - -TEST(AddressSpaceRandomizationTest, Range) { - uintptr_t mask = GetMask(); - if (!mask) - return; - - uintptr_t min = internal::kASLROffset; - uintptr_t max = internal::kASLROffset + internal::kASLRMask; - for (size_t i = 0; i < kSamples; ++i) { - uintptr_t address = GetAddressBits(); - EXPECT_LE(min, address); - EXPECT_GE(max + mask, address); - } -} - -TEST(AddressSpaceRandomizationTest, Predictable) { - uintptr_t mask = GetMask(); - if (!mask) - return; - - const uintptr_t kInitialSeed = 0xfeed5eedULL; - base::SetRandomPageBaseSeed(kInitialSeed); - - std::vector sequence; - for (size_t i = 0; i < kSamples; ++i) { - uintptr_t address = reinterpret_cast(base::GetRandomPageBase()); - sequence.push_back(address); - } - - base::SetRandomPageBaseSeed(kInitialSeed); - - for (size_t i = 0; i < kSamples; ++i) { - uintptr_t address = reinterpret_cast(base::GetRandomPageBase()); - EXPECT_EQ(address, sequence[i]); - } -} - -// This randomness test is adapted from V8's PRNG tests. - -// Chi squared for getting m 0s out of n bits. -double ChiSquared(int m, int n) { - double ys_minus_np1 = (m - n / 2.0); - double chi_squared_1 = ys_minus_np1 * ys_minus_np1 * 2.0 / n; - double ys_minus_np2 = ((n - m) - n / 2.0); - double chi_squared_2 = ys_minus_np2 * ys_minus_np2 * 2.0 / n; - return chi_squared_1 + chi_squared_2; -} - -// Test for correlations between recent bits from the PRNG, or bits that are -// biased. -void RandomBitCorrelation(int random_bit) { - uintptr_t mask = GetMask(); - if ((mask & (1ULL << random_bit)) == 0) - return; // bit is always 0. - -#ifdef DEBUG - constexpr int kHistory = 2; - constexpr int kRepeats = 1000; -#else - constexpr int kHistory = 8; - constexpr int kRepeats = 10000; -#endif - constexpr int kPointerBits = 8 * sizeof(void*); - uintptr_t history[kHistory]; - // The predictor bit is either constant 0 or 1, or one of the bits from the - // history. - for (int predictor_bit = -2; predictor_bit < kPointerBits; predictor_bit++) { - // The predicted bit is one of the bits from the PRNG. - for (int ago = 0; ago < kHistory; ago++) { - // We don't want to check whether each bit predicts itself. - if (ago == 0 && predictor_bit == random_bit) - continue; - - // Enter the new random value into the history. - for (int i = ago; i >= 0; i--) { - history[i] = GetRandomBits(); - } - - // Find out how many of the bits are the same as the prediction bit. - int m = 0; - for (int i = 0; i < kRepeats; i++) { - uintptr_t random = GetRandomBits(); - for (int j = ago - 1; j >= 0; j--) - history[j + 1] = history[j]; - history[0] = random; - - int predicted; - if (predictor_bit >= 0) { - predicted = (history[ago] >> predictor_bit) & 1; - } else { - predicted = predictor_bit == -2 ? 0 : 1; - } - int bit = (random >> random_bit) & 1; - if (bit == predicted) - m++; - } - - // Chi squared analysis for k = 2 (2, states: same/not-same) and one - // degree of freedom (k - 1). - double chi_squared = ChiSquared(m, kRepeats); - // For 1 degree of freedom this corresponds to 1 in a million. We are - // running ~8000 tests, so that would be surprising. - CHECK_GE(24, chi_squared); - // If the predictor bit is a fixed 0 or 1 then it makes no sense to - // repeat the test with a different age. - if (predictor_bit < 0) - break; - } - } -} - -// TODO(crbug.com/811881): These are flaky on Fuchsia -#if !defined(OS_FUCHSIA) - -// Tests are fairly slow, so give each random bit its own test. -#define TEST_RANDOM_BIT(BIT) \ - TEST(AddressSpaceRandomizationTest, RandomBitCorrelations##BIT) { \ - RandomBitCorrelation(BIT); \ - } - -// The first 12 bits on all platforms are always 0. -TEST_RANDOM_BIT(12) -TEST_RANDOM_BIT(13) -TEST_RANDOM_BIT(14) -TEST_RANDOM_BIT(15) -TEST_RANDOM_BIT(16) -TEST_RANDOM_BIT(17) -TEST_RANDOM_BIT(18) -TEST_RANDOM_BIT(19) -TEST_RANDOM_BIT(20) -TEST_RANDOM_BIT(21) -TEST_RANDOM_BIT(22) -TEST_RANDOM_BIT(23) -TEST_RANDOM_BIT(24) -TEST_RANDOM_BIT(25) -TEST_RANDOM_BIT(26) -TEST_RANDOM_BIT(27) -TEST_RANDOM_BIT(28) -TEST_RANDOM_BIT(29) -TEST_RANDOM_BIT(30) -TEST_RANDOM_BIT(31) -#if defined(ARCH_CPU_64_BITS) -TEST_RANDOM_BIT(32) -TEST_RANDOM_BIT(33) -TEST_RANDOM_BIT(34) -TEST_RANDOM_BIT(35) -TEST_RANDOM_BIT(36) -TEST_RANDOM_BIT(37) -TEST_RANDOM_BIT(38) -TEST_RANDOM_BIT(39) -TEST_RANDOM_BIT(40) -TEST_RANDOM_BIT(41) -TEST_RANDOM_BIT(42) -TEST_RANDOM_BIT(43) -TEST_RANDOM_BIT(44) -TEST_RANDOM_BIT(45) -TEST_RANDOM_BIT(46) -TEST_RANDOM_BIT(47) -TEST_RANDOM_BIT(48) -// No platforms have more than 48 address bits. -#endif // defined(ARCH_CPU_64_BITS) - -#endif // defined(OS_FUCHSIA) - -#undef TEST_RANDOM_BIT - -} // namespace base diff --git a/allocator/partition_allocator/oom.h b/allocator/partition_allocator/oom.h deleted file mode 100644 index e2d197c1e..000000000 --- a/allocator/partition_allocator/oom.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_OOM_H -#define BASE_ALLOCATOR_OOM_H - -#include "base/logging.h" - -#if defined(OS_WIN) -#include -#endif - -// Do not want trivial entry points just calling OOM_CRASH() to be -// commoned up by linker icf/comdat folding. -#define OOM_CRASH_PREVENT_ICF() \ - volatile int oom_crash_inhibit_icf = __LINE__; \ - ALLOW_UNUSED_LOCAL(oom_crash_inhibit_icf) - -// OOM_CRASH() - Specialization of IMMEDIATE_CRASH which will raise a custom -// exception on Windows to signal this is OOM and not a normal assert. -#if defined(OS_WIN) -#define OOM_CRASH() \ - do { \ - OOM_CRASH_PREVENT_ICF(); \ - ::RaiseException(0xE0000008, EXCEPTION_NONCONTINUABLE, 0, nullptr); \ - IMMEDIATE_CRASH(); \ - } while (0) -#else -#define OOM_CRASH() \ - do { \ - OOM_CRASH_PREVENT_ICF(); \ - IMMEDIATE_CRASH(); \ - } while (0) -#endif - -#endif // BASE_ALLOCATOR_OOM_H diff --git a/allocator/partition_allocator/page_allocator.cc b/allocator/partition_allocator/page_allocator.cc deleted file mode 100644 index 328384e90..000000000 --- a/allocator/partition_allocator/page_allocator.cc +++ /dev/null @@ -1,258 +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/allocator/partition_allocator/page_allocator.h" - -#include - -#include "base/allocator/partition_allocator/address_space_randomization.h" -#include "base/allocator/partition_allocator/page_allocator_internal.h" -#include "base/allocator/partition_allocator/spin_lock.h" -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/numerics/checked_math.h" -#include "build/build_config.h" - -#include - -#if defined(OS_WIN) -#include -#endif - -#if defined(OS_WIN) -#include "base/allocator/partition_allocator/page_allocator_internals_win.h" -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#include "base/allocator/partition_allocator/page_allocator_internals_posix.h" -#else -#error Platform not supported. -#endif - -namespace base { - -namespace { - -// We may reserve/release address space on different threads. -LazyInstance::Leaky s_reserveLock = LAZY_INSTANCE_INITIALIZER; - -// We only support a single block of reserved address space. -void* s_reservation_address = nullptr; -size_t s_reservation_size = 0; - -void* AllocPagesIncludingReserved(void* address, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - bool commit) { - void* ret = - SystemAllocPages(address, length, accessibility, page_tag, commit); - if (ret == nullptr) { - const bool cant_alloc_length = kHintIsAdvisory || address == nullptr; - if (cant_alloc_length) { - // The system cannot allocate |length| bytes. Release any reserved address - // space and try once more. - ReleaseReservation(); - ret = SystemAllocPages(address, length, accessibility, page_tag, commit); - } - } - return ret; -} - -// Trims |base| to given |trim_length| and |alignment|. -// -// On failure, on Windows, this function returns nullptr and frees |base|. -void* TrimMapping(void* base, - size_t base_length, - size_t trim_length, - uintptr_t alignment, - PageAccessibilityConfiguration accessibility, - bool commit) { - size_t pre_slack = reinterpret_cast(base) & (alignment - 1); - if (pre_slack) { - pre_slack = alignment - pre_slack; - } - size_t post_slack = base_length - pre_slack - trim_length; - DCHECK(base_length >= trim_length || pre_slack || post_slack); - DCHECK(pre_slack < base_length); - DCHECK(post_slack < base_length); - return TrimMappingInternal(base, base_length, trim_length, accessibility, - commit, pre_slack, post_slack); -} - -} // namespace - -void* SystemAllocPages(void* hint, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - bool commit) { - DCHECK(!(length & kPageAllocationGranularityOffsetMask)); - DCHECK(!(reinterpret_cast(hint) & - kPageAllocationGranularityOffsetMask)); - DCHECK(commit || accessibility == PageInaccessible); - return SystemAllocPagesInternal(hint, length, accessibility, page_tag, - commit); -} - -void* AllocPages(void* address, - size_t length, - size_t align, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - bool commit) { - DCHECK(length >= kPageAllocationGranularity); - DCHECK(!(length & kPageAllocationGranularityOffsetMask)); - DCHECK(align >= kPageAllocationGranularity); - // Alignment must be power of 2 for masking math to work. - DCHECK_EQ(align & (align - 1), 0UL); - DCHECK(!(reinterpret_cast(address) & - kPageAllocationGranularityOffsetMask)); - uintptr_t align_offset_mask = align - 1; - uintptr_t align_base_mask = ~align_offset_mask; - DCHECK(!(reinterpret_cast(address) & align_offset_mask)); - -#if defined(OS_LINUX) && defined(ARCH_CPU_64_BITS) - // On 64 bit Linux, we may need to adjust the address space limit for - // guarded allocations. - if (length >= kMinimumGuardedMemorySize) { - CHECK_EQ(PageInaccessible, accessibility); - CHECK(!commit); - if (!AdjustAddressSpaceLimit(base::checked_cast(length))) { - DLOG(WARNING) << "Could not adjust address space by " << length; - // Fall through. Try the allocation, since we may have a reserve. - } - } -#endif - - // If the client passed null as the address, choose a good one. - if (address == nullptr) { - address = GetRandomPageBase(); - address = reinterpret_cast(reinterpret_cast(address) & - align_base_mask); - } - - // First try to force an exact-size, aligned allocation from our random base. -#if defined(ARCH_CPU_32_BITS) - // On 32 bit systems, first try one random aligned address, and then try an - // aligned address derived from the value of |ret|. - constexpr int kExactSizeTries = 2; -#else - // On 64 bit systems, try 3 random aligned addresses. - constexpr int kExactSizeTries = 3; -#endif - - for (int i = 0; i < kExactSizeTries; ++i) { - void* ret = AllocPagesIncludingReserved(address, length, accessibility, - page_tag, commit); - if (ret != nullptr) { - // If the alignment is to our liking, we're done. - if (!(reinterpret_cast(ret) & align_offset_mask)) - return ret; - // Free the memory and try again. - FreePages(ret, length); - } else { - // |ret| is null; if this try was unhinted, we're OOM. - if (kHintIsAdvisory || address == nullptr) - return nullptr; - } - -#if defined(ARCH_CPU_32_BITS) - // For small address spaces, try the first aligned address >= |ret|. Note - // |ret| may be null, in which case |address| becomes null. - address = reinterpret_cast( - (reinterpret_cast(ret) + align_offset_mask) & - align_base_mask); -#else // defined(ARCH_CPU_64_BITS) - // Keep trying random addresses on systems that have a large address space. - address = GetRandomPageBase(); - address = reinterpret_cast(reinterpret_cast(address) & - align_base_mask); -#endif - } - - // Make a larger allocation so we can force alignment. - size_t try_length = length + (align - kPageAllocationGranularity); - CHECK(try_length >= length); - void* ret; - - do { - // Continue randomizing only on POSIX. - address = kHintIsAdvisory ? GetRandomPageBase() : nullptr; - ret = AllocPagesIncludingReserved(address, try_length, accessibility, - page_tag, commit); - // The retries are for Windows, where a race can steal our mapping on - // resize. - } while (ret != nullptr && - (ret = TrimMapping(ret, try_length, length, align, accessibility, - commit)) == nullptr); - - return ret; -} - -void FreePages(void* address, size_t length) { - DCHECK(!(reinterpret_cast(address) & - kPageAllocationGranularityOffsetMask)); - DCHECK(!(length & kPageAllocationGranularityOffsetMask)); - FreePagesInternal(address, length); -} - -bool SetSystemPagesAccess(void* address, - size_t length, - PageAccessibilityConfiguration accessibility) { - DCHECK(!(length & kSystemPageOffsetMask)); - return SetSystemPagesAccessInternal(address, length, accessibility); -} - -void DecommitSystemPages(void* address, size_t length) { - DCHECK_EQ(0UL, length & kSystemPageOffsetMask); - DecommitSystemPagesInternal(address, length); -} - -bool RecommitSystemPages(void* address, - size_t length, - PageAccessibilityConfiguration accessibility) { - DCHECK_EQ(0UL, length & kSystemPageOffsetMask); - DCHECK_NE(PageInaccessible, accessibility); - return RecommitSystemPagesInternal(address, length, accessibility); -} - -void DiscardSystemPages(void* address, size_t length) { - DCHECK_EQ(0UL, length & kSystemPageOffsetMask); - DiscardSystemPagesInternal(address, length); -} - -bool ReserveAddressSpace(size_t size) { - // To avoid deadlock, call only SystemAllocPages. - subtle::SpinLock::Guard guard(s_reserveLock.Get()); - if (s_reservation_address == nullptr) { - void* mem = SystemAllocPages(nullptr, size, PageInaccessible, - PageTag::kChromium, false); - if (mem != nullptr) { - // We guarantee this alignment when reserving address space. - DCHECK(!(reinterpret_cast(mem) & - kPageAllocationGranularityOffsetMask)); - s_reservation_address = mem; - s_reservation_size = size; - return true; - } - } - return false; -} - -void ReleaseReservation() { - // To avoid deadlock, call only FreePages. - subtle::SpinLock::Guard guard(s_reserveLock.Get()); - if (s_reservation_address != nullptr) { - FreePages(s_reservation_address, s_reservation_size); - s_reservation_address = nullptr; - s_reservation_size = 0; - } -} - -uint32_t GetAllocPageErrorCode() { - return s_allocPageErrorCode; -} - -} // namespace base diff --git a/allocator/partition_allocator/page_allocator.h b/allocator/partition_allocator/page_allocator.h deleted file mode 100644 index 49733484a..000000000 --- a/allocator/partition_allocator/page_allocator.h +++ /dev/null @@ -1,179 +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_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H - -#include - -#include - -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "build/build_config.h" - -namespace base { - -enum PageAccessibilityConfiguration { - PageInaccessible, - PageRead, - PageReadWrite, - PageReadExecute, - // This flag is deprecated and will go away soon. - // TODO(bbudge) Remove this as soon as V8 doesn't need RWX pages. - PageReadWriteExecute, -}; - -// Mac OSX supports tagged memory regions, to help in debugging. -enum class PageTag { - kFirst = 240, // Minimum tag value. - kChromium = 254, // Chromium page, including off-heap V8 ArrayBuffers. - kV8 = 255, // V8 heap pages. - kLast = kV8 // Maximum tag value. -}; - -// Allocate one or more pages. -// -// The requested |address| is just a hint; the actual address returned may -// differ. The returned address will be aligned at least to |align| bytes. -// |length| is in bytes, and must be a multiple of |kPageAllocationGranularity|. -// |align| is in bytes, and must be a power-of-two multiple of -// |kPageAllocationGranularity|. -// -// If |address| is null, then a suitable and randomized address will be chosen -// automatically. -// -// |page_accessibility| controls the permission of the allocated pages. -// -// This call will return null if the allocation cannot be satisfied. -BASE_EXPORT void* AllocPages(void* address, - size_t length, - size_t align, - PageAccessibilityConfiguration page_accessibility, - PageTag tag = PageTag::kChromium, - bool commit = true); - -// Free one or more pages starting at |address| and continuing for |length| -// bytes. -// -// |address| and |length| must match a previous call to |AllocPages|. Therefore, -// |address| must be aligned to |kPageAllocationGranularity| bytes, and |length| -// must be a multiple of |kPageAllocationGranularity|. -BASE_EXPORT void FreePages(void* address, size_t length); - -// Mark one or more system pages, starting at |address| with the given -// |page_accessibility|. |length| must be a multiple of |kSystemPageSize| bytes. -// -// Returns true if the permission change succeeded. In most cases you must -// |CHECK| the result. -BASE_EXPORT WARN_UNUSED_RESULT bool SetSystemPagesAccess( - void* address, - size_t length, - PageAccessibilityConfiguration page_accessibility); - -// Decommit one or more system pages starting at |address| and continuing for -// |length| bytes. |length| must be a multiple of |kSystemPageSize|. -// -// Decommitted means that physical resources (RAM or swap) backing the allocated -// virtual address range are released back to the system, but the address space -// is still allocated to the process (possibly using up page table entries or -// other accounting resources). Any access to a decommitted region of memory -// is an error and will generate a fault. -// -// This operation is not atomic on all platforms. -// -// Note: "Committed memory" is a Windows Memory Subsystem concept that ensures -// processes will not fault when touching a committed memory region. There is -// no analogue in the POSIX memory API where virtual memory pages are -// best-effort allocated resources on the first touch. To create a -// platform-agnostic abstraction, this API simulates the Windows "decommit" -// state by both discarding the region (allowing the OS to avoid swap -// operations) and changing the page protections so accesses fault. -// -// TODO(ajwong): This currently does not change page protections on POSIX -// systems due to a perf regression. Tracked at http://crbug.com/766882. -BASE_EXPORT void DecommitSystemPages(void* address, size_t length); - -// Recommit one or more system pages, starting at |address| and continuing for -// |length| bytes with the given |page_accessibility|. |length| must be a -// multiple of |kSystemPageSize|. -// -// Decommitted system pages must be recommitted with their original permissions -// before they are used again. -// -// Returns true if the recommit change succeeded. In most cases you must |CHECK| -// the result. -BASE_EXPORT WARN_UNUSED_RESULT bool RecommitSystemPages( - void* address, - size_t length, - PageAccessibilityConfiguration page_accessibility); - -// Discard one or more system pages starting at |address| and continuing for -// |length| bytes. |length| must be a multiple of |kSystemPageSize|. -// -// Discarding is a hint to the system that the page is no longer required. The -// hint may: -// - Do nothing. -// - Discard the page immediately, freeing up physical pages. -// - Discard the page at some time in the future in response to memory -// pressure. -// -// Only committed pages should be discarded. Discarding a page does not decommit -// it, and it is valid to discard an already-discarded page. A read or write to -// a discarded page will not fault. -// -// Reading from a discarded page may return the original page content, or a page -// full of zeroes. -// -// Writing to a discarded page is the only guaranteed way to tell the system -// that the page is required again. Once written to, the content of the page is -// guaranteed stable once more. After being written to, the page content may be -// based on the original page content, or a page of zeroes. -BASE_EXPORT void DiscardSystemPages(void* address, size_t length); - -// Rounds up |address| to the next multiple of |kSystemPageSize|. Returns -// 0 for an |address| of 0. -constexpr ALWAYS_INLINE uintptr_t RoundUpToSystemPage(uintptr_t address) { - return (address + kSystemPageOffsetMask) & kSystemPageBaseMask; -} - -// Rounds down |address| to the previous multiple of |kSystemPageSize|. Returns -// 0 for an |address| of 0. -constexpr ALWAYS_INLINE uintptr_t RoundDownToSystemPage(uintptr_t address) { - return address & kSystemPageBaseMask; -} - -// Rounds up |address| to the next multiple of |kPageAllocationGranularity|. -// Returns 0 for an |address| of 0. -constexpr ALWAYS_INLINE uintptr_t -RoundUpToPageAllocationGranularity(uintptr_t address) { - return (address + kPageAllocationGranularityOffsetMask) & - kPageAllocationGranularityBaseMask; -} - -// Rounds down |address| to the previous multiple of -// |kPageAllocationGranularity|. Returns 0 for an |address| of 0. -constexpr ALWAYS_INLINE uintptr_t -RoundDownToPageAllocationGranularity(uintptr_t address) { - return address & kPageAllocationGranularityBaseMask; -} - -// Reserves (at least) |size| bytes of address space, aligned to -// |kPageAllocationGranularity|. This can be called early on to make it more -// likely that large allocations will succeed. Returns true if the reservation -// succeeded, false if the reservation failed or a reservation was already made. -BASE_EXPORT bool ReserveAddressSpace(size_t size); - -// Releases any reserved address space. |AllocPages| calls this automatically on -// an allocation failure. External allocators may also call this on failure. -BASE_EXPORT void ReleaseReservation(); - -// Returns |errno| (POSIX) or the result of |GetLastError| (Windows) when |mmap| -// (POSIX) or |VirtualAlloc| (Windows) fails. -BASE_EXPORT uint32_t GetAllocPageErrorCode(); - -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H diff --git a/allocator/partition_allocator/page_allocator_constants.h b/allocator/partition_allocator/page_allocator_constants.h deleted file mode 100644 index 308d09979..000000000 --- a/allocator/partition_allocator/page_allocator_constants.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H_ - -#include - -#include "build/build_config.h" - -namespace base { -#if defined(OS_WIN) -static constexpr size_t kPageAllocationGranularityShift = 16; // 64KB -#elif defined(_MIPS_ARCH_LOONGSON) -static constexpr size_t kPageAllocationGranularityShift = 14; // 16KB -#else -static constexpr size_t kPageAllocationGranularityShift = 12; // 4KB -#endif -static constexpr size_t kPageAllocationGranularity = - 1 << kPageAllocationGranularityShift; -static constexpr size_t kPageAllocationGranularityOffsetMask = - kPageAllocationGranularity - 1; -static constexpr size_t kPageAllocationGranularityBaseMask = - ~kPageAllocationGranularityOffsetMask; - -#if defined(_MIPS_ARCH_LOONGSON) -static constexpr size_t kSystemPageSize = 16384; -#else -static constexpr size_t kSystemPageSize = 4096; -#endif -static constexpr size_t kSystemPageOffsetMask = kSystemPageSize - 1; -static_assert((kSystemPageSize & (kSystemPageSize - 1)) == 0, - "kSystemPageSize must be power of 2"); -static constexpr size_t kSystemPageBaseMask = ~kSystemPageOffsetMask; - -static constexpr size_t kPageMetadataShift = 5; // 32 bytes per partition page. -static constexpr size_t kPageMetadataSize = 1 << kPageMetadataShift; - -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_CONSTANTS_H diff --git a/allocator/partition_allocator/page_allocator_internal.h b/allocator/partition_allocator/page_allocator_internal.h deleted file mode 100644 index c8c003d99..000000000 --- a/allocator/partition_allocator/page_allocator_internal.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_ - -namespace base { - -void* SystemAllocPages(void* hint, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - bool commit); - -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNAL_H_ diff --git a/allocator/partition_allocator/page_allocator_internals_posix.h b/allocator/partition_allocator/page_allocator_internals_posix.h deleted file mode 100644 index a5792666f..000000000 --- a/allocator/partition_allocator/page_allocator_internals_posix.h +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_ - -#include -#include - -#if defined(OS_MACOSX) -#include -#endif -#if defined(OS_LINUX) -#include -#endif - -#include "build/build_config.h" - -#ifndef MAP_ANONYMOUS -#define MAP_ANONYMOUS MAP_ANON -#endif - -namespace base { - -// |mmap| uses a nearby address if the hint address is blocked. -const bool kHintIsAdvisory = true; -std::atomic s_allocPageErrorCode{0}; - -int GetAccessFlags(PageAccessibilityConfiguration accessibility) { - switch (accessibility) { - case PageRead: - return PROT_READ; - case PageReadWrite: - return PROT_READ | PROT_WRITE; - case PageReadExecute: - return PROT_READ | PROT_EXEC; - case PageReadWriteExecute: - return PROT_READ | PROT_WRITE | PROT_EXEC; - default: - NOTREACHED(); - FALLTHROUGH; - case PageInaccessible: - return PROT_NONE; - } -} - -#if defined(OS_LINUX) && defined(ARCH_CPU_64_BITS) - -// Multiple guarded memory regions may exceed the process address space limit. -// This function will raise or lower the limit by |amount|. -bool AdjustAddressSpaceLimit(int64_t amount) { - struct rlimit old_rlimit; - if (getrlimit(RLIMIT_AS, &old_rlimit)) - return false; - const rlim_t new_limit = - CheckAdd(old_rlimit.rlim_cur, amount).ValueOrDefault(old_rlimit.rlim_max); - const struct rlimit new_rlimit = {std::min(new_limit, old_rlimit.rlim_max), - old_rlimit.rlim_max}; - // setrlimit will fail if limit > old_rlimit.rlim_max. - return setrlimit(RLIMIT_AS, &new_rlimit) == 0; -} - -// Current WASM guarded memory regions have 8 GiB of address space. There are -// schemes that reduce that to 4 GiB. -constexpr size_t kMinimumGuardedMemorySize = 1ULL << 32; // 4 GiB - -#endif // defined(OS_LINUX) && defined(ARCH_CPU_64_BITS) - -void* SystemAllocPagesInternal(void* hint, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - bool commit) { -#if defined(OS_MACOSX) - // Use a custom tag to make it easier to distinguish Partition Alloc regions - // in vmmap(1). Tags between 240-255 are supported. - DCHECK_LE(PageTag::kFirst, page_tag); - DCHECK_GE(PageTag::kLast, page_tag); - int fd = VM_MAKE_TAG(static_cast(page_tag)); -#else - int fd = -1; -#endif - - int access_flag = GetAccessFlags(accessibility); - void* ret = - mmap(hint, length, access_flag, MAP_ANONYMOUS | MAP_PRIVATE, fd, 0); - if (ret == MAP_FAILED) { - s_allocPageErrorCode = errno; - ret = nullptr; - } - return ret; -} - -void* TrimMappingInternal(void* base, - size_t base_length, - size_t trim_length, - PageAccessibilityConfiguration accessibility, - bool commit, - size_t pre_slack, - size_t post_slack) { - void* ret = base; - // We can resize the allocation run. Release unneeded memory before and after - // the aligned range. - if (pre_slack) { - int res = munmap(base, pre_slack); - CHECK(!res); - ret = reinterpret_cast(base) + pre_slack; - } - if (post_slack) { - int res = munmap(reinterpret_cast(ret) + trim_length, post_slack); - CHECK(!res); - } - return ret; -} - -bool SetSystemPagesAccessInternal( - void* address, - size_t length, - PageAccessibilityConfiguration accessibility) { - return 0 == mprotect(address, length, GetAccessFlags(accessibility)); -} - -void FreePagesInternal(void* address, size_t length) { - CHECK(!munmap(address, length)); - -#if defined(OS_LINUX) && defined(ARCH_CPU_64_BITS) - // Restore the address space limit. - if (length >= kMinimumGuardedMemorySize) { - CHECK(AdjustAddressSpaceLimit(-base::checked_cast(length))); - } -#endif -} - -void DecommitSystemPagesInternal(void* address, size_t length) { - // In POSIX, there is no decommit concept. Discarding is an effective way of - // implementing the Windows semantics where the OS is allowed to not swap the - // pages in the region. - // - // TODO(ajwong): Also explore setting PageInaccessible to make the protection - // semantics consistent between Windows and POSIX. This might have a perf cost - // though as both decommit and recommit would incur an extra syscall. - // http://crbug.com/766882 - DiscardSystemPages(address, length); -} - -bool RecommitSystemPagesInternal(void* address, - size_t length, - PageAccessibilityConfiguration accessibility) { -#if defined(OS_MACOSX) - // On macOS, to update accounting, we need to make another syscall. For more - // details, see https://crbug.com/823915. - madvise(address, length, MADV_FREE_REUSE); -#endif - - // On POSIX systems, the caller need simply read the memory to recommit it. - // This has the correct behavior because the API requires the permissions to - // be the same as before decommitting and all configurations can read. - return true; -} - -void DiscardSystemPagesInternal(void* address, size_t length) { -#if defined(OS_MACOSX) - int ret = madvise(address, length, MADV_FREE_REUSABLE); - if (ret) { - // MADV_FREE_REUSABLE sometimes fails, so fall back to MADV_DONTNEED. - ret = madvise(address, length, MADV_DONTNEED); - } - CHECK(0 == ret); -#else - // We have experimented with other flags, but with suboptimal results. - // - // MADV_FREE (Linux): Makes our memory measurements less predictable; - // performance benefits unclear. - // - // Therefore, we just do the simple thing: MADV_DONTNEED. - CHECK(!madvise(address, length, MADV_DONTNEED)); -#endif -} - -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_ diff --git a/allocator/partition_allocator/page_allocator_internals_win.h b/allocator/partition_allocator/page_allocator_internals_win.h deleted file mode 100644 index 1b6adb2ba..000000000 --- a/allocator/partition_allocator/page_allocator_internals_win.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_ - -#include "base/allocator/partition_allocator/page_allocator_internal.h" - -namespace base { - -// |VirtualAlloc| will fail if allocation at the hint address is blocked. -const bool kHintIsAdvisory = false; -std::atomic s_allocPageErrorCode{ERROR_SUCCESS}; - -int GetAccessFlags(PageAccessibilityConfiguration accessibility) { - switch (accessibility) { - case PageRead: - return PAGE_READONLY; - case PageReadWrite: - return PAGE_READWRITE; - case PageReadExecute: - return PAGE_EXECUTE_READ; - case PageReadWriteExecute: - return PAGE_EXECUTE_READWRITE; - default: - NOTREACHED(); - FALLTHROUGH; - case PageInaccessible: - return PAGE_NOACCESS; - } -} - -void* SystemAllocPagesInternal(void* hint, - size_t length, - PageAccessibilityConfiguration accessibility, - PageTag page_tag, - bool commit) { - DWORD access_flag = GetAccessFlags(accessibility); - const DWORD type_flags = commit ? (MEM_RESERVE | MEM_COMMIT) : MEM_RESERVE; - void* ret = VirtualAlloc(hint, length, type_flags, access_flag); - if (ret == nullptr) { - s_allocPageErrorCode = GetLastError(); - } - return ret; -} - -void* TrimMappingInternal(void* base, - size_t base_length, - size_t trim_length, - PageAccessibilityConfiguration accessibility, - bool commit, - size_t pre_slack, - size_t post_slack) { - void* ret = base; - if (pre_slack || post_slack) { - // We cannot resize the allocation run. Free it and retry at the aligned - // address within the freed range. - ret = reinterpret_cast(base) + pre_slack; - FreePages(base, base_length); - ret = SystemAllocPages(ret, trim_length, accessibility, PageTag::kChromium, - commit); - } - return ret; -} - -bool SetSystemPagesAccessInternal( - void* address, - size_t length, - PageAccessibilityConfiguration accessibility) { - if (accessibility == PageInaccessible) { - return VirtualFree(address, length, MEM_DECOMMIT) != 0; - } else { - return nullptr != VirtualAlloc(address, length, MEM_COMMIT, - GetAccessFlags(accessibility)); - } -} - -void FreePagesInternal(void* address, size_t length) { - CHECK(VirtualFree(address, 0, MEM_RELEASE)); -} - -void DecommitSystemPagesInternal(void* address, size_t length) { - CHECK(SetSystemPagesAccess(address, length, PageInaccessible)); -} - -bool RecommitSystemPagesInternal(void* address, - size_t length, - PageAccessibilityConfiguration accessibility) { - return SetSystemPagesAccess(address, length, accessibility); -} - -void DiscardSystemPagesInternal(void* address, size_t length) { - // On Windows, discarded pages are not returned to the system immediately and - // not guaranteed to be zeroed when returned to the application. - using DiscardVirtualMemoryFunction = - DWORD(WINAPI*)(PVOID virtualAddress, SIZE_T size); - static DiscardVirtualMemoryFunction discard_virtual_memory = - reinterpret_cast(-1); - if (discard_virtual_memory == - reinterpret_cast(-1)) - discard_virtual_memory = - reinterpret_cast(GetProcAddress( - GetModuleHandle(L"Kernel32.dll"), "DiscardVirtualMemory")); - // Use DiscardVirtualMemory when available because it releases faster than - // MEM_RESET. - DWORD ret = 1; - if (discard_virtual_memory) { - ret = discard_virtual_memory(address, length); - } - // DiscardVirtualMemory is buggy in Win10 SP0, so fall back to MEM_RESET on - // failure. - if (ret) { - void* ptr = VirtualAlloc(address, length, MEM_RESET, PAGE_READWRITE); - CHECK(ptr); - } -} - -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_WIN_H_ diff --git a/allocator/partition_allocator/page_allocator_unittest.cc b/allocator/partition_allocator/page_allocator_unittest.cc deleted file mode 100644 index 22c645551..000000000 --- a/allocator/partition_allocator/page_allocator_unittest.cc +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/page_allocator.h" - -#include -#include - -#include "base/allocator/partition_allocator/address_space_randomization.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_POSIX) && !defined(OS_FUCHSIA) -#include -#include -#include -#include -#endif // defined(OS_POSIX) && !defined(OS_FUCHSIA) - -#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - -namespace base { - -namespace { - -// Any number of bytes that can be allocated with no trouble. -constexpr size_t kEasyAllocSize = - (1024 * 1024) & ~(kPageAllocationGranularity - 1); - -// A huge amount of memory, greater than or equal to the ASLR space. -constexpr size_t kHugeMemoryAmount = - std::max(internal::kASLRMask, std::size_t{2} * internal::kASLRMask); - -} // namespace - -TEST(PageAllocatorTest, Rounding) { - EXPECT_EQ(0u, RoundUpToSystemPage(0u)); - EXPECT_EQ(kSystemPageSize, RoundUpToSystemPage(1)); - EXPECT_EQ(kSystemPageSize, RoundUpToSystemPage(kSystemPageSize - 1)); - EXPECT_EQ(kSystemPageSize, RoundUpToSystemPage(kSystemPageSize)); - EXPECT_EQ(2 * kSystemPageSize, RoundUpToSystemPage(kSystemPageSize + 1)); - EXPECT_EQ(0u, RoundDownToSystemPage(0u)); - EXPECT_EQ(0u, RoundDownToSystemPage(kSystemPageSize - 1)); - EXPECT_EQ(kSystemPageSize, RoundDownToSystemPage(kSystemPageSize)); - EXPECT_EQ(kSystemPageSize, RoundDownToSystemPage(kSystemPageSize + 1)); - EXPECT_EQ(kSystemPageSize, RoundDownToSystemPage(2 * kSystemPageSize - 1)); - EXPECT_EQ(0u, RoundUpToPageAllocationGranularity(0u)); - EXPECT_EQ(kPageAllocationGranularity, RoundUpToPageAllocationGranularity(1)); - EXPECT_EQ(kPageAllocationGranularity, - RoundUpToPageAllocationGranularity(kPageAllocationGranularity - 1)); - EXPECT_EQ(kPageAllocationGranularity, - RoundUpToPageAllocationGranularity(kPageAllocationGranularity)); - EXPECT_EQ(2 * kPageAllocationGranularity, - RoundUpToPageAllocationGranularity(kPageAllocationGranularity + 1)); - EXPECT_EQ(0u, RoundDownToPageAllocationGranularity(0u)); - EXPECT_EQ( - 0u, RoundDownToPageAllocationGranularity(kPageAllocationGranularity - 1)); - EXPECT_EQ(kPageAllocationGranularity, - RoundDownToPageAllocationGranularity(kPageAllocationGranularity)); - EXPECT_EQ(kPageAllocationGranularity, RoundDownToPageAllocationGranularity( - kPageAllocationGranularity + 1)); - EXPECT_EQ( - kPageAllocationGranularity, - RoundDownToPageAllocationGranularity(2 * kPageAllocationGranularity - 1)); -} - -// Test that failed page allocations invoke base::ReleaseReservation(). -// We detect this by making a reservation and ensuring that after failure, we -// can make a new reservation. -TEST(PageAllocatorTest, AllocFailure) { - // Release any reservation made by another test. - ReleaseReservation(); - - // We can make a reservation. - EXPECT_TRUE(ReserveAddressSpace(kEasyAllocSize)); - - // We can't make another reservation until we trigger an allocation failure. - EXPECT_FALSE(ReserveAddressSpace(kEasyAllocSize)); - - size_t size = kHugeMemoryAmount; - // Skip the test for sanitizers and platforms with ASLR turned off. - if (size == 0) - return; - - void* result = AllocPages(nullptr, size, kPageAllocationGranularity, - PageInaccessible, PageTag::kChromium, false); - if (result == nullptr) { - // We triggered allocation failure. Our reservation should have been - // released, and we should be able to make a new reservation. - EXPECT_TRUE(ReserveAddressSpace(kEasyAllocSize)); - ReleaseReservation(); - return; - } - // We couldn't fail. Make sure reservation is still there. - EXPECT_FALSE(ReserveAddressSpace(kEasyAllocSize)); -} - -// TODO(crbug.com/765801): Test failed on chromium.win/Win10 Tests x64. -#if defined(OS_WIN) && defined(ARCH_CPU_64_BITS) -#define MAYBE_ReserveAddressSpace DISABLED_ReserveAddressSpace -#else -#define MAYBE_ReserveAddressSpace ReserveAddressSpace -#endif // defined(OS_WIN) && defined(ARCH_CPU_64_BITS) - -// Test that reserving address space can fail. -TEST(PageAllocatorTest, MAYBE_ReserveAddressSpace) { - // Release any reservation made by another test. - ReleaseReservation(); - - size_t size = kHugeMemoryAmount; - // Skip the test for sanitizers and platforms with ASLR turned off. - if (size == 0) - return; - - bool success = ReserveAddressSpace(size); - if (!success) { - EXPECT_TRUE(ReserveAddressSpace(kEasyAllocSize)); - return; - } - // We couldn't fail. Make sure reservation is still there. - EXPECT_FALSE(ReserveAddressSpace(kEasyAllocSize)); -} - -TEST(PageAllocatorTest, AllocAndFreePages) { - void* buffer = AllocPages(nullptr, kPageAllocationGranularity, - kPageAllocationGranularity, PageReadWrite, - PageTag::kChromium, true); - EXPECT_TRUE(buffer); - int* buffer0 = reinterpret_cast(buffer); - *buffer0 = 42; - EXPECT_EQ(42, *buffer0); - FreePages(buffer, kPageAllocationGranularity); -} - -// Test permission setting on POSIX, where we can set a trap handler. -#if defined(OS_POSIX) && !defined(OS_FUCHSIA) - -namespace { -sigjmp_buf g_continuation; - -void SignalHandler(int signal, siginfo_t* info, void*) { - siglongjmp(g_continuation, 1); -} -} // namespace - -// On Mac, sometimes we get SIGBUS instead of SIGSEGV, so handle that too. -#if defined(OS_MACOSX) -#define EXTRA_FAULT_BEGIN_ACTION() \ - struct sigaction old_bus_action; \ - sigaction(SIGBUS, &action, &old_bus_action); -#define EXTRA_FAULT_END_ACTION() sigaction(SIGBUS, &old_bus_action, nullptr); -#else -#define EXTRA_FAULT_BEGIN_ACTION() -#define EXTRA_FAULT_END_ACTION() -#endif - -// Install a signal handler so we can catch the fault we're about to trigger. -#define FAULT_TEST_BEGIN() \ - struct sigaction action = {}; \ - struct sigaction old_action = {}; \ - action.sa_sigaction = SignalHandler; \ - sigemptyset(&action.sa_mask); \ - action.sa_flags = SA_SIGINFO; \ - sigaction(SIGSEGV, &action, &old_action); \ - EXTRA_FAULT_BEGIN_ACTION(); \ - int const save_sigs = 1; \ - if (!sigsetjmp(g_continuation, save_sigs)) { -// Fault generating code goes here... - -// Handle when sigsetjmp returns nonzero (we are returning from our handler). -#define FAULT_TEST_END() \ - } \ - else { \ - sigaction(SIGSEGV, &old_action, nullptr); \ - EXTRA_FAULT_END_ACTION(); \ - } - -TEST(PageAllocatorTest, InaccessiblePages) { - void* buffer = AllocPages(nullptr, kPageAllocationGranularity, - kPageAllocationGranularity, PageInaccessible, - PageTag::kChromium, true); - EXPECT_TRUE(buffer); - - FAULT_TEST_BEGIN(); - - // Reading from buffer should fault. - int* buffer0 = reinterpret_cast(buffer); - int buffer0_contents = *buffer0; - EXPECT_EQ(buffer0_contents, *buffer0); - EXPECT_TRUE(false); - - FAULT_TEST_END(); - - FreePages(buffer, kPageAllocationGranularity); -} - -TEST(PageAllocatorTest, ReadExecutePages) { - void* buffer = AllocPages(nullptr, kPageAllocationGranularity, - kPageAllocationGranularity, PageReadExecute, - PageTag::kChromium, true); - EXPECT_TRUE(buffer); - int* buffer0 = reinterpret_cast(buffer); - // Reading from buffer should succeed. - int buffer0_contents = *buffer0; - - FAULT_TEST_BEGIN(); - - // Writing to buffer should fault. - *buffer0 = ~buffer0_contents; - EXPECT_TRUE(false); - - FAULT_TEST_END(); - - // Make sure no write occurred. - EXPECT_EQ(buffer0_contents, *buffer0); - FreePages(buffer, kPageAllocationGranularity); -} - -#endif // defined(OS_POSIX) && !defined(OS_FUCHSIA) - -} // namespace base - -#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) diff --git a/allocator/partition_allocator/partition_alloc.cc b/allocator/partition_allocator/partition_alloc.cc deleted file mode 100644 index 855467390..000000000 --- a/allocator/partition_allocator/partition_alloc.cc +++ /dev/null @@ -1,727 +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/allocator/partition_allocator/partition_alloc.h" - -#include -#include - -#include "base/allocator/partition_allocator/partition_direct_map_extent.h" -#include "base/allocator/partition_allocator/partition_oom.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "base/allocator/partition_allocator/spin_lock.h" -#include "base/compiler_specific.h" -#include "base/lazy_instance.h" - -// Two partition pages are used as guard / metadata page so make sure the super -// page size is bigger. -static_assert(base::kPartitionPageSize * 4 <= base::kSuperPageSize, - "ok super page size"); -static_assert(!(base::kSuperPageSize % base::kPartitionPageSize), - "ok super page multiple"); -// Four system pages gives us room to hack out a still-guard-paged piece -// of metadata in the middle of a guard partition page. -static_assert(base::kSystemPageSize * 4 <= base::kPartitionPageSize, - "ok partition page size"); -static_assert(!(base::kPartitionPageSize % base::kSystemPageSize), - "ok partition page multiple"); -static_assert(sizeof(base::internal::PartitionPage) <= base::kPageMetadataSize, - "PartitionPage should not be too big"); -static_assert(sizeof(base::internal::PartitionBucket) <= - base::kPageMetadataSize, - "PartitionBucket should not be too big"); -static_assert(sizeof(base::internal::PartitionSuperPageExtentEntry) <= - base::kPageMetadataSize, - "PartitionSuperPageExtentEntry should not be too big"); -static_assert(base::kPageMetadataSize * base::kNumPartitionPagesPerSuperPage <= - base::kSystemPageSize, - "page metadata fits in hole"); -// Limit to prevent callers accidentally overflowing an int size. -static_assert(base::kGenericMaxDirectMapped <= - (1UL << 31) + base::kPageAllocationGranularity, - "maximum direct mapped allocation"); -// Check that some of our zanier calculations worked out as expected. -static_assert(base::kGenericSmallestBucket == 8, "generic smallest bucket"); -static_assert(base::kGenericMaxBucketed == 983040, "generic max bucketed"); -static_assert(base::kMaxSystemPagesPerSlotSpan < (1 << 8), - "System pages per slot span must be less than 128."); - -namespace base { - -internal::PartitionRootBase::PartitionRootBase() = default; -internal::PartitionRootBase::~PartitionRootBase() = default; -PartitionRoot::PartitionRoot() = default; -PartitionRoot::~PartitionRoot() = default; -PartitionRootGeneric::PartitionRootGeneric() = default; -PartitionRootGeneric::~PartitionRootGeneric() = default; -PartitionAllocatorGeneric::PartitionAllocatorGeneric() = default; -PartitionAllocatorGeneric::~PartitionAllocatorGeneric() = default; - -static LazyInstance::Leaky g_initialized_lock = - LAZY_INSTANCE_INITIALIZER; -static bool g_initialized = false; - -void (*internal::PartitionRootBase::gOomHandlingFunction)() = nullptr; -PartitionAllocHooks::AllocationHook* PartitionAllocHooks::allocation_hook_ = - nullptr; -PartitionAllocHooks::FreeHook* PartitionAllocHooks::free_hook_ = nullptr; - -static void PartitionAllocBaseInit(internal::PartitionRootBase* root) { - DCHECK(!root->initialized); - { - subtle::SpinLock::Guard guard(g_initialized_lock.Get()); - if (!g_initialized) { - g_initialized = true; - // We mark the sentinel bucket/page as free to make sure it is skipped by - // our logic to find a new active page. - internal::PartitionBucket::get_sentinel_bucket()->active_pages_head = - internal::PartitionPage::get_sentinel_page(); - } - } - - root->initialized = true; - - // This is a "magic" value so we can test if a root pointer is valid. - root->inverted_self = ~reinterpret_cast(root); -} - -void PartitionAllocGlobalInit(void (*oom_handling_function)()) { - DCHECK(oom_handling_function); - internal::PartitionRootBase::gOomHandlingFunction = oom_handling_function; -} - -void PartitionRoot::Init(size_t num_buckets, size_t max_allocation) { - PartitionAllocBaseInit(this); - - this->num_buckets = num_buckets; - this->max_allocation = max_allocation; - size_t i; - for (i = 0; i < this->num_buckets; ++i) { - internal::PartitionBucket* bucket = &this->buckets()[i]; - if (!i) - bucket->Init(kAllocationGranularity); - else - bucket->Init(i << kBucketShift); - } -} - -void PartitionRootGeneric::Init() { - subtle::SpinLock::Guard guard(this->lock); - - PartitionAllocBaseInit(this); - - // Precalculate some shift and mask constants used in the hot path. - // Example: malloc(41) == 101001 binary. - // Order is 6 (1 << 6-1) == 32 is highest bit set. - // order_index is the next three MSB == 010 == 2. - // sub_order_index_mask is a mask for the remaining bits == 11 (masking to 01 - // for - // the sub_order_index). - size_t order; - for (order = 0; order <= kBitsPerSizeT; ++order) { - size_t order_index_shift; - if (order < kGenericNumBucketsPerOrderBits + 1) - order_index_shift = 0; - else - order_index_shift = order - (kGenericNumBucketsPerOrderBits + 1); - this->order_index_shifts[order] = order_index_shift; - size_t sub_order_index_mask; - if (order == kBitsPerSizeT) { - // This avoids invoking undefined behavior for an excessive shift. - sub_order_index_mask = - static_cast(-1) >> (kGenericNumBucketsPerOrderBits + 1); - } else { - sub_order_index_mask = ((static_cast(1) << order) - 1) >> - (kGenericNumBucketsPerOrderBits + 1); - } - this->order_sub_index_masks[order] = sub_order_index_mask; - } - - // Set up the actual usable buckets first. - // Note that typical values (i.e. min allocation size of 8) will result in - // pseudo buckets (size==9 etc. or more generally, size is not a multiple - // of the smallest allocation granularity). - // We avoid them in the bucket lookup map, but we tolerate them to keep the - // code simpler and the structures more generic. - size_t i, j; - size_t current_size = kGenericSmallestBucket; - size_t currentIncrement = - kGenericSmallestBucket >> kGenericNumBucketsPerOrderBits; - internal::PartitionBucket* bucket = &this->buckets[0]; - for (i = 0; i < kGenericNumBucketedOrders; ++i) { - for (j = 0; j < kGenericNumBucketsPerOrder; ++j) { - bucket->Init(current_size); - // Disable psuedo buckets so that touching them faults. - if (current_size % kGenericSmallestBucket) - bucket->active_pages_head = nullptr; - current_size += currentIncrement; - ++bucket; - } - currentIncrement <<= 1; - } - DCHECK(current_size == 1 << kGenericMaxBucketedOrder); - DCHECK(bucket == &this->buckets[0] + kGenericNumBuckets); - - // Then set up the fast size -> bucket lookup table. - bucket = &this->buckets[0]; - internal::PartitionBucket** bucketPtr = &this->bucket_lookups[0]; - for (order = 0; order <= kBitsPerSizeT; ++order) { - for (j = 0; j < kGenericNumBucketsPerOrder; ++j) { - if (order < kGenericMinBucketedOrder) { - // Use the bucket of the finest granularity for malloc(0) etc. - *bucketPtr++ = &this->buckets[0]; - } else if (order > kGenericMaxBucketedOrder) { - *bucketPtr++ = internal::PartitionBucket::get_sentinel_bucket(); - } else { - internal::PartitionBucket* validBucket = bucket; - // Skip over invalid buckets. - while (validBucket->slot_size % kGenericSmallestBucket) - validBucket++; - *bucketPtr++ = validBucket; - bucket++; - } - } - } - DCHECK(bucket == &this->buckets[0] + kGenericNumBuckets); - DCHECK(bucketPtr == &this->bucket_lookups[0] + - ((kBitsPerSizeT + 1) * kGenericNumBucketsPerOrder)); - // And there's one last bucket lookup that will be hit for e.g. malloc(-1), - // which tries to overflow to a non-existant order. - *bucketPtr = internal::PartitionBucket::get_sentinel_bucket(); -} - -bool PartitionReallocDirectMappedInPlace(PartitionRootGeneric* root, - internal::PartitionPage* page, - size_t raw_size) { - DCHECK(page->bucket->is_direct_mapped()); - - raw_size = internal::PartitionCookieSizeAdjustAdd(raw_size); - - // Note that the new size might be a bucketed size; this function is called - // whenever we're reallocating a direct mapped allocation. - size_t new_size = internal::PartitionBucket::get_direct_map_size(raw_size); - if (new_size < kGenericMinDirectMappedDownsize) - return false; - - // bucket->slot_size is the current size of the allocation. - size_t current_size = page->bucket->slot_size; - if (new_size == current_size) - return true; - - char* char_ptr = static_cast(internal::PartitionPage::ToPointer(page)); - - if (new_size < current_size) { - size_t map_size = - internal::PartitionDirectMapExtent::FromPage(page)->map_size; - - // Don't reallocate in-place if new size is less than 80 % of the full - // map size, to avoid holding on to too much unused address space. - if ((new_size / kSystemPageSize) * 5 < (map_size / kSystemPageSize) * 4) - return false; - - // Shrink by decommitting unneeded pages and making them inaccessible. - size_t decommitSize = current_size - new_size; - root->DecommitSystemPages(char_ptr + new_size, decommitSize); - CHECK(SetSystemPagesAccess(char_ptr + new_size, decommitSize, - PageInaccessible)); - } else if (new_size <= - internal::PartitionDirectMapExtent::FromPage(page)->map_size) { - // Grow within the actually allocated memory. Just need to make the - // pages accessible again. - size_t recommit_size = new_size - current_size; - CHECK(SetSystemPagesAccess(char_ptr + current_size, recommit_size, - PageReadWrite)); - root->RecommitSystemPages(char_ptr + current_size, recommit_size); - -#if DCHECK_IS_ON() - memset(char_ptr + current_size, internal::kUninitializedByte, - recommit_size); -#endif - } else { - // We can't perform the realloc in-place. - // TODO: support this too when possible. - return false; - } - -#if DCHECK_IS_ON() - // Write a new trailing cookie. - internal::PartitionCookieWriteValue(char_ptr + raw_size - - internal::kCookieSize); -#endif - - page->set_raw_size(raw_size); - DCHECK(page->get_raw_size() == raw_size); - - page->bucket->slot_size = new_size; - return true; -} - -void* PartitionReallocGenericFlags(PartitionRootGeneric* root, - int flags, - void* ptr, - size_t new_size, - const char* type_name) { -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - void* result = realloc(ptr, new_size); - CHECK(result || flags & PartitionAllocReturnNull); - return result; -#else - if (UNLIKELY(!ptr)) - return PartitionAllocGenericFlags(root, flags, new_size, type_name); - if (UNLIKELY(!new_size)) { - root->Free(ptr); - return nullptr; - } - - if (new_size > kGenericMaxDirectMapped) { - if (flags & PartitionAllocReturnNull) - return nullptr; - else - internal::PartitionExcessiveAllocationSize(); - } - - internal::PartitionPage* page = internal::PartitionPage::FromPointer( - internal::PartitionCookieFreePointerAdjust(ptr)); - // TODO(palmer): See if we can afford to make this a CHECK. - DCHECK(root->IsValidPage(page)); - - if (UNLIKELY(page->bucket->is_direct_mapped())) { - // We may be able to perform the realloc in place by changing the - // accessibility of memory pages and, if reducing the size, decommitting - // them. - if (PartitionReallocDirectMappedInPlace(root, page, new_size)) { - PartitionAllocHooks::ReallocHookIfEnabled(ptr, ptr, new_size, type_name); - return ptr; - } - } - - size_t actual_new_size = root->ActualSize(new_size); - size_t actual_old_size = PartitionAllocGetSize(ptr); - - // TODO: note that tcmalloc will "ignore" a downsizing realloc() unless the - // new size is a significant percentage smaller. We could do the same if we - // determine it is a win. - if (actual_new_size == actual_old_size) { - // Trying to allocate a block of size new_size would give us a block of - // the same size as the one we've already got, so re-use the allocation - // after updating statistics (and cookies, if present). - page->set_raw_size(internal::PartitionCookieSizeAdjustAdd(new_size)); -#if DCHECK_IS_ON() - // Write a new trailing cookie when it is possible to keep track of - // |new_size| via the raw size pointer. - if (page->get_raw_size_ptr()) - internal::PartitionCookieWriteValue(static_cast(ptr) + new_size); -#endif - return ptr; - } - - // This realloc cannot be resized in-place. Sadness. - void* ret = PartitionAllocGenericFlags(root, flags, new_size, type_name); - if (!ret) { - if (flags & PartitionAllocReturnNull) - return nullptr; - else - internal::PartitionExcessiveAllocationSize(); - } - - size_t copy_size = actual_old_size; - if (new_size < copy_size) - copy_size = new_size; - - memcpy(ret, ptr, copy_size); - root->Free(ptr); - return ret; -#endif -} - -void* PartitionRootGeneric::Realloc(void* ptr, - size_t new_size, - const char* type_name) { - return PartitionReallocGenericFlags(this, 0, ptr, new_size, type_name); -} - -static size_t PartitionPurgePage(internal::PartitionPage* page, bool discard) { - const internal::PartitionBucket* bucket = page->bucket; - size_t slot_size = bucket->slot_size; - if (slot_size < kSystemPageSize || !page->num_allocated_slots) - return 0; - - size_t bucket_num_slots = bucket->get_slots_per_span(); - size_t discardable_bytes = 0; - - size_t raw_size = page->get_raw_size(); - if (raw_size) { - uint32_t usedBytes = static_cast(RoundUpToSystemPage(raw_size)); - discardable_bytes = bucket->slot_size - usedBytes; - if (discardable_bytes && discard) { - char* ptr = - reinterpret_cast(internal::PartitionPage::ToPointer(page)); - ptr += usedBytes; - DiscardSystemPages(ptr, discardable_bytes); - } - return discardable_bytes; - } - - constexpr size_t kMaxSlotCount = - (kPartitionPageSize * kMaxPartitionPagesPerSlotSpan) / kSystemPageSize; - DCHECK(bucket_num_slots <= kMaxSlotCount); - DCHECK(page->num_unprovisioned_slots < bucket_num_slots); - size_t num_slots = bucket_num_slots - page->num_unprovisioned_slots; - char slot_usage[kMaxSlotCount]; -#if !defined(OS_WIN) - // The last freelist entry should not be discarded when using OS_WIN. - // DiscardVirtualMemory makes the contents of discarded memory undefined. - size_t last_slot = static_cast(-1); -#endif - memset(slot_usage, 1, num_slots); - char* ptr = reinterpret_cast(internal::PartitionPage::ToPointer(page)); - // First, walk the freelist for this page and make a bitmap of which slots - // are not in use. - for (internal::PartitionFreelistEntry* entry = page->freelist_head; entry; - /**/) { - size_t slotIndex = (reinterpret_cast(entry) - ptr) / slot_size; - DCHECK(slotIndex < num_slots); - slot_usage[slotIndex] = 0; - entry = internal::PartitionFreelistEntry::Transform(entry->next); -#if !defined(OS_WIN) - // If we have a slot where the masked freelist entry is 0, we can - // actually discard that freelist entry because touching a discarded - // page is guaranteed to return original content or 0. - // (Note that this optimization won't fire on big endian machines - // because the masking function is negation.) - if (!internal::PartitionFreelistEntry::Transform(entry)) - last_slot = slotIndex; -#endif - } - - // If the slot(s) at the end of the slot span are not in used, we can - // truncate them entirely and rewrite the freelist. - size_t truncated_slots = 0; - while (!slot_usage[num_slots - 1]) { - truncated_slots++; - num_slots--; - DCHECK(num_slots); - } - // First, do the work of calculating the discardable bytes. Don't actually - // discard anything unless the discard flag was passed in. - if (truncated_slots) { - size_t unprovisioned_bytes = 0; - char* begin_ptr = ptr + (num_slots * slot_size); - char* end_ptr = begin_ptr + (slot_size * truncated_slots); - begin_ptr = reinterpret_cast( - RoundUpToSystemPage(reinterpret_cast(begin_ptr))); - // We round the end pointer here up and not down because we're at the - // end of a slot span, so we "own" all the way up the page boundary. - end_ptr = reinterpret_cast( - RoundUpToSystemPage(reinterpret_cast(end_ptr))); - DCHECK(end_ptr <= ptr + bucket->get_bytes_per_span()); - if (begin_ptr < end_ptr) { - unprovisioned_bytes = end_ptr - begin_ptr; - discardable_bytes += unprovisioned_bytes; - } - if (unprovisioned_bytes && discard) { - DCHECK(truncated_slots > 0); - size_t num_new_entries = 0; - page->num_unprovisioned_slots += static_cast(truncated_slots); - // Rewrite the freelist. - internal::PartitionFreelistEntry** entry_ptr = &page->freelist_head; - for (size_t slotIndex = 0; slotIndex < num_slots; ++slotIndex) { - if (slot_usage[slotIndex]) - continue; - auto* entry = reinterpret_cast( - ptr + (slot_size * slotIndex)); - *entry_ptr = internal::PartitionFreelistEntry::Transform(entry); - entry_ptr = reinterpret_cast(entry); - num_new_entries++; -#if !defined(OS_WIN) - last_slot = slotIndex; -#endif - } - // Terminate the freelist chain. - *entry_ptr = nullptr; - // The freelist head is stored unmasked. - page->freelist_head = - internal::PartitionFreelistEntry::Transform(page->freelist_head); - DCHECK(num_new_entries == num_slots - page->num_allocated_slots); - // Discard the memory. - DiscardSystemPages(begin_ptr, unprovisioned_bytes); - } - } - - // Next, walk the slots and for any not in use, consider where the system - // page boundaries occur. We can release any system pages back to the - // system as long as we don't interfere with a freelist pointer or an - // adjacent slot. - for (size_t i = 0; i < num_slots; ++i) { - if (slot_usage[i]) - continue; - // The first address we can safely discard is just after the freelist - // pointer. There's one quirk: if the freelist pointer is actually a - // null, we can discard that pointer value too. - char* begin_ptr = ptr + (i * slot_size); - char* end_ptr = begin_ptr + slot_size; -#if !defined(OS_WIN) - if (i != last_slot) - begin_ptr += sizeof(internal::PartitionFreelistEntry); -#else - begin_ptr += sizeof(internal::PartitionFreelistEntry); -#endif - begin_ptr = reinterpret_cast( - RoundUpToSystemPage(reinterpret_cast(begin_ptr))); - end_ptr = reinterpret_cast( - RoundDownToSystemPage(reinterpret_cast(end_ptr))); - if (begin_ptr < end_ptr) { - size_t partial_slot_bytes = end_ptr - begin_ptr; - discardable_bytes += partial_slot_bytes; - if (discard) - DiscardSystemPages(begin_ptr, partial_slot_bytes); - } - } - return discardable_bytes; -} - -static void PartitionPurgeBucket(internal::PartitionBucket* bucket) { - if (bucket->active_pages_head != - internal::PartitionPage::get_sentinel_page()) { - for (internal::PartitionPage* page = bucket->active_pages_head; page; - page = page->next_page) { - DCHECK(page != internal::PartitionPage::get_sentinel_page()); - PartitionPurgePage(page, true); - } - } -} - -void PartitionRoot::PurgeMemory(int flags) { - if (flags & PartitionPurgeDecommitEmptyPages) - DecommitEmptyPages(); - // We don't currently do anything for PartitionPurgeDiscardUnusedSystemPages - // here because that flag is only useful for allocations >= system page - // size. We only have allocations that large inside generic partitions - // at the moment. -} - -void PartitionRootGeneric::PurgeMemory(int flags) { - subtle::SpinLock::Guard guard(this->lock); - if (flags & PartitionPurgeDecommitEmptyPages) - DecommitEmptyPages(); - if (flags & PartitionPurgeDiscardUnusedSystemPages) { - for (size_t i = 0; i < kGenericNumBuckets; ++i) { - internal::PartitionBucket* bucket = &this->buckets[i]; - if (bucket->slot_size >= kSystemPageSize) - PartitionPurgeBucket(bucket); - } - } -} - -static void PartitionDumpPageStats(PartitionBucketMemoryStats* stats_out, - internal::PartitionPage* page) { - uint16_t bucket_num_slots = page->bucket->get_slots_per_span(); - - if (page->is_decommitted()) { - ++stats_out->num_decommitted_pages; - return; - } - - stats_out->discardable_bytes += PartitionPurgePage(page, false); - - size_t raw_size = page->get_raw_size(); - if (raw_size) { - stats_out->active_bytes += static_cast(raw_size); - } else { - stats_out->active_bytes += - (page->num_allocated_slots * stats_out->bucket_slot_size); - } - - size_t page_bytes_resident = - RoundUpToSystemPage((bucket_num_slots - page->num_unprovisioned_slots) * - stats_out->bucket_slot_size); - stats_out->resident_bytes += page_bytes_resident; - if (page->is_empty()) { - stats_out->decommittable_bytes += page_bytes_resident; - ++stats_out->num_empty_pages; - } else if (page->is_full()) { - ++stats_out->num_full_pages; - } else { - DCHECK(page->is_active()); - ++stats_out->num_active_pages; - } -} - -static void PartitionDumpBucketStats(PartitionBucketMemoryStats* stats_out, - const internal::PartitionBucket* bucket) { - DCHECK(!bucket->is_direct_mapped()); - stats_out->is_valid = false; - // If the active page list is empty (== - // internal::PartitionPage::get_sentinel_page()), - // the bucket might still need to be reported if it has a list of empty, - // decommitted or full pages. - if (bucket->active_pages_head == - internal::PartitionPage::get_sentinel_page() && - !bucket->empty_pages_head && !bucket->decommitted_pages_head && - !bucket->num_full_pages) - return; - - memset(stats_out, '\0', sizeof(*stats_out)); - stats_out->is_valid = true; - stats_out->is_direct_map = false; - stats_out->num_full_pages = static_cast(bucket->num_full_pages); - stats_out->bucket_slot_size = bucket->slot_size; - uint16_t bucket_num_slots = bucket->get_slots_per_span(); - size_t bucket_useful_storage = stats_out->bucket_slot_size * bucket_num_slots; - stats_out->allocated_page_size = bucket->get_bytes_per_span(); - stats_out->active_bytes = bucket->num_full_pages * bucket_useful_storage; - stats_out->resident_bytes = - bucket->num_full_pages * stats_out->allocated_page_size; - - for (internal::PartitionPage* page = bucket->empty_pages_head; page; - page = page->next_page) { - DCHECK(page->is_empty() || page->is_decommitted()); - PartitionDumpPageStats(stats_out, page); - } - for (internal::PartitionPage* page = bucket->decommitted_pages_head; page; - page = page->next_page) { - DCHECK(page->is_decommitted()); - PartitionDumpPageStats(stats_out, page); - } - - if (bucket->active_pages_head != - internal::PartitionPage::get_sentinel_page()) { - for (internal::PartitionPage* page = bucket->active_pages_head; page; - page = page->next_page) { - DCHECK(page != internal::PartitionPage::get_sentinel_page()); - PartitionDumpPageStats(stats_out, page); - } - } -} - -void PartitionRootGeneric::DumpStats(const char* partition_name, - bool is_light_dump, - PartitionStatsDumper* dumper) { - PartitionMemoryStats stats = {0}; - stats.total_mmapped_bytes = - this->total_size_of_super_pages + this->total_size_of_direct_mapped_pages; - stats.total_committed_bytes = this->total_size_of_committed_pages; - - size_t direct_mapped_allocations_total_size = 0; - - static const size_t kMaxReportableDirectMaps = 4096; - - // Allocate on the heap rather than on the stack to avoid stack overflow - // skirmishes (on Windows, in particular). - std::unique_ptr direct_map_lengths = nullptr; - if (!is_light_dump) { - direct_map_lengths = - std::unique_ptr(new uint32_t[kMaxReportableDirectMaps]); - } - - PartitionBucketMemoryStats bucket_stats[kGenericNumBuckets]; - size_t num_direct_mapped_allocations = 0; - { - subtle::SpinLock::Guard guard(this->lock); - - for (size_t i = 0; i < kGenericNumBuckets; ++i) { - const internal::PartitionBucket* bucket = &this->buckets[i]; - // Don't report the pseudo buckets that the generic allocator sets up in - // order to preserve a fast size->bucket map (see - // PartitionRootGeneric::Init() for details). - if (!bucket->active_pages_head) - bucket_stats[i].is_valid = false; - else - PartitionDumpBucketStats(&bucket_stats[i], bucket); - if (bucket_stats[i].is_valid) { - stats.total_resident_bytes += bucket_stats[i].resident_bytes; - stats.total_active_bytes += bucket_stats[i].active_bytes; - stats.total_decommittable_bytes += bucket_stats[i].decommittable_bytes; - stats.total_discardable_bytes += bucket_stats[i].discardable_bytes; - } - } - - for (internal::PartitionDirectMapExtent *extent = this->direct_map_list; - extent && num_direct_mapped_allocations < kMaxReportableDirectMaps; - extent = extent->next_extent, ++num_direct_mapped_allocations) { - DCHECK(!extent->next_extent || - extent->next_extent->prev_extent == extent); - size_t slot_size = extent->bucket->slot_size; - direct_mapped_allocations_total_size += slot_size; - if (is_light_dump) - continue; - direct_map_lengths[num_direct_mapped_allocations] = slot_size; - } - } - - if (!is_light_dump) { - // Call |PartitionsDumpBucketStats| after collecting stats because it can - // try to allocate using |PartitionRootGeneric::Alloc()| and it can't - // obtain the lock. - for (size_t i = 0; i < kGenericNumBuckets; ++i) { - if (bucket_stats[i].is_valid) - dumper->PartitionsDumpBucketStats(partition_name, &bucket_stats[i]); - } - - for (size_t i = 0; i < num_direct_mapped_allocations; ++i) { - uint32_t size = direct_map_lengths[i]; - - PartitionBucketMemoryStats mapped_stats = {}; - mapped_stats.is_valid = true; - mapped_stats.is_direct_map = true; - mapped_stats.num_full_pages = 1; - mapped_stats.allocated_page_size = size; - mapped_stats.bucket_slot_size = size; - mapped_stats.active_bytes = size; - mapped_stats.resident_bytes = size; - dumper->PartitionsDumpBucketStats(partition_name, &mapped_stats); - } - } - - stats.total_resident_bytes += direct_mapped_allocations_total_size; - stats.total_active_bytes += direct_mapped_allocations_total_size; - dumper->PartitionDumpTotals(partition_name, &stats); -} - -void PartitionRoot::DumpStats(const char* partition_name, - bool is_light_dump, - PartitionStatsDumper* dumper) { - PartitionMemoryStats stats = {0}; - stats.total_mmapped_bytes = this->total_size_of_super_pages; - stats.total_committed_bytes = this->total_size_of_committed_pages; - DCHECK(!this->total_size_of_direct_mapped_pages); - - static const size_t kMaxReportableBuckets = 4096 / sizeof(void*); - std::unique_ptr memory_stats; - if (!is_light_dump) - memory_stats = std::unique_ptr( - new PartitionBucketMemoryStats[kMaxReportableBuckets]); - - const size_t partitionNumBuckets = this->num_buckets; - DCHECK(partitionNumBuckets <= kMaxReportableBuckets); - - for (size_t i = 0; i < partitionNumBuckets; ++i) { - PartitionBucketMemoryStats bucket_stats = {0}; - PartitionDumpBucketStats(&bucket_stats, &this->buckets()[i]); - if (bucket_stats.is_valid) { - stats.total_resident_bytes += bucket_stats.resident_bytes; - stats.total_active_bytes += bucket_stats.active_bytes; - stats.total_decommittable_bytes += bucket_stats.decommittable_bytes; - stats.total_discardable_bytes += bucket_stats.discardable_bytes; - } - if (!is_light_dump) { - if (bucket_stats.is_valid) - memory_stats[i] = bucket_stats; - else - memory_stats[i].is_valid = false; - } - } - if (!is_light_dump) { - // PartitionsDumpBucketStats is called after collecting stats because it - // can use PartitionRoot::Alloc() to allocate and this can affect the - // statistics. - for (size_t i = 0; i < partitionNumBuckets; ++i) { - if (memory_stats[i].is_valid) - dumper->PartitionsDumpBucketStats(partition_name, &memory_stats[i]); - } - } - dumper->PartitionDumpTotals(partition_name, &stats); -} - -} // namespace base diff --git a/allocator/partition_allocator/partition_alloc.h b/allocator/partition_allocator/partition_alloc.h deleted file mode 100644 index 79d09053c..000000000 --- a/allocator/partition_allocator/partition_alloc.h +++ /dev/null @@ -1,438 +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_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_ - -// DESCRIPTION -// PartitionRoot::Alloc() / PartitionRootGeneric::Alloc() and PartitionFree() / -// PartitionRootGeneric::Free() are approximately analagous to malloc() and -// free(). -// -// The main difference is that a PartitionRoot / PartitionRootGeneric object -// must be supplied to these functions, representing a specific "heap partition" -// that will be used to satisfy the allocation. Different partitions are -// guaranteed to exist in separate address spaces, including being separate from -// the main system heap. If the contained objects are all freed, physical memory -// is returned to the system but the address space remains reserved. -// See PartitionAlloc.md for other security properties PartitionAlloc provides. -// -// THE ONLY LEGITIMATE WAY TO OBTAIN A PartitionRoot IS THROUGH THE -// SizeSpecificPartitionAllocator / PartitionAllocatorGeneric classes. To -// minimize the instruction count to the fullest extent possible, the -// PartitionRoot is really just a header adjacent to other data areas provided -// by the allocator class. -// -// The PartitionRoot::Alloc() variant of the API has the following caveats: -// - Allocations and frees against a single partition must be single threaded. -// - Allocations must not exceed a max size, chosen at compile-time via a -// templated parameter to PartitionAllocator. -// - Allocation sizes must be aligned to the system pointer size. -// - Allocations are bucketed exactly according to size. -// -// And for PartitionRootGeneric::Alloc(): -// - Multi-threaded use against a single partition is ok; locking is handled. -// - Allocations of any arbitrary size can be handled (subject to a limit of -// INT_MAX bytes for security reasons). -// - Bucketing is by approximate size, for example an allocation of 4000 bytes -// might be placed into a 4096-byte bucket. Bucket sizes are chosen to try and -// keep worst-case waste to ~10%. -// -// The allocators are designed to be extremely fast, thanks to the following -// properties and design: -// - Just two single (reasonably predicatable) branches in the hot / fast path -// for both allocating and (significantly) freeing. -// - A minimal number of operations in the hot / fast path, with the slow paths -// in separate functions, leading to the possibility of inlining. -// - Each partition page (which is usually multiple physical pages) has a -// metadata structure which allows fast mapping of free() address to an -// underlying bucket. -// - Supports a lock-free API for fast performance in single-threaded cases. -// - The freelist for a given bucket is split across a number of partition -// pages, enabling various simple tricks to try and minimize fragmentation. -// - Fine-grained bucket sizes leading to less waste and better packing. -// -// The following security properties could be investigated in the future: -// - Per-object bucketing (instead of per-size) is mostly available at the API, -// but not used yet. -// - No randomness of freelist entries or bucket position. -// - Better checking for wild pointers in free(). -// - Better freelist masking function to guarantee fault on 32-bit. - -#include -#include - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_bucket.h" -#include "base/allocator/partition_allocator/partition_cookie.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "base/allocator/partition_allocator/partition_root_base.h" -#include "base/allocator/partition_allocator/spin_lock.h" -#include "base/base_export.h" -#include "base/bits.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/sys_byteorder.h" -#include "build/build_config.h" - -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) -#include -#endif - -namespace base { - -class PartitionStatsDumper; - -enum PartitionPurgeFlags { - // Decommitting the ring list of empty pages is reasonably fast. - PartitionPurgeDecommitEmptyPages = 1 << 0, - // Discarding unused system pages is slower, because it involves walking all - // freelists in all active partition pages of all buckets >= system page - // size. It often frees a similar amount of memory to decommitting the empty - // pages, though. - PartitionPurgeDiscardUnusedSystemPages = 1 << 1, -}; - -// Never instantiate a PartitionRoot directly, instead use PartitionAlloc. -struct BASE_EXPORT PartitionRoot : public internal::PartitionRootBase { - PartitionRoot(); - ~PartitionRoot() override; - // This references the buckets OFF the edge of this struct. All uses of - // PartitionRoot must have the bucket array come right after. - // - // The PartitionAlloc templated class ensures the following is correct. - ALWAYS_INLINE internal::PartitionBucket* buckets() { - return reinterpret_cast(this + 1); - } - ALWAYS_INLINE const internal::PartitionBucket* buckets() const { - return reinterpret_cast(this + 1); - } - - void Init(size_t num_buckets, size_t max_allocation); - - ALWAYS_INLINE void* Alloc(size_t size, const char* type_name); - - void PurgeMemory(int flags); - - void DumpStats(const char* partition_name, - bool is_light_dump, - PartitionStatsDumper* dumper); -}; - -// Never instantiate a PartitionRootGeneric directly, instead use -// PartitionAllocatorGeneric. -struct BASE_EXPORT PartitionRootGeneric : public internal::PartitionRootBase { - PartitionRootGeneric(); - ~PartitionRootGeneric() override; - subtle::SpinLock lock; - // Some pre-computed constants. - size_t order_index_shifts[kBitsPerSizeT + 1] = {}; - size_t order_sub_index_masks[kBitsPerSizeT + 1] = {}; - // The bucket lookup table lets us map a size_t to a bucket quickly. - // The trailing +1 caters for the overflow case for very large allocation - // sizes. It is one flat array instead of a 2D array because in the 2D - // world, we'd need to index array[blah][max+1] which risks undefined - // behavior. - internal::PartitionBucket* - bucket_lookups[((kBitsPerSizeT + 1) * kGenericNumBucketsPerOrder) + 1] = - {}; - internal::PartitionBucket buckets[kGenericNumBuckets] = {}; - - // Public API. - void Init(); - - ALWAYS_INLINE void* Alloc(size_t size, const char* type_name); - ALWAYS_INLINE void Free(void* ptr); - - NOINLINE void* Realloc(void* ptr, size_t new_size, const char* type_name); - - ALWAYS_INLINE size_t ActualSize(size_t size); - - void PurgeMemory(int flags); - - void DumpStats(const char* partition_name, - bool is_light_dump, - PartitionStatsDumper* partition_stats_dumper); -}; - -// Struct used to retrieve total memory usage of a partition. Used by -// PartitionStatsDumper implementation. -struct PartitionMemoryStats { - size_t total_mmapped_bytes; // Total bytes mmaped from the system. - size_t total_committed_bytes; // Total size of commmitted pages. - size_t total_resident_bytes; // Total bytes provisioned by the partition. - size_t total_active_bytes; // Total active bytes in the partition. - size_t total_decommittable_bytes; // Total bytes that could be decommitted. - size_t total_discardable_bytes; // Total bytes that could be discarded. -}; - -// Struct used to retrieve memory statistics about a partition bucket. Used by -// PartitionStatsDumper implementation. -struct PartitionBucketMemoryStats { - bool is_valid; // Used to check if the stats is valid. - bool is_direct_map; // True if this is a direct mapping; size will not be - // unique. - uint32_t bucket_slot_size; // The size of the slot in bytes. - uint32_t allocated_page_size; // Total size the partition page allocated from - // the system. - uint32_t active_bytes; // Total active bytes used in the bucket. - uint32_t resident_bytes; // Total bytes provisioned in the bucket. - uint32_t decommittable_bytes; // Total bytes that could be decommitted. - uint32_t discardable_bytes; // Total bytes that could be discarded. - uint32_t num_full_pages; // Number of pages with all slots allocated. - uint32_t num_active_pages; // Number of pages that have at least one - // provisioned slot. - uint32_t num_empty_pages; // Number of pages that are empty - // but not decommitted. - uint32_t num_decommitted_pages; // Number of pages that are empty - // and decommitted. -}; - -// Interface that is passed to PartitionDumpStats and -// PartitionDumpStatsGeneric for using the memory statistics. -class BASE_EXPORT PartitionStatsDumper { - public: - // Called to dump total memory used by partition, once per partition. - virtual void PartitionDumpTotals(const char* partition_name, - const PartitionMemoryStats*) = 0; - - // Called to dump stats about buckets, for each bucket. - virtual void PartitionsDumpBucketStats(const char* partition_name, - const PartitionBucketMemoryStats*) = 0; -}; - -BASE_EXPORT void PartitionAllocGlobalInit(void (*oom_handling_function)()); - -class BASE_EXPORT PartitionAllocHooks { - public: - typedef void AllocationHook(void* address, size_t, const char* type_name); - typedef void FreeHook(void* address); - - // To unhook, call Set*Hook with nullptr. - static void SetAllocationHook(AllocationHook* hook) { - // Chained allocation hooks are not supported. Registering a non-null - // hook when a non-null hook is already registered indicates somebody is - // trying to overwrite a hook. - CHECK(!hook || !allocation_hook_) << "Overwriting allocation hook"; - allocation_hook_ = hook; - } - static void SetFreeHook(FreeHook* hook) { - CHECK(!hook || !free_hook_) << "Overwriting free hook"; - free_hook_ = hook; - } - - static void AllocationHookIfEnabled(void* address, - size_t size, - const char* type_name) { - AllocationHook* hook = allocation_hook_; - if (UNLIKELY(hook != nullptr)) - hook(address, size, type_name); - } - - static void FreeHookIfEnabled(void* address) { - FreeHook* hook = free_hook_; - if (UNLIKELY(hook != nullptr)) - hook(address); - } - - static void ReallocHookIfEnabled(void* old_address, - void* new_address, - size_t size, - const char* type_name) { - // Report a reallocation as a free followed by an allocation. - AllocationHook* allocation_hook = allocation_hook_; - FreeHook* free_hook = free_hook_; - if (UNLIKELY(allocation_hook && free_hook)) { - free_hook(old_address); - allocation_hook(new_address, size, type_name); - } - } - - private: - // Pointers to hook functions that PartitionAlloc will call on allocation and - // free if the pointers are non-null. - static AllocationHook* allocation_hook_; - static FreeHook* free_hook_; -}; - -ALWAYS_INLINE void* PartitionRoot::Alloc(size_t size, const char* type_name) { -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - void* result = malloc(size); - CHECK(result); - return result; -#else - size_t requested_size = size; - size = internal::PartitionCookieSizeAdjustAdd(size); - DCHECK(this->initialized); - size_t index = size >> kBucketShift; - DCHECK(index < this->num_buckets); - DCHECK(size == index << kBucketShift); - internal::PartitionBucket* bucket = &this->buckets()[index]; - void* result = AllocFromBucket(bucket, 0, size); - PartitionAllocHooks::AllocationHookIfEnabled(result, requested_size, - type_name); - return result; -#endif // defined(MEMORY_TOOL_REPLACES_ALLOCATOR) -} - -ALWAYS_INLINE void PartitionFree(void* ptr) { -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - free(ptr); -#else - // TODO(palmer): Check ptr alignment before continuing. Shall we do the check - // inside PartitionCookieFreePointerAdjust? - PartitionAllocHooks::FreeHookIfEnabled(ptr); - ptr = internal::PartitionCookieFreePointerAdjust(ptr); - internal::PartitionPage* page = internal::PartitionPage::FromPointer(ptr); - // TODO(palmer): See if we can afford to make this a CHECK. - DCHECK(internal::PartitionRootBase::IsValidPage(page)); - page->Free(ptr); -#endif -} - -ALWAYS_INLINE internal::PartitionBucket* PartitionGenericSizeToBucket( - PartitionRootGeneric* root, - size_t size) { - size_t order = kBitsPerSizeT - bits::CountLeadingZeroBitsSizeT(size); - // The order index is simply the next few bits after the most significant bit. - size_t order_index = (size >> root->order_index_shifts[order]) & - (kGenericNumBucketsPerOrder - 1); - // And if the remaining bits are non-zero we must bump the bucket up. - size_t sub_order_index = size & root->order_sub_index_masks[order]; - internal::PartitionBucket* bucket = - root->bucket_lookups[(order << kGenericNumBucketsPerOrderBits) + - order_index + !!sub_order_index]; - DCHECK(!bucket->slot_size || bucket->slot_size >= size); - DCHECK(!(bucket->slot_size % kGenericSmallestBucket)); - return bucket; -} - -ALWAYS_INLINE void* PartitionAllocGenericFlags(PartitionRootGeneric* root, - int flags, - size_t size, - const char* type_name) { -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - void* result = malloc(size); - CHECK(result || flags & PartitionAllocReturnNull); - return result; -#else - DCHECK(root->initialized); - size_t requested_size = size; - size = internal::PartitionCookieSizeAdjustAdd(size); - internal::PartitionBucket* bucket = PartitionGenericSizeToBucket(root, size); - void* ret = nullptr; - { - subtle::SpinLock::Guard guard(root->lock); - ret = root->AllocFromBucket(bucket, flags, size); - } - PartitionAllocHooks::AllocationHookIfEnabled(ret, requested_size, type_name); - return ret; -#endif -} - -ALWAYS_INLINE void* PartitionRootGeneric::Alloc(size_t size, - const char* type_name) { - return PartitionAllocGenericFlags(this, 0, size, type_name); -} - -ALWAYS_INLINE void PartitionRootGeneric::Free(void* ptr) { -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - free(ptr); -#else - DCHECK(this->initialized); - - if (UNLIKELY(!ptr)) - return; - - PartitionAllocHooks::FreeHookIfEnabled(ptr); - ptr = internal::PartitionCookieFreePointerAdjust(ptr); - internal::PartitionPage* page = internal::PartitionPage::FromPointer(ptr); - // TODO(palmer): See if we can afford to make this a CHECK. - DCHECK(IsValidPage(page)); - { - subtle::SpinLock::Guard guard(this->lock); - page->Free(ptr); - } -#endif -} - -BASE_EXPORT void* PartitionReallocGenericFlags(PartitionRootGeneric* root, - int flags, - void* ptr, - size_t new_size, - const char* type_name); - -ALWAYS_INLINE size_t PartitionRootGeneric::ActualSize(size_t size) { -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - return size; -#else - DCHECK(this->initialized); - size = internal::PartitionCookieSizeAdjustAdd(size); - internal::PartitionBucket* bucket = PartitionGenericSizeToBucket(this, size); - if (LIKELY(!bucket->is_direct_mapped())) { - size = bucket->slot_size; - } else if (size > kGenericMaxDirectMapped) { - // Too large to allocate => return the size unchanged. - } else { - size = internal::PartitionBucket::get_direct_map_size(size); - } - return internal::PartitionCookieSizeAdjustSubtract(size); -#endif -} - -ALWAYS_INLINE bool PartitionAllocSupportsGetSize() { -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - return false; -#else - return true; -#endif -} - -ALWAYS_INLINE size_t PartitionAllocGetSize(void* ptr) { - // No need to lock here. Only |ptr| being freed by another thread could - // cause trouble, and the caller is responsible for that not happening. - DCHECK(PartitionAllocSupportsGetSize()); - ptr = internal::PartitionCookieFreePointerAdjust(ptr); - internal::PartitionPage* page = internal::PartitionPage::FromPointer(ptr); - // TODO(palmer): See if we can afford to make this a CHECK. - DCHECK(internal::PartitionRootBase::IsValidPage(page)); - size_t size = page->bucket->slot_size; - return internal::PartitionCookieSizeAdjustSubtract(size); -} - -template -class SizeSpecificPartitionAllocator { - public: - SizeSpecificPartitionAllocator() { - memset(actual_buckets_, 0, - sizeof(internal::PartitionBucket) * arraysize(actual_buckets_)); - } - ~SizeSpecificPartitionAllocator() = default; - static const size_t kMaxAllocation = N - kAllocationGranularity; - static const size_t kNumBuckets = N / kAllocationGranularity; - void init() { partition_root_.Init(kNumBuckets, kMaxAllocation); } - ALWAYS_INLINE PartitionRoot* root() { return &partition_root_; } - - private: - PartitionRoot partition_root_; - internal::PartitionBucket actual_buckets_[kNumBuckets]; -}; - -class BASE_EXPORT PartitionAllocatorGeneric { - public: - PartitionAllocatorGeneric(); - ~PartitionAllocatorGeneric(); - - void init() { partition_root_.Init(); } - ALWAYS_INLINE PartitionRootGeneric* root() { return &partition_root_; } - - private: - PartitionRootGeneric partition_root_; -}; - -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_H_ diff --git a/allocator/partition_allocator/partition_alloc_constants.h b/allocator/partition_allocator/partition_alloc_constants.h deleted file mode 100644 index deaa19e34..000000000 --- a/allocator/partition_allocator/partition_alloc_constants.h +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_ - -#include - -#include "base/allocator/partition_allocator/page_allocator_constants.h" -#include "base/bits.h" -#include "base/logging.h" - -namespace base { - -// Allocation granularity of sizeof(void*) bytes. -static const size_t kAllocationGranularity = sizeof(void*); -static const size_t kAllocationGranularityMask = kAllocationGranularity - 1; -static const size_t kBucketShift = (kAllocationGranularity == 8) ? 3 : 2; - -// Underlying partition storage pages are a power-of-two size. It is typical -// for a partition page to be based on multiple system pages. Most references to -// "page" refer to partition pages. -// We also have the concept of "super pages" -- these are the underlying system -// allocations we make. Super pages contain multiple partition pages inside them -// and include space for a small amount of metadata per partition page. -// Inside super pages, we store "slot spans". A slot span is a continguous range -// of one or more partition pages that stores allocations of the same size. -// Slot span sizes are adjusted depending on the allocation size, to make sure -// the packing does not lead to unused (wasted) space at the end of the last -// system page of the span. For our current max slot span size of 64k and other -// constant values, we pack _all_ PartitionRootGeneric::Alloc() sizes perfectly -// up against the end of a system page. -#if defined(_MIPS_ARCH_LOONGSON) -static const size_t kPartitionPageShift = 16; // 64KB -#else -static const size_t kPartitionPageShift = 14; // 16KB -#endif -static const size_t kPartitionPageSize = 1 << kPartitionPageShift; -static const size_t kPartitionPageOffsetMask = kPartitionPageSize - 1; -static const size_t kPartitionPageBaseMask = ~kPartitionPageOffsetMask; -static const size_t kMaxPartitionPagesPerSlotSpan = 4; - -// To avoid fragmentation via never-used freelist entries, we hand out partition -// freelist sections gradually, in units of the dominant system page size. -// What we're actually doing is avoiding filling the full partition page (16 KB) -// with freelist pointers right away. Writing freelist pointers will fault and -// dirty a private page, which is very wasteful if we never actually store -// objects there. -static const size_t kNumSystemPagesPerPartitionPage = - kPartitionPageSize / kSystemPageSize; -static const size_t kMaxSystemPagesPerSlotSpan = - kNumSystemPagesPerPartitionPage * kMaxPartitionPagesPerSlotSpan; - -// We reserve virtual address space in 2MB chunks (aligned to 2MB as well). -// These chunks are called "super pages". We do this so that we can store -// metadata in the first few pages of each 2MB aligned section. This leads to -// a very fast free(). We specifically choose 2MB because this virtual address -// block represents a full but single PTE allocation on ARM, ia32 and x64. -// -// The layout of the super page is as follows. The sizes below are the same -// for 32 bit and 64 bit. -// -// | Guard page (4KB) | -// | Metadata page (4KB) | -// | Guard pages (8KB) | -// | Slot span | -// | Slot span | -// | ... | -// | Slot span | -// | Guard page (4KB) | -// -// - Each slot span is a contiguous range of one or more PartitionPages. -// - The metadata page has the following format. Note that the PartitionPage -// that is not at the head of a slot span is "unused". In other words, -// the metadata for the slot span is stored only in the first PartitionPage -// of the slot span. Metadata accesses to other PartitionPages are -// redirected to the first PartitionPage. -// -// | SuperPageExtentEntry (32B) | -// | PartitionPage of slot span 1 (32B, used) | -// | PartitionPage of slot span 1 (32B, unused) | -// | PartitionPage of slot span 1 (32B, unused) | -// | PartitionPage of slot span 2 (32B, used) | -// | PartitionPage of slot span 3 (32B, used) | -// | ... | -// | PartitionPage of slot span N (32B, unused) | -// -// A direct mapped page has a similar layout to fake it looking like a super -// page: -// -// | Guard page (4KB) | -// | Metadata page (4KB) | -// | Guard pages (8KB) | -// | Direct mapped object | -// | Guard page (4KB) | -// -// - The metadata page has the following layout: -// -// | SuperPageExtentEntry (32B) | -// | PartitionPage (32B) | -// | PartitionBucket (32B) | -// | PartitionDirectMapExtent (8B) | -static const size_t kSuperPageShift = 21; // 2MB -static const size_t kSuperPageSize = 1 << kSuperPageShift; -static const size_t kSuperPageOffsetMask = kSuperPageSize - 1; -static const size_t kSuperPageBaseMask = ~kSuperPageOffsetMask; -static const size_t kNumPartitionPagesPerSuperPage = - kSuperPageSize / kPartitionPageSize; - -// The following kGeneric* constants apply to the generic variants of the API. -// The "order" of an allocation is closely related to the power-of-two size of -// the allocation. More precisely, the order is the bit index of the -// most-significant-bit in the allocation size, where the bit numbers starts -// at index 1 for the least-significant-bit. -// In terms of allocation sizes, order 0 covers 0, order 1 covers 1, order 2 -// covers 2->3, order 3 covers 4->7, order 4 covers 8->15. -static const size_t kGenericMinBucketedOrder = 4; // 8 bytes. -static const size_t kGenericMaxBucketedOrder = - 20; // Largest bucketed order is 1<<(20-1) (storing 512KB -> almost 1MB) -static const size_t kGenericNumBucketedOrders = - (kGenericMaxBucketedOrder - kGenericMinBucketedOrder) + 1; -// Eight buckets per order (for the higher orders), e.g. order 8 is 128, 144, -// 160, ..., 240: -static const size_t kGenericNumBucketsPerOrderBits = 3; -static const size_t kGenericNumBucketsPerOrder = - 1 << kGenericNumBucketsPerOrderBits; -static const size_t kGenericNumBuckets = - kGenericNumBucketedOrders * kGenericNumBucketsPerOrder; -static const size_t kGenericSmallestBucket = 1 - << (kGenericMinBucketedOrder - 1); -static const size_t kGenericMaxBucketSpacing = - 1 << ((kGenericMaxBucketedOrder - 1) - kGenericNumBucketsPerOrderBits); -static const size_t kGenericMaxBucketed = - (1 << (kGenericMaxBucketedOrder - 1)) + - ((kGenericNumBucketsPerOrder - 1) * kGenericMaxBucketSpacing); -static const size_t kGenericMinDirectMappedDownsize = - kGenericMaxBucketed + - 1; // Limit when downsizing a direct mapping using realloc(). -static const size_t kGenericMaxDirectMapped = - (1UL << 31) + kPageAllocationGranularity; // 2 GB plus one more page. -static const size_t kBitsPerSizeT = sizeof(void*) * CHAR_BIT; - -// Constant for the memory reclaim logic. -static const size_t kMaxFreeableSpans = 16; - -// If the total size in bytes of allocated but not committed pages exceeds this -// value (probably it is a "out of virtual address space" crash), -// a special crash stack trace is generated at |PartitionOutOfMemory|. -// This is to distinguish "out of virtual address space" from -// "out of physical memory" in crash reports. -static const size_t kReasonableSizeOfUnusedPages = 1024 * 1024 * 1024; // 1GB - -// Flags for PartitionAllocGenericFlags. -enum PartitionAllocFlags { - PartitionAllocReturnNull = 1 << 0, -}; - -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONSTANTS_H_ diff --git a/allocator/partition_allocator/partition_alloc_unittest.cc b/allocator/partition_allocator/partition_alloc_unittest.cc deleted file mode 100644 index da065146e..000000000 --- a/allocator/partition_allocator/partition_alloc_unittest.cc +++ /dev/null @@ -1,2127 +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/allocator/partition_allocator/partition_alloc.h" - -#include -#include - -#include -#include - -#include "base/allocator/partition_allocator/address_space_randomization.h" -#include "base/bit_cast.h" -#include "base/bits.h" -#include "base/sys_info.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_POSIX) -#include -#if !defined(OS_FUCHSIA) -#include -#endif -#include -#endif // defined(OS_POSIX) - -#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) - -// Because there is so much deep inspection of the internal objects, -// explicitly annotating the namespaces for commonly expected objects makes the -// code unreadable. Prefer using directives instead. -using base::internal::PartitionBucket; -using base::internal::PartitionPage; - -namespace { - -constexpr size_t kTestMaxAllocation = base::kSystemPageSize; - -bool IsLargeMemoryDevice() { - // Treat any device with 2GiB or more of physical memory as a "large memory - // device". We check for slightly less than 2GiB so that devices with a small - // amount of memory not accessible to the OS still count as "large". - return base::SysInfo::AmountOfPhysicalMemory() >= 2040LL * 1024 * 1024; -} - -bool SetAddressSpaceLimit() { -#if !defined(ARCH_CPU_64_BITS) || !defined(OS_POSIX) - // 32 bits => address space is limited already. - return true; -#elif defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_FUCHSIA) - // macOS will accept, but not enforce, |RLIMIT_AS| changes. See - // https://crbug.com/435269 and rdar://17576114. - // - // Note: This number must be not less than 6 GB, because with - // sanitizer_coverage_flags=edge, it reserves > 5 GB of address space. See - // https://crbug.com/674665. - const size_t kAddressSpaceLimit = static_cast(6144) * 1024 * 1024; - struct rlimit limit; - if (getrlimit(RLIMIT_AS, &limit) != 0) - return false; - if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > kAddressSpaceLimit) { - limit.rlim_cur = kAddressSpaceLimit; - if (setrlimit(RLIMIT_AS, &limit) != 0) - return false; - } - return true; -#else - return false; -#endif -} - -bool ClearAddressSpaceLimit() { -#if !defined(ARCH_CPU_64_BITS) || !defined(OS_POSIX) || defined(OS_FUCHSIA) - return true; -#elif defined(OS_POSIX) - struct rlimit limit; - if (getrlimit(RLIMIT_AS, &limit) != 0) - return false; - limit.rlim_cur = limit.rlim_max; - if (setrlimit(RLIMIT_AS, &limit) != 0) - return false; - return true; -#else - return false; -#endif -} - -} // namespace - -namespace base { - -// NOTE: Though this test actually excercises interfaces inside the ::base -// namespace, the unittest is inside the ::base::internal spaces because a -// portion of the test expectations require inspecting objects and behavior -// in the ::base::internal namespace. An alternate formulation would be to -// explicitly add using statements for each inspected type but this felt more -// readable. -namespace internal { - -const size_t kTestAllocSize = 16; -#if !DCHECK_IS_ON() -const size_t kPointerOffset = 0; -const size_t kExtraAllocSize = 0; -#else -const size_t kPointerOffset = kCookieSize; -const size_t kExtraAllocSize = kCookieSize * 2; -#endif -const size_t kRealAllocSize = kTestAllocSize + kExtraAllocSize; -const size_t kTestBucketIndex = kRealAllocSize >> kBucketShift; - -const char* type_name = nullptr; - -class PartitionAllocTest : public testing::Test { - protected: - PartitionAllocTest() = default; - - ~PartitionAllocTest() override = default; - - void SetUp() override { - allocator.init(); - generic_allocator.init(); - } - - PartitionPage* GetFullPage(size_t size) { - size_t real_size = size + kExtraAllocSize; - size_t bucket_index = real_size >> kBucketShift; - PartitionBucket* bucket = &allocator.root()->buckets()[bucket_index]; - size_t num_slots = - (bucket->num_system_pages_per_slot_span * kSystemPageSize) / real_size; - void* first = nullptr; - void* last = nullptr; - size_t i; - for (i = 0; i < num_slots; ++i) { - void* ptr = allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - if (!i) - first = PartitionCookieFreePointerAdjust(ptr); - else if (i == num_slots - 1) - last = PartitionCookieFreePointerAdjust(ptr); - } - EXPECT_EQ(PartitionPage::FromPointer(first), - PartitionPage::FromPointer(last)); - if (bucket->num_system_pages_per_slot_span == - kNumSystemPagesPerPartitionPage) - EXPECT_EQ(reinterpret_cast(first) & kPartitionPageBaseMask, - reinterpret_cast(last) & kPartitionPageBaseMask); - EXPECT_EQ(num_slots, static_cast( - bucket->active_pages_head->num_allocated_slots)); - EXPECT_EQ(nullptr, bucket->active_pages_head->freelist_head); - EXPECT_TRUE(bucket->active_pages_head); - EXPECT_TRUE(bucket->active_pages_head != - PartitionPage::get_sentinel_page()); - return bucket->active_pages_head; - } - - void CycleFreeCache(size_t size) { - size_t real_size = size + kExtraAllocSize; - size_t bucket_index = real_size >> kBucketShift; - PartitionBucket* bucket = &allocator.root()->buckets()[bucket_index]; - DCHECK(!bucket->active_pages_head->num_allocated_slots); - - for (size_t i = 0; i < kMaxFreeableSpans; ++i) { - void* ptr = allocator.root()->Alloc(size, type_name); - EXPECT_EQ(1, bucket->active_pages_head->num_allocated_slots); - PartitionFree(ptr); - EXPECT_EQ(0, bucket->active_pages_head->num_allocated_slots); - EXPECT_NE(-1, bucket->active_pages_head->empty_cache_index); - } - } - - void CycleGenericFreeCache(size_t size) { - for (size_t i = 0; i < kMaxFreeableSpans; ++i) { - void* ptr = generic_allocator.root()->Alloc(size, type_name); - PartitionPage* page = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - PartitionBucket* bucket = page->bucket; - EXPECT_EQ(1, bucket->active_pages_head->num_allocated_slots); - generic_allocator.root()->Free(ptr); - EXPECT_EQ(0, bucket->active_pages_head->num_allocated_slots); - EXPECT_NE(-1, bucket->active_pages_head->empty_cache_index); - } - } - - void DoReturnNullTest(size_t allocSize, bool use_realloc) { - // TODO(crbug.com/678782): Where necessary and possible, disable the - // platform's OOM-killing behavior. OOM-killing makes this test flaky on - // low-memory devices. - if (!IsLargeMemoryDevice()) { - LOG(WARNING) - << "Skipping test on this device because of crbug.com/678782"; - LOG(FATAL) << "DoReturnNullTest"; - } - - ASSERT_TRUE(SetAddressSpaceLimit()); - - // Work out the number of allocations for 6 GB of memory. - const int numAllocations = (6 * 1024 * 1024) / (allocSize / 1024); - - void** ptrs = reinterpret_cast(generic_allocator.root()->Alloc( - numAllocations * sizeof(void*), type_name)); - int i; - - for (i = 0; i < numAllocations; ++i) { - if (use_realloc) { - ptrs[i] = PartitionAllocGenericFlags( - generic_allocator.root(), PartitionAllocReturnNull, 1, type_name); - ptrs[i] = PartitionReallocGenericFlags(generic_allocator.root(), - PartitionAllocReturnNull, - ptrs[i], allocSize, type_name); - } else { - ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(), - PartitionAllocReturnNull, - allocSize, type_name); - } - if (!i) - EXPECT_TRUE(ptrs[0]); - if (!ptrs[i]) { - ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(), - PartitionAllocReturnNull, - allocSize, type_name); - EXPECT_FALSE(ptrs[i]); - break; - } - } - - // We shouldn't succeed in allocating all 6 GB of memory. If we do, then - // we're not actually testing anything here. - EXPECT_LT(i, numAllocations); - - // Free, reallocate and free again each block we allocated. We do this to - // check that freeing memory also works correctly after a failed allocation. - for (--i; i >= 0; --i) { - generic_allocator.root()->Free(ptrs[i]); - ptrs[i] = PartitionAllocGenericFlags(generic_allocator.root(), - PartitionAllocReturnNull, allocSize, - type_name); - EXPECT_TRUE(ptrs[i]); - generic_allocator.root()->Free(ptrs[i]); - } - - generic_allocator.root()->Free(ptrs); - - EXPECT_TRUE(ClearAddressSpaceLimit()); - LOG(FATAL) << "DoReturnNullTest"; - } - - SizeSpecificPartitionAllocator allocator; - PartitionAllocatorGeneric generic_allocator; -}; - -class PartitionAllocDeathTest : public PartitionAllocTest {}; - -namespace { - -void FreeFullPage(PartitionPage* page) { - size_t size = page->bucket->slot_size; - size_t num_slots = - (page->bucket->num_system_pages_per_slot_span * kSystemPageSize) / size; - EXPECT_EQ(num_slots, static_cast(abs(page->num_allocated_slots))); - char* ptr = reinterpret_cast(PartitionPage::ToPointer(page)); - size_t i; - for (i = 0; i < num_slots; ++i) { - PartitionFree(ptr + kPointerOffset); - ptr += size; - } -} - -#if defined(OS_LINUX) -bool CheckPageInCore(void* ptr, bool in_core) { - unsigned char ret = 0; - EXPECT_EQ(0, mincore(ptr, kSystemPageSize, &ret)); - return in_core == (ret & 1); -} - -#define CHECK_PAGE_IN_CORE(ptr, in_core) \ - EXPECT_TRUE(CheckPageInCore(ptr, in_core)) -#else -#define CHECK_PAGE_IN_CORE(ptr, in_core) (void)(0) -#endif // defined(OS_LINUX) - -class MockPartitionStatsDumper : public PartitionStatsDumper { - public: - MockPartitionStatsDumper() - : total_resident_bytes(0), - total_active_bytes(0), - total_decommittable_bytes(0), - total_discardable_bytes(0) {} - - void PartitionDumpTotals(const char* partition_name, - const PartitionMemoryStats* stats) override { - EXPECT_GE(stats->total_mmapped_bytes, stats->total_resident_bytes); - EXPECT_EQ(total_resident_bytes, stats->total_resident_bytes); - EXPECT_EQ(total_active_bytes, stats->total_active_bytes); - EXPECT_EQ(total_decommittable_bytes, stats->total_decommittable_bytes); - EXPECT_EQ(total_discardable_bytes, stats->total_discardable_bytes); - } - - void PartitionsDumpBucketStats( - const char* partition_name, - const PartitionBucketMemoryStats* stats) override { - (void)partition_name; - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->bucket_slot_size & kAllocationGranularityMask); - bucket_stats.push_back(*stats); - total_resident_bytes += stats->resident_bytes; - total_active_bytes += stats->active_bytes; - total_decommittable_bytes += stats->decommittable_bytes; - total_discardable_bytes += stats->discardable_bytes; - } - - bool IsMemoryAllocationRecorded() { - return total_resident_bytes != 0 && total_active_bytes != 0; - } - - const PartitionBucketMemoryStats* GetBucketStats(size_t bucket_size) { - for (size_t i = 0; i < bucket_stats.size(); ++i) { - if (bucket_stats[i].bucket_slot_size == bucket_size) - return &bucket_stats[i]; - } - return nullptr; - } - - private: - size_t total_resident_bytes; - size_t total_active_bytes; - size_t total_decommittable_bytes; - size_t total_discardable_bytes; - - std::vector bucket_stats; -}; - -} // namespace - -// Check that the most basic of allocate / free pairs work. -TEST_F(PartitionAllocTest, Basic) { - PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; - PartitionPage* seedPage = PartitionPage::get_sentinel_page(); - - EXPECT_FALSE(bucket->empty_pages_head); - EXPECT_FALSE(bucket->decommitted_pages_head); - EXPECT_EQ(seedPage, bucket->active_pages_head); - EXPECT_EQ(nullptr, bucket->active_pages_head->next_page); - - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - EXPECT_EQ(kPointerOffset, - reinterpret_cast(ptr) & kPartitionPageOffsetMask); - // Check that the offset appears to include a guard page. - EXPECT_EQ(kPartitionPageSize + kPointerOffset, - reinterpret_cast(ptr) & kSuperPageOffsetMask); - - PartitionFree(ptr); - // Expect that the last active page gets noticed as empty but doesn't get - // decommitted. - EXPECT_TRUE(bucket->empty_pages_head); - EXPECT_FALSE(bucket->decommitted_pages_head); -} - -// Test multiple allocations, and freelist handling. -TEST_F(PartitionAllocTest, MultiAlloc) { - char* ptr1 = reinterpret_cast( - allocator.root()->Alloc(kTestAllocSize, type_name)); - char* ptr2 = reinterpret_cast( - allocator.root()->Alloc(kTestAllocSize, type_name)); - EXPECT_TRUE(ptr1); - EXPECT_TRUE(ptr2); - ptrdiff_t diff = ptr2 - ptr1; - EXPECT_EQ(static_cast(kRealAllocSize), diff); - - // Check that we re-use the just-freed slot. - PartitionFree(ptr2); - ptr2 = reinterpret_cast( - allocator.root()->Alloc(kTestAllocSize, type_name)); - EXPECT_TRUE(ptr2); - diff = ptr2 - ptr1; - EXPECT_EQ(static_cast(kRealAllocSize), diff); - PartitionFree(ptr1); - ptr1 = reinterpret_cast( - allocator.root()->Alloc(kTestAllocSize, type_name)); - EXPECT_TRUE(ptr1); - diff = ptr2 - ptr1; - EXPECT_EQ(static_cast(kRealAllocSize), diff); - - char* ptr3 = reinterpret_cast( - allocator.root()->Alloc(kTestAllocSize, type_name)); - EXPECT_TRUE(ptr3); - diff = ptr3 - ptr1; - EXPECT_EQ(static_cast(kRealAllocSize * 2), diff); - - PartitionFree(ptr1); - PartitionFree(ptr2); - PartitionFree(ptr3); -} - -// Test a bucket with multiple pages. -TEST_F(PartitionAllocTest, MultiPages) { - PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; - - PartitionPage* page = GetFullPage(kTestAllocSize); - FreeFullPage(page); - EXPECT_TRUE(bucket->empty_pages_head); - EXPECT_EQ(PartitionPage::get_sentinel_page(), bucket->active_pages_head); - EXPECT_EQ(nullptr, page->next_page); - EXPECT_EQ(0, page->num_allocated_slots); - - page = GetFullPage(kTestAllocSize); - PartitionPage* page2 = GetFullPage(kTestAllocSize); - - EXPECT_EQ(page2, bucket->active_pages_head); - EXPECT_EQ(nullptr, page2->next_page); - EXPECT_EQ(reinterpret_cast(PartitionPage::ToPointer(page)) & - kSuperPageBaseMask, - reinterpret_cast(PartitionPage::ToPointer(page2)) & - kSuperPageBaseMask); - - // Fully free the non-current page. This will leave us with no current - // active page because one is empty and the other is full. - FreeFullPage(page); - EXPECT_EQ(0, page->num_allocated_slots); - EXPECT_TRUE(bucket->empty_pages_head); - EXPECT_EQ(PartitionPage::get_sentinel_page(), bucket->active_pages_head); - - // Allocate a new page, it should pull from the freelist. - page = GetFullPage(kTestAllocSize); - EXPECT_FALSE(bucket->empty_pages_head); - EXPECT_EQ(page, bucket->active_pages_head); - - FreeFullPage(page); - FreeFullPage(page2); - EXPECT_EQ(0, page->num_allocated_slots); - EXPECT_EQ(0, page2->num_allocated_slots); - EXPECT_EQ(0, page2->num_unprovisioned_slots); - EXPECT_NE(-1, page2->empty_cache_index); -} - -// Test some finer aspects of internal page transitions. -TEST_F(PartitionAllocTest, PageTransitions) { - PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; - - PartitionPage* page1 = GetFullPage(kTestAllocSize); - EXPECT_EQ(page1, bucket->active_pages_head); - EXPECT_EQ(nullptr, page1->next_page); - PartitionPage* page2 = GetFullPage(kTestAllocSize); - EXPECT_EQ(page2, bucket->active_pages_head); - EXPECT_EQ(nullptr, page2->next_page); - - // Bounce page1 back into the non-full list then fill it up again. - char* ptr = - reinterpret_cast(PartitionPage::ToPointer(page1)) + kPointerOffset; - PartitionFree(ptr); - EXPECT_EQ(page1, bucket->active_pages_head); - (void)allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_EQ(page1, bucket->active_pages_head); - EXPECT_EQ(page2, bucket->active_pages_head->next_page); - - // Allocating another page at this point should cause us to scan over page1 - // (which is both full and NOT our current page), and evict it from the - // freelist. Older code had a O(n^2) condition due to failure to do this. - PartitionPage* page3 = GetFullPage(kTestAllocSize); - EXPECT_EQ(page3, bucket->active_pages_head); - EXPECT_EQ(nullptr, page3->next_page); - - // Work out a pointer into page2 and free it. - ptr = - reinterpret_cast(PartitionPage::ToPointer(page2)) + kPointerOffset; - PartitionFree(ptr); - // Trying to allocate at this time should cause us to cycle around to page2 - // and find the recently freed slot. - char* newPtr = reinterpret_cast( - allocator.root()->Alloc(kTestAllocSize, type_name)); - EXPECT_EQ(ptr, newPtr); - EXPECT_EQ(page2, bucket->active_pages_head); - EXPECT_EQ(page3, page2->next_page); - - // Work out a pointer into page1 and free it. This should pull the page - // back into the list of available pages. - ptr = - reinterpret_cast(PartitionPage::ToPointer(page1)) + kPointerOffset; - PartitionFree(ptr); - // This allocation should be satisfied by page1. - newPtr = reinterpret_cast( - allocator.root()->Alloc(kTestAllocSize, type_name)); - EXPECT_EQ(ptr, newPtr); - EXPECT_EQ(page1, bucket->active_pages_head); - EXPECT_EQ(page2, page1->next_page); - - FreeFullPage(page3); - FreeFullPage(page2); - FreeFullPage(page1); - - // Allocating whilst in this state exposed a bug, so keep the test. - ptr = reinterpret_cast( - allocator.root()->Alloc(kTestAllocSize, type_name)); - PartitionFree(ptr); -} - -// Test some corner cases relating to page transitions in the internal -// free page list metadata bucket. -TEST_F(PartitionAllocTest, FreePageListPageTransitions) { - PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; - - size_t numToFillFreeListPage = - kPartitionPageSize / (sizeof(PartitionPage) + kExtraAllocSize); - // The +1 is because we need to account for the fact that the current page - // never gets thrown on the freelist. - ++numToFillFreeListPage; - auto pages = std::make_unique(numToFillFreeListPage); - - size_t i; - for (i = 0; i < numToFillFreeListPage; ++i) { - pages[i] = GetFullPage(kTestAllocSize); - } - EXPECT_EQ(pages[numToFillFreeListPage - 1], bucket->active_pages_head); - for (i = 0; i < numToFillFreeListPage; ++i) - FreeFullPage(pages[i]); - EXPECT_EQ(PartitionPage::get_sentinel_page(), bucket->active_pages_head); - EXPECT_TRUE(bucket->empty_pages_head); - - // Allocate / free in a different bucket size so we get control of a - // different free page list. We need two pages because one will be the last - // active page and not get freed. - PartitionPage* page1 = GetFullPage(kTestAllocSize * 2); - PartitionPage* page2 = GetFullPage(kTestAllocSize * 2); - FreeFullPage(page1); - FreeFullPage(page2); - - for (i = 0; i < numToFillFreeListPage; ++i) { - pages[i] = GetFullPage(kTestAllocSize); - } - EXPECT_EQ(pages[numToFillFreeListPage - 1], bucket->active_pages_head); - - for (i = 0; i < numToFillFreeListPage; ++i) - FreeFullPage(pages[i]); - EXPECT_EQ(PartitionPage::get_sentinel_page(), bucket->active_pages_head); - EXPECT_TRUE(bucket->empty_pages_head); -} - -// Test a large series of allocations that cross more than one underlying -// 64KB super page allocation. -TEST_F(PartitionAllocTest, MultiPageAllocs) { - // This is guaranteed to cross a super page boundary because the first - // partition page "slot" will be taken up by a guard page. - size_t numPagesNeeded = kNumPartitionPagesPerSuperPage; - // The super page should begin and end in a guard so we one less page in - // order to allocate a single page in the new super page. - --numPagesNeeded; - - EXPECT_GT(numPagesNeeded, 1u); - auto pages = std::make_unique(numPagesNeeded); - uintptr_t firstSuperPageBase = 0; - size_t i; - for (i = 0; i < numPagesNeeded; ++i) { - pages[i] = GetFullPage(kTestAllocSize); - void* storagePtr = PartitionPage::ToPointer(pages[i]); - if (!i) - firstSuperPageBase = - reinterpret_cast(storagePtr) & kSuperPageBaseMask; - if (i == numPagesNeeded - 1) { - uintptr_t secondSuperPageBase = - reinterpret_cast(storagePtr) & kSuperPageBaseMask; - uintptr_t secondSuperPageOffset = - reinterpret_cast(storagePtr) & kSuperPageOffsetMask; - EXPECT_FALSE(secondSuperPageBase == firstSuperPageBase); - // Check that we allocated a guard page for the second page. - EXPECT_EQ(kPartitionPageSize, secondSuperPageOffset); - } - } - for (i = 0; i < numPagesNeeded; ++i) - FreeFullPage(pages[i]); -} - -// Test the generic allocation functions that can handle arbitrary sizes and -// reallocing etc. -TEST_F(PartitionAllocTest, GenericAlloc) { - void* ptr = generic_allocator.root()->Alloc(1, type_name); - EXPECT_TRUE(ptr); - generic_allocator.root()->Free(ptr); - ptr = generic_allocator.root()->Alloc(kGenericMaxBucketed + 1, type_name); - EXPECT_TRUE(ptr); - generic_allocator.root()->Free(ptr); - - ptr = generic_allocator.root()->Alloc(1, type_name); - EXPECT_TRUE(ptr); - void* origPtr = ptr; - char* charPtr = static_cast(ptr); - *charPtr = 'A'; - - // Change the size of the realloc, remaining inside the same bucket. - void* newPtr = generic_allocator.root()->Realloc(ptr, 2, type_name); - EXPECT_EQ(ptr, newPtr); - newPtr = generic_allocator.root()->Realloc(ptr, 1, type_name); - EXPECT_EQ(ptr, newPtr); - newPtr = - generic_allocator.root()->Realloc(ptr, kGenericSmallestBucket, type_name); - EXPECT_EQ(ptr, newPtr); - - // Change the size of the realloc, switching buckets. - newPtr = generic_allocator.root()->Realloc(ptr, kGenericSmallestBucket + 1, - type_name); - EXPECT_NE(newPtr, ptr); - // Check that the realloc copied correctly. - char* newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'A'); -#if DCHECK_IS_ON() - // Subtle: this checks for an old bug where we copied too much from the - // source of the realloc. The condition can be detected by a trashing of - // the uninitialized value in the space of the upsized allocation. - EXPECT_EQ(kUninitializedByte, - static_cast(*(newCharPtr + kGenericSmallestBucket))); -#endif - *newCharPtr = 'B'; - // The realloc moved. To check that the old allocation was freed, we can - // do an alloc of the old allocation size and check that the old allocation - // address is at the head of the freelist and reused. - void* reusedPtr = generic_allocator.root()->Alloc(1, type_name); - EXPECT_EQ(reusedPtr, origPtr); - generic_allocator.root()->Free(reusedPtr); - - // Downsize the realloc. - ptr = newPtr; - newPtr = generic_allocator.root()->Realloc(ptr, 1, type_name); - EXPECT_EQ(newPtr, origPtr); - newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'B'); - *newCharPtr = 'C'; - - // Upsize the realloc to outside the partition. - ptr = newPtr; - newPtr = generic_allocator.root()->Realloc(ptr, kGenericMaxBucketed + 1, - type_name); - EXPECT_NE(newPtr, ptr); - newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'C'); - *newCharPtr = 'D'; - - // Upsize and downsize the realloc, remaining outside the partition. - ptr = newPtr; - newPtr = generic_allocator.root()->Realloc(ptr, kGenericMaxBucketed * 10, - type_name); - newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'D'); - *newCharPtr = 'E'; - ptr = newPtr; - newPtr = generic_allocator.root()->Realloc(ptr, kGenericMaxBucketed * 2, - type_name); - newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'E'); - *newCharPtr = 'F'; - - // Downsize the realloc to inside the partition. - ptr = newPtr; - newPtr = generic_allocator.root()->Realloc(ptr, 1, type_name); - EXPECT_NE(newPtr, ptr); - EXPECT_EQ(newPtr, origPtr); - newCharPtr = static_cast(newPtr); - EXPECT_EQ(*newCharPtr, 'F'); - - generic_allocator.root()->Free(newPtr); -} - -// Test the generic allocation functions can handle some specific sizes of -// interest. -TEST_F(PartitionAllocTest, GenericAllocSizes) { - void* ptr = generic_allocator.root()->Alloc(0, type_name); - EXPECT_TRUE(ptr); - generic_allocator.root()->Free(ptr); - - // kPartitionPageSize is interesting because it results in just one - // allocation per page, which tripped up some corner cases. - size_t size = kPartitionPageSize - kExtraAllocSize; - ptr = generic_allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - void* ptr2 = generic_allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr2); - generic_allocator.root()->Free(ptr); - // Should be freeable at this point. - PartitionPage* page = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - EXPECT_NE(-1, page->empty_cache_index); - generic_allocator.root()->Free(ptr2); - - size = (((kPartitionPageSize * kMaxPartitionPagesPerSlotSpan) - - kSystemPageSize) / - 2) - - kExtraAllocSize; - ptr = generic_allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - memset(ptr, 'A', size); - ptr2 = generic_allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr2); - void* ptr3 = generic_allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr3); - void* ptr4 = generic_allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr4); - - page = PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - PartitionPage* page2 = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr3)); - EXPECT_NE(page, page2); - - generic_allocator.root()->Free(ptr); - generic_allocator.root()->Free(ptr3); - generic_allocator.root()->Free(ptr2); - // Should be freeable at this point. - EXPECT_NE(-1, page->empty_cache_index); - EXPECT_EQ(0, page->num_allocated_slots); - EXPECT_EQ(0, page->num_unprovisioned_slots); - void* newPtr = generic_allocator.root()->Alloc(size, type_name); - EXPECT_EQ(ptr3, newPtr); - newPtr = generic_allocator.root()->Alloc(size, type_name); - EXPECT_EQ(ptr2, newPtr); -#if defined(OS_LINUX) && !DCHECK_IS_ON() - // On Linux, we have a guarantee that freelisting a page should cause its - // contents to be nulled out. We check for null here to detect an bug we - // had where a large slot size was causing us to not properly free all - // resources back to the system. - // We only run the check when asserts are disabled because when they are - // enabled, the allocated area is overwritten with an "uninitialized" - // byte pattern. - EXPECT_EQ(0, *(reinterpret_cast(newPtr) + (size - 1))); -#endif - generic_allocator.root()->Free(newPtr); - generic_allocator.root()->Free(ptr3); - generic_allocator.root()->Free(ptr4); - - // Can we allocate a massive (512MB) size? - // Allocate 512MB, but +1, to test for cookie writing alignment issues. - // Test this only if the device has enough memory or it might fail due - // to OOM. - if (IsLargeMemoryDevice()) { - ptr = generic_allocator.root()->Alloc(512 * 1024 * 1024 + 1, type_name); - generic_allocator.root()->Free(ptr); - } - - // Check a more reasonable, but still direct mapped, size. - // Chop a system page and a byte off to test for rounding errors. - size = 20 * 1024 * 1024; - size -= kSystemPageSize; - size -= 1; - ptr = generic_allocator.root()->Alloc(size, type_name); - char* charPtr = reinterpret_cast(ptr); - *(charPtr + (size - 1)) = 'A'; - generic_allocator.root()->Free(ptr); - - // Can we free null? - generic_allocator.root()->Free(nullptr); - - // Do we correctly get a null for a failed allocation? - EXPECT_EQ(nullptr, PartitionAllocGenericFlags( - generic_allocator.root(), PartitionAllocReturnNull, - 3u * 1024 * 1024 * 1024, type_name)); -} - -// Test that we can fetch the real allocated size after an allocation. -TEST_F(PartitionAllocTest, GenericAllocGetSize) { - void* ptr; - size_t requested_size, actual_size, predicted_size; - - EXPECT_TRUE(PartitionAllocSupportsGetSize()); - - // Allocate something small. - requested_size = 511 - kExtraAllocSize; - predicted_size = generic_allocator.root()->ActualSize(requested_size); - ptr = generic_allocator.root()->Alloc(requested_size, type_name); - EXPECT_TRUE(ptr); - actual_size = PartitionAllocGetSize(ptr); - EXPECT_EQ(predicted_size, actual_size); - EXPECT_LT(requested_size, actual_size); - generic_allocator.root()->Free(ptr); - - // Allocate a size that should be a perfect match for a bucket, because it - // is an exact power of 2. - requested_size = (256 * 1024) - kExtraAllocSize; - predicted_size = generic_allocator.root()->ActualSize(requested_size); - ptr = generic_allocator.root()->Alloc(requested_size, type_name); - EXPECT_TRUE(ptr); - actual_size = PartitionAllocGetSize(ptr); - EXPECT_EQ(predicted_size, actual_size); - EXPECT_EQ(requested_size, actual_size); - generic_allocator.root()->Free(ptr); - - // Allocate a size that is a system page smaller than a bucket. GetSize() - // should return a larger size than we asked for now. - size_t num = 64; - while (num * kSystemPageSize >= 1024 * 1024) { - num /= 2; - } - requested_size = num * kSystemPageSize - kSystemPageSize - kExtraAllocSize; - predicted_size = generic_allocator.root()->ActualSize(requested_size); - ptr = generic_allocator.root()->Alloc(requested_size, type_name); - EXPECT_TRUE(ptr); - actual_size = PartitionAllocGetSize(ptr); - EXPECT_EQ(predicted_size, actual_size); - EXPECT_EQ(requested_size + kSystemPageSize, actual_size); - // Check that we can write at the end of the reported size too. - char* charPtr = reinterpret_cast(ptr); - *(charPtr + (actual_size - 1)) = 'A'; - generic_allocator.root()->Free(ptr); - - // Allocate something very large, and uneven. - if (IsLargeMemoryDevice()) { - requested_size = 512 * 1024 * 1024 - 1; - predicted_size = generic_allocator.root()->ActualSize(requested_size); - ptr = generic_allocator.root()->Alloc(requested_size, type_name); - EXPECT_TRUE(ptr); - actual_size = PartitionAllocGetSize(ptr); - EXPECT_EQ(predicted_size, actual_size); - EXPECT_LT(requested_size, actual_size); - generic_allocator.root()->Free(ptr); - } - - // Too large allocation. - requested_size = kGenericMaxDirectMapped + 1; - predicted_size = generic_allocator.root()->ActualSize(requested_size); - EXPECT_EQ(requested_size, predicted_size); -} - -// Test the realloc() contract. -TEST_F(PartitionAllocTest, Realloc) { - // realloc(0, size) should be equivalent to malloc(). - void* ptr = - generic_allocator.root()->Realloc(nullptr, kTestAllocSize, type_name); - memset(ptr, 'A', kTestAllocSize); - PartitionPage* page = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - // realloc(ptr, 0) should be equivalent to free(). - void* ptr2 = generic_allocator.root()->Realloc(ptr, 0, type_name); - EXPECT_EQ(nullptr, ptr2); - EXPECT_EQ(PartitionCookieFreePointerAdjust(ptr), page->freelist_head); - - // Test that growing an allocation with realloc() copies everything from the - // old allocation. - size_t size = kSystemPageSize - kExtraAllocSize; - EXPECT_EQ(size, generic_allocator.root()->ActualSize(size)); - ptr = generic_allocator.root()->Alloc(size, type_name); - memset(ptr, 'A', size); - ptr2 = generic_allocator.root()->Realloc(ptr, size + 1, type_name); - EXPECT_NE(ptr, ptr2); - char* charPtr2 = static_cast(ptr2); - EXPECT_EQ('A', charPtr2[0]); - EXPECT_EQ('A', charPtr2[size - 1]); -#if DCHECK_IS_ON() - EXPECT_EQ(kUninitializedByte, static_cast(charPtr2[size])); -#endif - - // Test that shrinking an allocation with realloc() also copies everything - // from the old allocation. - ptr = generic_allocator.root()->Realloc(ptr2, size - 1, type_name); - EXPECT_NE(ptr2, ptr); - char* charPtr = static_cast(ptr); - EXPECT_EQ('A', charPtr[0]); - EXPECT_EQ('A', charPtr[size - 2]); -#if DCHECK_IS_ON() - EXPECT_EQ(kUninitializedByte, static_cast(charPtr[size - 1])); -#endif - - generic_allocator.root()->Free(ptr); - - // Test that shrinking a direct mapped allocation happens in-place. - size = kGenericMaxBucketed + 16 * kSystemPageSize; - ptr = generic_allocator.root()->Alloc(size, type_name); - size_t actual_size = PartitionAllocGetSize(ptr); - ptr2 = generic_allocator.root()->Realloc( - ptr, kGenericMaxBucketed + 8 * kSystemPageSize, type_name); - EXPECT_EQ(ptr, ptr2); - EXPECT_EQ(actual_size - 8 * kSystemPageSize, PartitionAllocGetSize(ptr2)); - - // Test that a previously in-place shrunk direct mapped allocation can be - // expanded up again within its original size. - ptr = generic_allocator.root()->Realloc(ptr2, size - kSystemPageSize, - type_name); - EXPECT_EQ(ptr2, ptr); - EXPECT_EQ(actual_size - kSystemPageSize, PartitionAllocGetSize(ptr)); - - // Test that a direct mapped allocation is performed not in-place when the - // new size is small enough. - ptr2 = generic_allocator.root()->Realloc(ptr, kSystemPageSize, type_name); - EXPECT_NE(ptr, ptr2); - - generic_allocator.root()->Free(ptr2); -} - -// Tests the handing out of freelists for partial pages. -TEST_F(PartitionAllocTest, PartialPageFreelists) { - size_t big_size = allocator.root()->max_allocation - kExtraAllocSize; - EXPECT_EQ(kSystemPageSize - kAllocationGranularity, - big_size + kExtraAllocSize); - size_t bucket_index = (big_size + kExtraAllocSize) >> kBucketShift; - PartitionBucket* bucket = &allocator.root()->buckets()[bucket_index]; - EXPECT_EQ(nullptr, bucket->empty_pages_head); - - void* ptr = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr); - - PartitionPage* page = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - size_t totalSlots = - (page->bucket->num_system_pages_per_slot_span * kSystemPageSize) / - (big_size + kExtraAllocSize); - EXPECT_EQ(4u, totalSlots); - // The freelist should have one entry, because we were able to exactly fit - // one object slot and one freelist pointer (the null that the head points - // to) into a system page. - EXPECT_TRUE(page->freelist_head); - EXPECT_EQ(1, page->num_allocated_slots); - EXPECT_EQ(2, page->num_unprovisioned_slots); - - void* ptr2 = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr2); - EXPECT_FALSE(page->freelist_head); - EXPECT_EQ(2, page->num_allocated_slots); - EXPECT_EQ(2, page->num_unprovisioned_slots); - - void* ptr3 = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr3); - EXPECT_TRUE(page->freelist_head); - EXPECT_EQ(3, page->num_allocated_slots); - EXPECT_EQ(0, page->num_unprovisioned_slots); - - void* ptr4 = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr4); - EXPECT_FALSE(page->freelist_head); - EXPECT_EQ(4, page->num_allocated_slots); - EXPECT_EQ(0, page->num_unprovisioned_slots); - - void* ptr5 = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr5); - - PartitionPage* page2 = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr5)); - EXPECT_EQ(1, page2->num_allocated_slots); - - // Churn things a little whilst there's a partial page freelist. - PartitionFree(ptr); - ptr = allocator.root()->Alloc(big_size, type_name); - void* ptr6 = allocator.root()->Alloc(big_size, type_name); - - PartitionFree(ptr); - PartitionFree(ptr2); - PartitionFree(ptr3); - PartitionFree(ptr4); - PartitionFree(ptr5); - PartitionFree(ptr6); - EXPECT_NE(-1, page->empty_cache_index); - EXPECT_NE(-1, page2->empty_cache_index); - EXPECT_TRUE(page2->freelist_head); - EXPECT_EQ(0, page2->num_allocated_slots); - - // And test a couple of sizes that do not cross kSystemPageSize with a single - // allocation. - size_t mediumSize = (kSystemPageSize / 2) - kExtraAllocSize; - bucket_index = (mediumSize + kExtraAllocSize) >> kBucketShift; - bucket = &allocator.root()->buckets()[bucket_index]; - EXPECT_EQ(nullptr, bucket->empty_pages_head); - - ptr = allocator.root()->Alloc(mediumSize, type_name); - EXPECT_TRUE(ptr); - page = PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->num_allocated_slots); - totalSlots = - (page->bucket->num_system_pages_per_slot_span * kSystemPageSize) / - (mediumSize + kExtraAllocSize); - size_t firstPageSlots = kSystemPageSize / (mediumSize + kExtraAllocSize); - EXPECT_EQ(2u, firstPageSlots); - EXPECT_EQ(totalSlots - firstPageSlots, page->num_unprovisioned_slots); - - PartitionFree(ptr); - - size_t smallSize = (kSystemPageSize / 4) - kExtraAllocSize; - bucket_index = (smallSize + kExtraAllocSize) >> kBucketShift; - bucket = &allocator.root()->buckets()[bucket_index]; - EXPECT_EQ(nullptr, bucket->empty_pages_head); - - ptr = allocator.root()->Alloc(smallSize, type_name); - EXPECT_TRUE(ptr); - page = PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->num_allocated_slots); - totalSlots = - (page->bucket->num_system_pages_per_slot_span * kSystemPageSize) / - (smallSize + kExtraAllocSize); - firstPageSlots = kSystemPageSize / (smallSize + kExtraAllocSize); - EXPECT_EQ(totalSlots - firstPageSlots, page->num_unprovisioned_slots); - - PartitionFree(ptr); - EXPECT_TRUE(page->freelist_head); - EXPECT_EQ(0, page->num_allocated_slots); - - size_t verySmallSize = 32 - kExtraAllocSize; - bucket_index = (verySmallSize + kExtraAllocSize) >> kBucketShift; - bucket = &allocator.root()->buckets()[bucket_index]; - EXPECT_EQ(nullptr, bucket->empty_pages_head); - - ptr = allocator.root()->Alloc(verySmallSize, type_name); - EXPECT_TRUE(ptr); - page = PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->num_allocated_slots); - totalSlots = - (page->bucket->num_system_pages_per_slot_span * kSystemPageSize) / - (verySmallSize + kExtraAllocSize); - firstPageSlots = kSystemPageSize / (verySmallSize + kExtraAllocSize); - EXPECT_EQ(totalSlots - firstPageSlots, page->num_unprovisioned_slots); - - PartitionFree(ptr); - EXPECT_TRUE(page->freelist_head); - EXPECT_EQ(0, page->num_allocated_slots); - - // And try an allocation size (against the generic allocator) that is - // larger than a system page. - size_t pageAndAHalfSize = - (kSystemPageSize + (kSystemPageSize / 2)) - kExtraAllocSize; - ptr = generic_allocator.root()->Alloc(pageAndAHalfSize, type_name); - EXPECT_TRUE(ptr); - page = PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->num_allocated_slots); - EXPECT_TRUE(page->freelist_head); - totalSlots = - (page->bucket->num_system_pages_per_slot_span * kSystemPageSize) / - (pageAndAHalfSize + kExtraAllocSize); - EXPECT_EQ(totalSlots - 2, page->num_unprovisioned_slots); - generic_allocator.root()->Free(ptr); - - // And then make sure than exactly the page size only faults one page. - size_t pageSize = kSystemPageSize - kExtraAllocSize; - ptr = generic_allocator.root()->Alloc(pageSize, type_name); - EXPECT_TRUE(ptr); - page = PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->num_allocated_slots); - EXPECT_FALSE(page->freelist_head); - totalSlots = - (page->bucket->num_system_pages_per_slot_span * kSystemPageSize) / - (pageSize + kExtraAllocSize); - EXPECT_EQ(totalSlots - 1, page->num_unprovisioned_slots); - generic_allocator.root()->Free(ptr); -} - -// Test some of the fragmentation-resistant properties of the allocator. -TEST_F(PartitionAllocTest, PageRefilling) { - PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex]; - - // Grab two full pages and a non-full page. - PartitionPage* page1 = GetFullPage(kTestAllocSize); - PartitionPage* page2 = GetFullPage(kTestAllocSize); - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - EXPECT_NE(page1, bucket->active_pages_head); - EXPECT_NE(page2, bucket->active_pages_head); - PartitionPage* page = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(1, page->num_allocated_slots); - - // Work out a pointer into page2 and free it; and then page1 and free it. - char* ptr2 = - reinterpret_cast(PartitionPage::ToPointer(page1)) + kPointerOffset; - PartitionFree(ptr2); - ptr2 = - reinterpret_cast(PartitionPage::ToPointer(page2)) + kPointerOffset; - PartitionFree(ptr2); - - // If we perform two allocations from the same bucket now, we expect to - // refill both the nearly full pages. - (void)allocator.root()->Alloc(kTestAllocSize, type_name); - (void)allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_EQ(1, page->num_allocated_slots); - - FreeFullPage(page2); - FreeFullPage(page1); - PartitionFree(ptr); -} - -// Basic tests to ensure that allocations work for partial page buckets. -TEST_F(PartitionAllocTest, PartialPages) { - // Find a size that is backed by a partial partition page. - size_t size = sizeof(void*); - PartitionBucket* bucket = nullptr; - while (size < kTestMaxAllocation) { - bucket = &allocator.root()->buckets()[size >> kBucketShift]; - if (bucket->num_system_pages_per_slot_span % - kNumSystemPagesPerPartitionPage) - break; - size += sizeof(void*); - } - EXPECT_LT(size, kTestMaxAllocation); - - PartitionPage* page1 = GetFullPage(size); - PartitionPage* page2 = GetFullPage(size); - FreeFullPage(page2); - FreeFullPage(page1); -} - -// Test correct handling if our mapping collides with another. -TEST_F(PartitionAllocTest, MappingCollision) { - // The -2 is because the first and last partition pages in a super page are - // guard pages. - size_t numPartitionPagesNeeded = kNumPartitionPagesPerSuperPage - 2; - auto firstSuperPagePages = - std::make_unique(numPartitionPagesNeeded); - auto secondSuperPagePages = - std::make_unique(numPartitionPagesNeeded); - - size_t i; - for (i = 0; i < numPartitionPagesNeeded; ++i) - firstSuperPagePages[i] = GetFullPage(kTestAllocSize); - - char* pageBase = - reinterpret_cast(PartitionPage::ToPointer(firstSuperPagePages[0])); - EXPECT_EQ(kPartitionPageSize, - reinterpret_cast(pageBase) & kSuperPageOffsetMask); - pageBase -= kPartitionPageSize; - // Map a single system page either side of the mapping for our allocations, - // with the goal of tripping up alignment of the next mapping. - void* map1 = AllocPages(pageBase - kPageAllocationGranularity, - kPageAllocationGranularity, - kPageAllocationGranularity, PageInaccessible); - EXPECT_TRUE(map1); - void* map2 = AllocPages(pageBase + kSuperPageSize, kPageAllocationGranularity, - kPageAllocationGranularity, PageInaccessible); - EXPECT_TRUE(map2); - - for (i = 0; i < numPartitionPagesNeeded; ++i) - secondSuperPagePages[i] = GetFullPage(kTestAllocSize); - - FreePages(map1, kPageAllocationGranularity); - FreePages(map2, kPageAllocationGranularity); - - pageBase = reinterpret_cast( - PartitionPage::ToPointer(secondSuperPagePages[0])); - EXPECT_EQ(kPartitionPageSize, - reinterpret_cast(pageBase) & kSuperPageOffsetMask); - pageBase -= kPartitionPageSize; - // Map a single system page either side of the mapping for our allocations, - // with the goal of tripping up alignment of the next mapping. - map1 = AllocPages(pageBase - kPageAllocationGranularity, - kPageAllocationGranularity, kPageAllocationGranularity, - PageReadWrite); - EXPECT_TRUE(map1); - map2 = AllocPages(pageBase + kSuperPageSize, kPageAllocationGranularity, - kPageAllocationGranularity, PageReadWrite); - EXPECT_TRUE(map2); - EXPECT_TRUE( - SetSystemPagesAccess(map1, kPageAllocationGranularity, PageInaccessible)); - EXPECT_TRUE( - SetSystemPagesAccess(map2, kPageAllocationGranularity, PageInaccessible)); - - PartitionPage* pageInThirdSuperPage = GetFullPage(kTestAllocSize); - FreePages(map1, kPageAllocationGranularity); - FreePages(map2, kPageAllocationGranularity); - - EXPECT_EQ(0u, reinterpret_cast( - PartitionPage::ToPointer(pageInThirdSuperPage)) & - kPartitionPageOffsetMask); - - // And make sure we really did get a page in a new superpage. - EXPECT_NE(reinterpret_cast( - PartitionPage::ToPointer(firstSuperPagePages[0])) & - kSuperPageBaseMask, - reinterpret_cast( - PartitionPage::ToPointer(pageInThirdSuperPage)) & - kSuperPageBaseMask); - EXPECT_NE(reinterpret_cast( - PartitionPage::ToPointer(secondSuperPagePages[0])) & - kSuperPageBaseMask, - reinterpret_cast( - PartitionPage::ToPointer(pageInThirdSuperPage)) & - kSuperPageBaseMask); - - FreeFullPage(pageInThirdSuperPage); - for (i = 0; i < numPartitionPagesNeeded; ++i) { - FreeFullPage(firstSuperPagePages[i]); - FreeFullPage(secondSuperPagePages[i]); - } -} - -// Tests that pages in the free page cache do get freed as appropriate. -TEST_F(PartitionAllocTest, FreeCache) { - EXPECT_EQ(0U, allocator.root()->total_size_of_committed_pages); - - size_t big_size = allocator.root()->max_allocation - kExtraAllocSize; - size_t bucket_index = (big_size + kExtraAllocSize) >> kBucketShift; - PartitionBucket* bucket = &allocator.root()->buckets()[bucket_index]; - - void* ptr = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(ptr); - PartitionPage* page = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - EXPECT_EQ(nullptr, bucket->empty_pages_head); - EXPECT_EQ(1, page->num_allocated_slots); - EXPECT_EQ(kPartitionPageSize, - allocator.root()->total_size_of_committed_pages); - PartitionFree(ptr); - EXPECT_EQ(0, page->num_allocated_slots); - EXPECT_NE(-1, page->empty_cache_index); - EXPECT_TRUE(page->freelist_head); - - CycleFreeCache(kTestAllocSize); - - // Flushing the cache should have really freed the unused page. - EXPECT_FALSE(page->freelist_head); - EXPECT_EQ(-1, page->empty_cache_index); - EXPECT_EQ(0, page->num_allocated_slots); - PartitionBucket* cycle_free_cache_bucket = - &allocator.root()->buckets()[kTestBucketIndex]; - EXPECT_EQ( - cycle_free_cache_bucket->num_system_pages_per_slot_span * kSystemPageSize, - allocator.root()->total_size_of_committed_pages); - - // Check that an allocation works ok whilst in this state (a free'd page - // as the active pages head). - ptr = allocator.root()->Alloc(big_size, type_name); - EXPECT_FALSE(bucket->empty_pages_head); - PartitionFree(ptr); - - // Also check that a page that is bouncing immediately between empty and - // used does not get freed. - for (size_t i = 0; i < kMaxFreeableSpans * 2; ++i) { - ptr = allocator.root()->Alloc(big_size, type_name); - EXPECT_TRUE(page->freelist_head); - PartitionFree(ptr); - EXPECT_TRUE(page->freelist_head); - } - EXPECT_EQ(kPartitionPageSize, - allocator.root()->total_size_of_committed_pages); -} - -// Tests for a bug we had with losing references to free pages. -TEST_F(PartitionAllocTest, LostFreePagesBug) { - size_t size = kPartitionPageSize - kExtraAllocSize; - - void* ptr = generic_allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - void* ptr2 = generic_allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr2); - - PartitionPage* page = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr)); - PartitionPage* page2 = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr2)); - PartitionBucket* bucket = page->bucket; - - EXPECT_EQ(nullptr, bucket->empty_pages_head); - EXPECT_EQ(-1, page->num_allocated_slots); - EXPECT_EQ(1, page2->num_allocated_slots); - - generic_allocator.root()->Free(ptr); - generic_allocator.root()->Free(ptr2); - - EXPECT_TRUE(bucket->empty_pages_head); - EXPECT_TRUE(bucket->empty_pages_head->next_page); - EXPECT_EQ(0, page->num_allocated_slots); - EXPECT_EQ(0, page2->num_allocated_slots); - EXPECT_TRUE(page->freelist_head); - EXPECT_TRUE(page2->freelist_head); - - CycleGenericFreeCache(kTestAllocSize); - - EXPECT_FALSE(page->freelist_head); - EXPECT_FALSE(page2->freelist_head); - - EXPECT_TRUE(bucket->empty_pages_head); - EXPECT_TRUE(bucket->empty_pages_head->next_page); - EXPECT_EQ(PartitionPage::get_sentinel_page(), bucket->active_pages_head); - - // At this moment, we have two decommitted pages, on the empty list. - ptr = generic_allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - generic_allocator.root()->Free(ptr); - - EXPECT_EQ(PartitionPage::get_sentinel_page(), bucket->active_pages_head); - EXPECT_TRUE(bucket->empty_pages_head); - EXPECT_TRUE(bucket->decommitted_pages_head); - - CycleGenericFreeCache(kTestAllocSize); - - // We're now set up to trigger a historical bug by scanning over the active - // pages list. The current code gets into a different state, but we'll keep - // the test as being an interesting corner case. - ptr = generic_allocator.root()->Alloc(size, type_name); - EXPECT_TRUE(ptr); - generic_allocator.root()->Free(ptr); - - EXPECT_TRUE(bucket->active_pages_head); - EXPECT_TRUE(bucket->empty_pages_head); - EXPECT_TRUE(bucket->decommitted_pages_head); -} - -// Death tests misbehave on Android, http://crbug.com/643760. -#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) - -// Unit tests that check if an allocation fails in "return null" mode, -// repeating it doesn't crash, and still returns null. The tests need to -// stress memory subsystem limits to do so, hence they try to allocate -// 6 GB of memory, each with a different per-allocation block sizes. -// -// On 64-bit systems we need to restrict the address space to force allocation -// failure, so these tests run only on POSIX systems that provide setrlimit(), -// and use it to limit address space to 6GB. -// -// Disable these tests on Android because, due to the allocation-heavy behavior, -// they tend to get OOM-killed rather than pass. -// TODO(https://crbug.com/779645): Fuchsia currently sets OS_POSIX, but does -// not provide a working setrlimit(). -// -// Disable these test on Windows, since they run slower, so tend to timout and -// cause flake. -#if !defined(OS_WIN) && \ - (!defined(ARCH_CPU_64_BITS) || \ - (defined(OS_POSIX) && \ - !(defined(OS_FUCHSIA) || defined(OS_MACOSX) || defined(OS_ANDROID)))) - -// The following four tests wrap a called function in an expect death statement -// to perform their test, because they are non-hermetic. Specifically they are -// going to attempt to exhaust the allocatable memory, which leaves the -// allocator in a bad global state. -// Performing them as death tests causes them to be forked into their own -// process, so they won't pollute other tests. -TEST_F(PartitionAllocDeathTest, RepeatedAllocReturnNullDirect) { - // A direct-mapped allocation size. - EXPECT_DEATH(DoReturnNullTest(32 * 1024 * 1024, false), "DoReturnNullTest"); -} - -// Repeating above test with Realloc -TEST_F(PartitionAllocDeathTest, RepeatedReallocReturnNullDirect) { - EXPECT_DEATH(DoReturnNullTest(32 * 1024 * 1024, true), "DoReturnNullTest"); -} - -// Test "return null" with a 512 kB block size. -TEST_F(PartitionAllocDeathTest, RepeatedAllocReturnNull) { - // A single-slot but non-direct-mapped allocation size. - EXPECT_DEATH(DoReturnNullTest(512 * 1024, false), "DoReturnNullTest"); -} - -// Repeating above test with Realloc. -TEST_F(PartitionAllocDeathTest, RepeatedReallocReturnNull) { - EXPECT_DEATH(DoReturnNullTest(512 * 1024, true), "DoReturnNullTest"); -} - -#endif // !defined(ARCH_CPU_64_BITS) || (defined(OS_POSIX) && - // !(defined(OS_FUCHSIA) || defined(OS_MACOSX) || defined(OS_ANDROID))) - -// Make sure that malloc(-1) dies. -// In the past, we had an integer overflow that would alias malloc(-1) to -// malloc(0), which is not good. -TEST_F(PartitionAllocDeathTest, LargeAllocs) { - // Largest alloc. - EXPECT_DEATH( - generic_allocator.root()->Alloc(static_cast(-1), type_name), ""); - // And the smallest allocation we expect to die. - EXPECT_DEATH( - generic_allocator.root()->Alloc(kGenericMaxDirectMapped + 1, type_name), - ""); -} - -// Check that our immediate double-free detection works. -TEST_F(PartitionAllocDeathTest, ImmediateDoubleFree) { - void* ptr = generic_allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - generic_allocator.root()->Free(ptr); - - EXPECT_DEATH(generic_allocator.root()->Free(ptr), ""); -} - -// Check that our refcount-based double-free detection works. -TEST_F(PartitionAllocDeathTest, RefcountDoubleFree) { - void* ptr = generic_allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr); - void* ptr2 = generic_allocator.root()->Alloc(kTestAllocSize, type_name); - EXPECT_TRUE(ptr2); - generic_allocator.root()->Free(ptr); - generic_allocator.root()->Free(ptr2); - // This is not an immediate double-free so our immediate detection won't - // fire. However, it does take the "refcount" of the partition page to -1, - // which is illegal and should be trapped. - EXPECT_DEATH(generic_allocator.root()->Free(ptr), ""); -} - -// Check that guard pages are present where expected. -TEST_F(PartitionAllocDeathTest, GuardPages) { -// PartitionAlloc adds kPartitionPageSize to the requested size -// (for metadata), and then rounds that size to kPageAllocationGranularity. -// To be able to reliably write one past a direct allocation, choose a size -// that's -// a) larger than kGenericMaxBucketed (to make the allocation direct) -// b) aligned at kPageAllocationGranularity boundaries after -// kPartitionPageSize has been added to it. -// (On 32-bit, PartitionAlloc adds another kSystemPageSize to the -// allocation size before rounding, but there it marks the memory right -// after size as inaccessible, so it's fine to write 1 past the size we -// hand to PartitionAlloc and we don't need to worry about allocation -// granularities.) -#define ALIGN(N, A) (((N) + (A)-1) / (A) * (A)) - const int kSize = ALIGN(kGenericMaxBucketed + 1 + kPartitionPageSize, - kPageAllocationGranularity) - - kPartitionPageSize; -#undef ALIGN - static_assert(kSize > kGenericMaxBucketed, - "allocation not large enough for direct allocation"); - size_t size = kSize - kExtraAllocSize; - void* ptr = generic_allocator.root()->Alloc(size, type_name); - - EXPECT_TRUE(ptr); - char* charPtr = reinterpret_cast(ptr) - kPointerOffset; - - EXPECT_DEATH(*(charPtr - 1) = 'A', ""); - EXPECT_DEATH(*(charPtr + size + kExtraAllocSize) = 'A', ""); - - generic_allocator.root()->Free(ptr); -} - -// Check that a bad free() is caught where the free() refers to an unused -// partition page of a large allocation. -TEST_F(PartitionAllocDeathTest, FreeWrongPartitionPage) { - // This large size will result in a direct mapped allocation with guard - // pages at either end. - void* ptr = - generic_allocator.root()->Alloc(kPartitionPageSize * 2, type_name); - EXPECT_TRUE(ptr); - char* badPtr = reinterpret_cast(ptr) + kPartitionPageSize; - - EXPECT_DEATH(generic_allocator.root()->Free(badPtr), ""); - - generic_allocator.root()->Free(ptr); -} - -#endif // !defined(OS_ANDROID) && !defined(OS_IOS) - -// Tests that |PartitionDumpStatsGeneric| and |PartitionDumpStats| run without -// crashing and return non-zero values when memory is allocated. -TEST_F(PartitionAllocTest, DumpMemoryStats) { - { - void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name); - MockPartitionStatsDumper mockStatsDumper; - allocator.root()->DumpStats("mock_allocator", false /* detailed dump */, - &mockStatsDumper); - EXPECT_TRUE(mockStatsDumper.IsMemoryAllocationRecorded()); - PartitionFree(ptr); - } - - // This series of tests checks the active -> empty -> decommitted states. - { - { - void* ptr = - generic_allocator.root()->Alloc(2048 - kExtraAllocSize, type_name); - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = dumper.GetBucketStats(2048); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(2048u, stats->bucket_slot_size); - EXPECT_EQ(2048u, stats->active_bytes); - EXPECT_EQ(kSystemPageSize, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(0u, stats->num_full_pages); - EXPECT_EQ(1u, stats->num_active_pages); - EXPECT_EQ(0u, stats->num_empty_pages); - EXPECT_EQ(0u, stats->num_decommitted_pages); - generic_allocator.root()->Free(ptr); - } - - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_FALSE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = dumper.GetBucketStats(2048); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(2048u, stats->bucket_slot_size); - EXPECT_EQ(0u, stats->active_bytes); - EXPECT_EQ(kSystemPageSize, stats->resident_bytes); - EXPECT_EQ(kSystemPageSize, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(0u, stats->num_full_pages); - EXPECT_EQ(0u, stats->num_active_pages); - EXPECT_EQ(1u, stats->num_empty_pages); - EXPECT_EQ(0u, stats->num_decommitted_pages); - } - - // TODO(crbug.com/722911): Commenting this out causes this test to fail when - // run singly (--gtest_filter=PartitionAllocTest.DumpMemoryStats), but not - // when run with the others (--gtest_filter=PartitionAllocTest.*). - CycleGenericFreeCache(kTestAllocSize); - - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_FALSE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = dumper.GetBucketStats(2048); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(2048u, stats->bucket_slot_size); - EXPECT_EQ(0u, stats->active_bytes); - EXPECT_EQ(0u, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(0u, stats->num_full_pages); - EXPECT_EQ(0u, stats->num_active_pages); - EXPECT_EQ(0u, stats->num_empty_pages); - EXPECT_EQ(1u, stats->num_decommitted_pages); - } - } - - // This test checks for correct empty page list accounting. - { - size_t size = kPartitionPageSize - kExtraAllocSize; - void* ptr1 = generic_allocator.root()->Alloc(size, type_name); - void* ptr2 = generic_allocator.root()->Alloc(size, type_name); - generic_allocator.root()->Free(ptr1); - generic_allocator.root()->Free(ptr2); - - CycleGenericFreeCache(kTestAllocSize); - - ptr1 = generic_allocator.root()->Alloc(size, type_name); - - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(kPartitionPageSize); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(kPartitionPageSize, stats->bucket_slot_size); - EXPECT_EQ(kPartitionPageSize, stats->active_bytes); - EXPECT_EQ(kPartitionPageSize, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(1u, stats->num_full_pages); - EXPECT_EQ(0u, stats->num_active_pages); - EXPECT_EQ(0u, stats->num_empty_pages); - EXPECT_EQ(1u, stats->num_decommitted_pages); - } - generic_allocator.root()->Free(ptr1); - } - - // This test checks for correct direct mapped accounting. - { - size_t size_smaller = kGenericMaxBucketed + 1; - size_t size_bigger = (kGenericMaxBucketed * 2) + 1; - size_t real_size_smaller = - (size_smaller + kSystemPageOffsetMask) & kSystemPageBaseMask; - size_t real_size_bigger = - (size_bigger + kSystemPageOffsetMask) & kSystemPageBaseMask; - void* ptr = generic_allocator.root()->Alloc(size_smaller, type_name); - void* ptr2 = generic_allocator.root()->Alloc(size_bigger, type_name); - - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(real_size_smaller); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_TRUE(stats->is_direct_map); - EXPECT_EQ(real_size_smaller, stats->bucket_slot_size); - EXPECT_EQ(real_size_smaller, stats->active_bytes); - EXPECT_EQ(real_size_smaller, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(1u, stats->num_full_pages); - EXPECT_EQ(0u, stats->num_active_pages); - EXPECT_EQ(0u, stats->num_empty_pages); - EXPECT_EQ(0u, stats->num_decommitted_pages); - - stats = dumper.GetBucketStats(real_size_bigger); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_TRUE(stats->is_direct_map); - EXPECT_EQ(real_size_bigger, stats->bucket_slot_size); - EXPECT_EQ(real_size_bigger, stats->active_bytes); - EXPECT_EQ(real_size_bigger, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(1u, stats->num_full_pages); - EXPECT_EQ(0u, stats->num_active_pages); - EXPECT_EQ(0u, stats->num_empty_pages); - EXPECT_EQ(0u, stats->num_decommitted_pages); - } - - generic_allocator.root()->Free(ptr2); - generic_allocator.root()->Free(ptr); - - // Whilst we're here, allocate again and free with different ordering to - // give a workout to our linked list code. - ptr = generic_allocator.root()->Alloc(size_smaller, type_name); - ptr2 = generic_allocator.root()->Alloc(size_bigger, type_name); - generic_allocator.root()->Free(ptr); - generic_allocator.root()->Free(ptr2); - } - - // This test checks large-but-not-quite-direct allocations. - { - constexpr size_t requested_size = 16 * kSystemPageSize; - void* ptr = generic_allocator.root()->Alloc(requested_size + 1, type_name); - - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - size_t slot_size = - requested_size + (requested_size / kGenericNumBucketsPerOrder); - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(slot_size); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_FALSE(stats->is_direct_map); - EXPECT_EQ(slot_size, stats->bucket_slot_size); - EXPECT_EQ(requested_size + 1 + kExtraAllocSize, stats->active_bytes); - EXPECT_EQ(slot_size, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(kSystemPageSize, stats->discardable_bytes); - EXPECT_EQ(1u, stats->num_full_pages); - EXPECT_EQ(0u, stats->num_active_pages); - EXPECT_EQ(0u, stats->num_empty_pages); - EXPECT_EQ(0u, stats->num_decommitted_pages); - } - - generic_allocator.root()->Free(ptr); - - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_FALSE(dumper.IsMemoryAllocationRecorded()); - - size_t slot_size = - requested_size + (requested_size / kGenericNumBucketsPerOrder); - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(slot_size); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_FALSE(stats->is_direct_map); - EXPECT_EQ(slot_size, stats->bucket_slot_size); - EXPECT_EQ(0u, stats->active_bytes); - EXPECT_EQ(slot_size, stats->resident_bytes); - EXPECT_EQ(slot_size, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->num_full_pages); - EXPECT_EQ(0u, stats->num_active_pages); - EXPECT_EQ(1u, stats->num_empty_pages); - EXPECT_EQ(0u, stats->num_decommitted_pages); - } - - void* ptr2 = generic_allocator.root()->Alloc( - requested_size + kSystemPageSize + 1, type_name); - EXPECT_EQ(ptr, ptr2); - - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - size_t slot_size = - requested_size + (requested_size / kGenericNumBucketsPerOrder); - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(slot_size); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_FALSE(stats->is_direct_map); - EXPECT_EQ(slot_size, stats->bucket_slot_size); - EXPECT_EQ(requested_size + kSystemPageSize + 1 + kExtraAllocSize, - stats->active_bytes); - EXPECT_EQ(slot_size, stats->resident_bytes); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->discardable_bytes); - EXPECT_EQ(1u, stats->num_full_pages); - EXPECT_EQ(0u, stats->num_active_pages); - EXPECT_EQ(0u, stats->num_empty_pages); - EXPECT_EQ(0u, stats->num_decommitted_pages); - } - - generic_allocator.root()->Free(ptr2); - } -} - -// Tests the API to purge freeable memory. -TEST_F(PartitionAllocTest, Purge) { - char* ptr = reinterpret_cast( - generic_allocator.root()->Alloc(2048 - kExtraAllocSize, type_name)); - generic_allocator.root()->Free(ptr); - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_FALSE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = dumper.GetBucketStats(2048); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(kSystemPageSize, stats->decommittable_bytes); - EXPECT_EQ(kSystemPageSize, stats->resident_bytes); - } - generic_allocator.root()->PurgeMemory(PartitionPurgeDecommitEmptyPages); - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_FALSE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = dumper.GetBucketStats(2048); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(0u, stats->resident_bytes); - } - // Calling purge again here is a good way of testing we didn't mess up the - // state of the free cache ring. - generic_allocator.root()->PurgeMemory(PartitionPurgeDecommitEmptyPages); - - char* bigPtr = reinterpret_cast( - generic_allocator.root()->Alloc(256 * 1024, type_name)); - generic_allocator.root()->Free(bigPtr); - generic_allocator.root()->PurgeMemory(PartitionPurgeDecommitEmptyPages); - - CHECK_PAGE_IN_CORE(ptr - kPointerOffset, false); - CHECK_PAGE_IN_CORE(bigPtr - kPointerOffset, false); -} - -// Tests that we prefer to allocate into a non-empty partition page over an -// empty one. This is an important aspect of minimizing memory usage for some -// allocation sizes, particularly larger ones. -TEST_F(PartitionAllocTest, PreferActiveOverEmpty) { - size_t size = (kSystemPageSize * 2) - kExtraAllocSize; - // Allocate 3 full slot spans worth of 8192-byte allocations. - // Each slot span for this size is 16384 bytes, or 1 partition page and 2 - // slots. - void* ptr1 = generic_allocator.root()->Alloc(size, type_name); - void* ptr2 = generic_allocator.root()->Alloc(size, type_name); - void* ptr3 = generic_allocator.root()->Alloc(size, type_name); - void* ptr4 = generic_allocator.root()->Alloc(size, type_name); - void* ptr5 = generic_allocator.root()->Alloc(size, type_name); - void* ptr6 = generic_allocator.root()->Alloc(size, type_name); - - PartitionPage* page1 = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr1)); - PartitionPage* page2 = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr3)); - PartitionPage* page3 = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr6)); - EXPECT_NE(page1, page2); - EXPECT_NE(page2, page3); - PartitionBucket* bucket = page1->bucket; - EXPECT_EQ(page3, bucket->active_pages_head); - - // Free up the 2nd slot in each slot span. - // This leaves the active list containing 3 pages, each with 1 used and 1 - // free slot. The active page will be the one containing ptr1. - generic_allocator.root()->Free(ptr6); - generic_allocator.root()->Free(ptr4); - generic_allocator.root()->Free(ptr2); - EXPECT_EQ(page1, bucket->active_pages_head); - - // Empty the middle page in the active list. - generic_allocator.root()->Free(ptr3); - EXPECT_EQ(page1, bucket->active_pages_head); - - // Empty the the first page in the active list -- also the current page. - generic_allocator.root()->Free(ptr1); - - // A good choice here is to re-fill the third page since the first two are - // empty. We used to fail that. - void* ptr7 = generic_allocator.root()->Alloc(size, type_name); - EXPECT_EQ(ptr6, ptr7); - EXPECT_EQ(page3, bucket->active_pages_head); - - generic_allocator.root()->Free(ptr5); - generic_allocator.root()->Free(ptr7); -} - -// Tests the API to purge discardable memory. -TEST_F(PartitionAllocTest, PurgeDiscardable) { - // Free the second of two 4096 byte allocations and then purge. - { - void* ptr1 = generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name); - char* ptr2 = reinterpret_cast(generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name)); - generic_allocator.root()->Free(ptr2); - PartitionPage* page = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr1)); - EXPECT_EQ(2u, page->num_unprovisioned_slots); - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(kSystemPageSize); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(kSystemPageSize, stats->discardable_bytes); - EXPECT_EQ(kSystemPageSize, stats->active_bytes); - EXPECT_EQ(2 * kSystemPageSize, stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr2 - kPointerOffset, true); - generic_allocator.root()->PurgeMemory( - PartitionPurgeDiscardUnusedSystemPages); - CHECK_PAGE_IN_CORE(ptr2 - kPointerOffset, false); - EXPECT_EQ(3u, page->num_unprovisioned_slots); - - generic_allocator.root()->Free(ptr1); - } - // Free the first of two 4096 byte allocations and then purge. - { - char* ptr1 = reinterpret_cast(generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name)); - void* ptr2 = generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name); - generic_allocator.root()->Free(ptr1); - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(kSystemPageSize); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); -#if defined(OS_WIN) - EXPECT_EQ(0u, stats->discardable_bytes); -#else - EXPECT_EQ(kSystemPageSize, stats->discardable_bytes); -#endif - EXPECT_EQ(kSystemPageSize, stats->active_bytes); - EXPECT_EQ(2 * kSystemPageSize, stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - generic_allocator.root()->PurgeMemory( - PartitionPurgeDiscardUnusedSystemPages); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, false); - - generic_allocator.root()->Free(ptr2); - } - { - constexpr size_t requested_size = 2.25 * kSystemPageSize; - char* ptr1 = reinterpret_cast(generic_allocator.root()->Alloc( - requested_size - kExtraAllocSize, type_name)); - void* ptr2 = generic_allocator.root()->Alloc( - requested_size - kExtraAllocSize, type_name); - void* ptr3 = generic_allocator.root()->Alloc( - requested_size - kExtraAllocSize, type_name); - void* ptr4 = generic_allocator.root()->Alloc( - requested_size - kExtraAllocSize, type_name); - memset(ptr1, 'A', requested_size - kExtraAllocSize); - memset(ptr2, 'A', requested_size - kExtraAllocSize); - generic_allocator.root()->Free(ptr2); - generic_allocator.root()->Free(ptr1); - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(requested_size); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(2 * kSystemPageSize, stats->discardable_bytes); - EXPECT_EQ(requested_size * 2, stats->active_bytes); - EXPECT_EQ(9 * kSystemPageSize, stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + kSystemPageSize, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 3), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 4), true); - generic_allocator.root()->PurgeMemory( - PartitionPurgeDiscardUnusedSystemPages); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + kSystemPageSize, false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 3), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 4), true); - - generic_allocator.root()->Free(ptr3); - generic_allocator.root()->Free(ptr4); - } - -// When kSystemPageSize = 16384 (as on _MIPS_ARCH_LOONGSON), 64 * -// kSystemPageSize (see the #else branch below) caused this test to OOM. -// Therefore, for systems with 16 KiB pages, use 32 * kSystemPageSize. -// -// TODO(palmer): Refactor this to branch on page size instead of architecture, -// for clarity of purpose and for applicability to more architectures. -#if defined(_MIPS_ARCH_LOONGSON) - { - char* ptr1 = reinterpret_cast(generic_allocator.root()->Alloc( - (32 * kSystemPageSize) - kExtraAllocSize, type_name)); - memset(ptr1, 'A', (32 * kSystemPageSize) - kExtraAllocSize); - generic_allocator.root()->Free(ptr1); - ptr1 = reinterpret_cast(generic_allocator.root()->Alloc( - (31 * kSystemPageSize) - kExtraAllocSize, type_name)); - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(32 * kSystemPageSize); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(kSystemPageSize, stats->discardable_bytes); - EXPECT_EQ(31 * kSystemPageSize, stats->active_bytes); - EXPECT_EQ(32 * kSystemPageSize, stats->resident_bytes); - } - CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 30), true); - CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 31), true); - generic_allocator.root()->PurgeMemory( - PartitionPurgeDiscardUnusedSystemPages); - CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 30), true); - CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 31), false); - - generic_allocator.root()->Free(ptr1); - } -#else - { - char* ptr1 = reinterpret_cast(generic_allocator.root()->Alloc( - (64 * kSystemPageSize) - kExtraAllocSize, type_name)); - memset(ptr1, 'A', (64 * kSystemPageSize) - kExtraAllocSize); - generic_allocator.root()->Free(ptr1); - ptr1 = reinterpret_cast(generic_allocator.root()->Alloc( - (61 * kSystemPageSize) - kExtraAllocSize, type_name)); - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(64 * kSystemPageSize); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(3 * kSystemPageSize, stats->discardable_bytes); - EXPECT_EQ(61 * kSystemPageSize, stats->active_bytes); - EXPECT_EQ(64 * kSystemPageSize, stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 60), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 61), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 62), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 63), true); - generic_allocator.root()->PurgeMemory( - PartitionPurgeDiscardUnusedSystemPages); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 60), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 61), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 62), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 63), false); - - generic_allocator.root()->Free(ptr1); - } -#endif - // This sub-test tests truncation of the provisioned slots in a trickier - // case where the freelist is rewritten. - generic_allocator.root()->PurgeMemory(PartitionPurgeDecommitEmptyPages); - { - char* ptr1 = reinterpret_cast(generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name)); - void* ptr2 = generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name); - void* ptr3 = generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name); - void* ptr4 = generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name); - ptr1[0] = 'A'; - ptr1[kSystemPageSize] = 'A'; - ptr1[kSystemPageSize * 2] = 'A'; - ptr1[kSystemPageSize * 3] = 'A'; - PartitionPage* page = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr1)); - generic_allocator.root()->Free(ptr2); - generic_allocator.root()->Free(ptr4); - generic_allocator.root()->Free(ptr1); - EXPECT_EQ(0u, page->num_unprovisioned_slots); - - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(kSystemPageSize); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); -#if defined(OS_WIN) - EXPECT_EQ(kSystemPageSize, stats->discardable_bytes); -#else - EXPECT_EQ(2 * kSystemPageSize, stats->discardable_bytes); -#endif - EXPECT_EQ(kSystemPageSize, stats->active_bytes); - EXPECT_EQ(4 * kSystemPageSize, stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + kSystemPageSize, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 3), true); - generic_allocator.root()->PurgeMemory( - PartitionPurgeDiscardUnusedSystemPages); - EXPECT_EQ(1u, page->num_unprovisioned_slots); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + kSystemPageSize, false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 3), false); - - // Let's check we didn't brick the freelist. - void* ptr1b = generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name); - EXPECT_EQ(ptr1, ptr1b); - void* ptr2b = generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name); - EXPECT_EQ(ptr2, ptr2b); - EXPECT_FALSE(page->freelist_head); - - generic_allocator.root()->Free(ptr1); - generic_allocator.root()->Free(ptr2); - generic_allocator.root()->Free(ptr3); - } - // This sub-test is similar, but tests a double-truncation. - generic_allocator.root()->PurgeMemory(PartitionPurgeDecommitEmptyPages); - { - char* ptr1 = reinterpret_cast(generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name)); - void* ptr2 = generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name); - void* ptr3 = generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name); - void* ptr4 = generic_allocator.root()->Alloc( - kSystemPageSize - kExtraAllocSize, type_name); - ptr1[0] = 'A'; - ptr1[kSystemPageSize] = 'A'; - ptr1[kSystemPageSize * 2] = 'A'; - ptr1[kSystemPageSize * 3] = 'A'; - PartitionPage* page = - PartitionPage::FromPointer(PartitionCookieFreePointerAdjust(ptr1)); - generic_allocator.root()->Free(ptr4); - generic_allocator.root()->Free(ptr3); - EXPECT_EQ(0u, page->num_unprovisioned_slots); - - { - MockPartitionStatsDumper dumper; - generic_allocator.root()->DumpStats("mock_generic_allocator", - false /* detailed dump */, &dumper); - EXPECT_TRUE(dumper.IsMemoryAllocationRecorded()); - - const PartitionBucketMemoryStats* stats = - dumper.GetBucketStats(kSystemPageSize); - EXPECT_TRUE(stats); - EXPECT_TRUE(stats->is_valid); - EXPECT_EQ(0u, stats->decommittable_bytes); - EXPECT_EQ(2 * kSystemPageSize, stats->discardable_bytes); - EXPECT_EQ(2 * kSystemPageSize, stats->active_bytes); - EXPECT_EQ(4 * kSystemPageSize, stats->resident_bytes); - } - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + kSystemPageSize, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 2), true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 3), true); - generic_allocator.root()->PurgeMemory( - PartitionPurgeDiscardUnusedSystemPages); - EXPECT_EQ(2u, page->num_unprovisioned_slots); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + kSystemPageSize, true); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 2), false); - CHECK_PAGE_IN_CORE(ptr1 - kPointerOffset + (kSystemPageSize * 3), false); - - EXPECT_FALSE(page->freelist_head); - - generic_allocator.root()->Free(ptr1); - generic_allocator.root()->Free(ptr2); - } -} - -TEST_F(PartitionAllocTest, ReallocMovesCookies) { - // Resize so as to be sure to hit a "resize in place" case, and ensure that - // use of the entire result is compatible with the debug mode's cookies, even - // when the bucket size is large enough to span more than one partition page - // and we can track the "raw" size. See https://crbug.com/709271 - static constexpr size_t kSize = - base::kMaxSystemPagesPerSlotSpan * base::kSystemPageSize; - void* ptr = generic_allocator.root()->Alloc(kSize + 1, type_name); - EXPECT_TRUE(ptr); - - memset(ptr, 0xbd, kSize + 1); - ptr = generic_allocator.root()->Realloc(ptr, kSize + 2, type_name); - EXPECT_TRUE(ptr); - - memset(ptr, 0xbd, kSize + 2); - generic_allocator.root()->Free(ptr); -} - -TEST_F(PartitionAllocTest, SmallReallocDoesNotMoveTrailingCookie) { - // For crbug.com/781473 - static constexpr size_t kSize = 264; - void* ptr = generic_allocator.root()->Alloc(kSize, type_name); - EXPECT_TRUE(ptr); - - ptr = generic_allocator.root()->Realloc(ptr, kSize + 16, type_name); - EXPECT_TRUE(ptr); - - generic_allocator.root()->Free(ptr); -} - -} // namespace internal -} // namespace base - -#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) diff --git a/allocator/partition_allocator/partition_bucket.cc b/allocator/partition_allocator/partition_bucket.cc deleted file mode 100644 index fcea52327..000000000 --- a/allocator/partition_allocator/partition_bucket.cc +++ /dev/null @@ -1,554 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_bucket.h" -#include "base/allocator/partition_allocator/oom.h" -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_direct_map_extent.h" -#include "base/allocator/partition_allocator/partition_oom.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "base/allocator/partition_allocator/partition_root_base.h" -#include "build/build_config.h" - -namespace base { -namespace internal { - -namespace { - -ALWAYS_INLINE PartitionPage* PartitionDirectMap(PartitionRootBase* root, - int flags, - size_t raw_size) { - size_t size = PartitionBucket::get_direct_map_size(raw_size); - - // Because we need to fake looking like a super page, we need to allocate - // a bunch of system pages more than "size": - // - The first few system pages are the partition page in which the super - // page metadata is stored. We fault just one system page out of a partition - // page sized clump. - // - We add a trailing guard page on 32-bit (on 64-bit we rely on the - // massive address space plus randomization instead). - size_t map_size = size + kPartitionPageSize; -#if !defined(ARCH_CPU_64_BITS) - map_size += kSystemPageSize; -#endif - // Round up to the allocation granularity. - map_size += kPageAllocationGranularityOffsetMask; - map_size &= kPageAllocationGranularityBaseMask; - - // TODO: these pages will be zero-filled. Consider internalizing an - // allocZeroed() API so we can avoid a memset() entirely in this case. - char* ptr = reinterpret_cast( - AllocPages(nullptr, map_size, kSuperPageSize, PageReadWrite)); - if (UNLIKELY(!ptr)) - return nullptr; - - size_t committed_page_size = size + kSystemPageSize; - root->total_size_of_direct_mapped_pages += committed_page_size; - root->IncreaseCommittedPages(committed_page_size); - - char* slot = ptr + kPartitionPageSize; - CHECK(SetSystemPagesAccess(ptr + (kSystemPageSize * 2), - kPartitionPageSize - (kSystemPageSize * 2), - PageInaccessible)); -#if !defined(ARCH_CPU_64_BITS) - CHECK(SetSystemPagesAccess(ptr, kSystemPageSize, PageInaccessible)); - CHECK(SetSystemPagesAccess(slot + size, kSystemPageSize, PageInaccessible)); -#endif - - PartitionSuperPageExtentEntry* extent = - reinterpret_cast( - PartitionSuperPageToMetadataArea(ptr)); - extent->root = root; - // The new structures are all located inside a fresh system page so they - // will all be zeroed out. These DCHECKs are for documentation. - DCHECK(!extent->super_page_base); - DCHECK(!extent->super_pages_end); - DCHECK(!extent->next); - PartitionPage* page = PartitionPage::FromPointerNoAlignmentCheck(slot); - PartitionBucket* bucket = reinterpret_cast( - reinterpret_cast(page) + (kPageMetadataSize * 2)); - DCHECK(!page->next_page); - DCHECK(!page->num_allocated_slots); - DCHECK(!page->num_unprovisioned_slots); - DCHECK(!page->page_offset); - DCHECK(!page->empty_cache_index); - page->bucket = bucket; - page->freelist_head = reinterpret_cast(slot); - PartitionFreelistEntry* next_entry = - reinterpret_cast(slot); - next_entry->next = PartitionFreelistEntry::Transform(nullptr); - - DCHECK(!bucket->active_pages_head); - DCHECK(!bucket->empty_pages_head); - DCHECK(!bucket->decommitted_pages_head); - DCHECK(!bucket->num_system_pages_per_slot_span); - DCHECK(!bucket->num_full_pages); - bucket->slot_size = size; - - PartitionDirectMapExtent* map_extent = - PartitionDirectMapExtent::FromPage(page); - map_extent->map_size = map_size - kPartitionPageSize - kSystemPageSize; - map_extent->bucket = bucket; - - // Maintain the doubly-linked list of all direct mappings. - map_extent->next_extent = root->direct_map_list; - if (map_extent->next_extent) - map_extent->next_extent->prev_extent = map_extent; - map_extent->prev_extent = nullptr; - root->direct_map_list = map_extent; - - return page; -} - -} // namespace - -// static -PartitionBucket PartitionBucket::sentinel_bucket_; - -PartitionBucket* PartitionBucket::get_sentinel_bucket() { - return &sentinel_bucket_; -} - -// TODO(ajwong): This seems to interact badly with -// get_pages_per_slot_span() which rounds the value from this up to a -// multiple of kNumSystemPagesPerPartitionPage (aka 4) anyways. -// http://crbug.com/776537 -// -// TODO(ajwong): The waste calculation seems wrong. The PTE usage should cover -// both used and unsed pages. -// http://crbug.com/776537 -uint8_t PartitionBucket::get_system_pages_per_slot_span() { - // This works out reasonably for the current bucket sizes of the generic - // allocator, and the current values of partition page size and constants. - // Specifically, we have enough room to always pack the slots perfectly into - // some number of system pages. The only waste is the waste associated with - // unfaulted pages (i.e. wasted address space). - // TODO: we end up using a lot of system pages for very small sizes. For - // example, we'll use 12 system pages for slot size 24. The slot size is - // so small that the waste would be tiny with just 4, or 1, system pages. - // Later, we can investigate whether there are anti-fragmentation benefits - // to using fewer system pages. - double best_waste_ratio = 1.0f; - uint16_t best_pages = 0; - if (this->slot_size > kMaxSystemPagesPerSlotSpan * kSystemPageSize) { - // TODO(ajwong): Why is there a DCHECK here for this? - // http://crbug.com/776537 - DCHECK(!(this->slot_size % kSystemPageSize)); - best_pages = static_cast(this->slot_size / kSystemPageSize); - // TODO(ajwong): Should this be checking against - // kMaxSystemPagesPerSlotSpan or numeric_limits::max? - // http://crbug.com/776537 - CHECK(best_pages < (1 << 8)); - return static_cast(best_pages); - } - DCHECK(this->slot_size <= kMaxSystemPagesPerSlotSpan * kSystemPageSize); - for (uint16_t i = kNumSystemPagesPerPartitionPage - 1; - i <= kMaxSystemPagesPerSlotSpan; ++i) { - size_t page_size = kSystemPageSize * i; - size_t num_slots = page_size / this->slot_size; - size_t waste = page_size - (num_slots * this->slot_size); - // Leaving a page unfaulted is not free; the page will occupy an empty page - // table entry. Make a simple attempt to account for that. - // - // TODO(ajwong): This looks wrong. PTEs are allocated for all pages - // regardless of whether or not they are wasted. Should it just - // be waste += i * sizeof(void*)? - // http://crbug.com/776537 - size_t num_remainder_pages = i & (kNumSystemPagesPerPartitionPage - 1); - size_t num_unfaulted_pages = - num_remainder_pages - ? (kNumSystemPagesPerPartitionPage - num_remainder_pages) - : 0; - waste += sizeof(void*) * num_unfaulted_pages; - double waste_ratio = (double)waste / (double)page_size; - if (waste_ratio < best_waste_ratio) { - best_waste_ratio = waste_ratio; - best_pages = i; - } - } - DCHECK(best_pages > 0); - CHECK(best_pages <= kMaxSystemPagesPerSlotSpan); - return static_cast(best_pages); -} - -void PartitionBucket::Init(uint32_t new_slot_size) { - slot_size = new_slot_size; - active_pages_head = PartitionPage::get_sentinel_page(); - empty_pages_head = nullptr; - decommitted_pages_head = nullptr; - num_full_pages = 0; - num_system_pages_per_slot_span = get_system_pages_per_slot_span(); -} - -NOINLINE void PartitionBucket::OnFull() { - OOM_CRASH(); -} - -ALWAYS_INLINE void* PartitionBucket::AllocNewSlotSpan( - PartitionRootBase* root, - int flags, - uint16_t num_partition_pages) { - DCHECK(!(reinterpret_cast(root->next_partition_page) % - kPartitionPageSize)); - DCHECK(!(reinterpret_cast(root->next_partition_page_end) % - kPartitionPageSize)); - DCHECK(num_partition_pages <= kNumPartitionPagesPerSuperPage); - size_t total_size = kPartitionPageSize * num_partition_pages; - size_t num_partition_pages_left = - (root->next_partition_page_end - root->next_partition_page) >> - kPartitionPageShift; - if (LIKELY(num_partition_pages_left >= num_partition_pages)) { - // In this case, we can still hand out pages from the current super page - // allocation. - char* ret = root->next_partition_page; - - // Fresh System Pages in the SuperPages are decommited. Commit them - // before vending them back. - CHECK(SetSystemPagesAccess(ret, total_size, PageReadWrite)); - - root->next_partition_page += total_size; - root->IncreaseCommittedPages(total_size); - return ret; - } - - // Need a new super page. We want to allocate super pages in a continguous - // address region as much as possible. This is important for not causing - // page table bloat and not fragmenting address spaces in 32 bit - // architectures. - char* requestedAddress = root->next_super_page; - char* super_page = reinterpret_cast(AllocPages( - requestedAddress, kSuperPageSize, kSuperPageSize, PageReadWrite)); - if (UNLIKELY(!super_page)) - return nullptr; - - root->total_size_of_super_pages += kSuperPageSize; - root->IncreaseCommittedPages(total_size); - - // |total_size| MUST be less than kSuperPageSize - (kPartitionPageSize*2). - // This is a trustworthy value because num_partition_pages is not user - // controlled. - // - // TODO(ajwong): Introduce a DCHECK. - root->next_super_page = super_page + kSuperPageSize; - char* ret = super_page + kPartitionPageSize; - root->next_partition_page = ret + total_size; - root->next_partition_page_end = root->next_super_page - kPartitionPageSize; - // Make the first partition page in the super page a guard page, but leave a - // hole in the middle. - // This is where we put page metadata and also a tiny amount of extent - // metadata. - CHECK(SetSystemPagesAccess(super_page, kSystemPageSize, PageInaccessible)); - CHECK(SetSystemPagesAccess(super_page + (kSystemPageSize * 2), - kPartitionPageSize - (kSystemPageSize * 2), - PageInaccessible)); - // CHECK(SetSystemPagesAccess(super_page + (kSuperPageSize - - // kPartitionPageSize), - // kPartitionPageSize, PageInaccessible)); - // All remaining slotspans for the unallocated PartitionPages inside the - // SuperPage are conceptually decommitted. Correctly set the state here - // so they do not occupy resources. - // - // TODO(ajwong): Refactor Page Allocator API so the SuperPage comes in - // decommited initially. - CHECK(SetSystemPagesAccess(super_page + kPartitionPageSize + total_size, - (kSuperPageSize - kPartitionPageSize - total_size), - PageInaccessible)); - - // If we were after a specific address, but didn't get it, assume that - // the system chose a lousy address. Here most OS'es have a default - // algorithm that isn't randomized. For example, most Linux - // distributions will allocate the mapping directly before the last - // successful mapping, which is far from random. So we just get fresh - // randomness for the next mapping attempt. - if (requestedAddress && requestedAddress != super_page) - root->next_super_page = nullptr; - - // We allocated a new super page so update super page metadata. - // First check if this is a new extent or not. - PartitionSuperPageExtentEntry* latest_extent = - reinterpret_cast( - PartitionSuperPageToMetadataArea(super_page)); - // By storing the root in every extent metadata object, we have a fast way - // to go from a pointer within the partition to the root object. - latest_extent->root = root; - // Most new extents will be part of a larger extent, and these three fields - // are unused, but we initialize them to 0 so that we get a clear signal - // in case they are accidentally used. - latest_extent->super_page_base = nullptr; - latest_extent->super_pages_end = nullptr; - latest_extent->next = nullptr; - - PartitionSuperPageExtentEntry* current_extent = root->current_extent; - bool isNewExtent = (super_page != requestedAddress); - if (UNLIKELY(isNewExtent)) { - if (UNLIKELY(!current_extent)) { - DCHECK(!root->first_extent); - root->first_extent = latest_extent; - } else { - DCHECK(current_extent->super_page_base); - current_extent->next = latest_extent; - } - root->current_extent = latest_extent; - latest_extent->super_page_base = super_page; - latest_extent->super_pages_end = super_page + kSuperPageSize; - } else { - // We allocated next to an existing extent so just nudge the size up a - // little. - DCHECK(current_extent->super_pages_end); - current_extent->super_pages_end += kSuperPageSize; - DCHECK(ret >= current_extent->super_page_base && - ret < current_extent->super_pages_end); - } - return ret; -} - -ALWAYS_INLINE uint16_t PartitionBucket::get_pages_per_slot_span() { - // Rounds up to nearest multiple of kNumSystemPagesPerPartitionPage. - return (num_system_pages_per_slot_span + - (kNumSystemPagesPerPartitionPage - 1)) / - kNumSystemPagesPerPartitionPage; -} - -ALWAYS_INLINE void PartitionBucket::InitializeSlotSpan(PartitionPage* page) { - // The bucket never changes. We set it up once. - page->bucket = this; - page->empty_cache_index = -1; - - page->Reset(); - - // If this page has just a single slot, do not set up page offsets for any - // page metadata other than the first one. This ensures that attempts to - // touch invalid page metadata fail. - if (page->num_unprovisioned_slots == 1) - return; - - uint16_t num_partition_pages = get_pages_per_slot_span(); - char* page_char_ptr = reinterpret_cast(page); - for (uint16_t i = 1; i < num_partition_pages; ++i) { - page_char_ptr += kPageMetadataSize; - PartitionPage* secondary_page = - reinterpret_cast(page_char_ptr); - secondary_page->page_offset = i; - } -} - -ALWAYS_INLINE char* PartitionBucket::AllocAndFillFreelist(PartitionPage* page) { - DCHECK(page != PartitionPage::get_sentinel_page()); - uint16_t num_slots = page->num_unprovisioned_slots; - DCHECK(num_slots); - // We should only get here when _every_ slot is either used or unprovisioned. - // (The third state is "on the freelist". If we have a non-empty freelist, we - // should not get here.) - DCHECK(num_slots + page->num_allocated_slots == this->get_slots_per_span()); - // Similarly, make explicitly sure that the freelist is empty. - DCHECK(!page->freelist_head); - DCHECK(page->num_allocated_slots >= 0); - - size_t size = this->slot_size; - char* base = reinterpret_cast(PartitionPage::ToPointer(page)); - char* return_object = base + (size * page->num_allocated_slots); - char* first_freelist_pointer = return_object + size; - char* first_freelist_pointer_extent = - first_freelist_pointer + sizeof(PartitionFreelistEntry*); - // Our goal is to fault as few system pages as possible. We calculate the - // page containing the "end" of the returned slot, and then allow freelist - // pointers to be written up to the end of that page. - char* sub_page_limit = reinterpret_cast( - RoundUpToSystemPage(reinterpret_cast(first_freelist_pointer))); - char* slots_limit = return_object + (size * num_slots); - char* freelist_limit = sub_page_limit; - if (UNLIKELY(slots_limit < freelist_limit)) - freelist_limit = slots_limit; - - uint16_t num_new_freelist_entries = 0; - if (LIKELY(first_freelist_pointer_extent <= freelist_limit)) { - // Only consider used space in the slot span. If we consider wasted - // space, we may get an off-by-one when a freelist pointer fits in the - // wasted space, but a slot does not. - // We know we can fit at least one freelist pointer. - num_new_freelist_entries = 1; - // Any further entries require space for the whole slot span. - num_new_freelist_entries += static_cast( - (freelist_limit - first_freelist_pointer_extent) / size); - } - - // We always return an object slot -- that's the +1 below. - // We do not neccessarily create any new freelist entries, because we cross - // sub page boundaries frequently for large bucket sizes. - DCHECK(num_new_freelist_entries + 1 <= num_slots); - num_slots -= (num_new_freelist_entries + 1); - page->num_unprovisioned_slots = num_slots; - page->num_allocated_slots++; - - if (LIKELY(num_new_freelist_entries)) { - char* freelist_pointer = first_freelist_pointer; - PartitionFreelistEntry* entry = - reinterpret_cast(freelist_pointer); - page->freelist_head = entry; - while (--num_new_freelist_entries) { - freelist_pointer += size; - PartitionFreelistEntry* next_entry = - reinterpret_cast(freelist_pointer); - entry->next = PartitionFreelistEntry::Transform(next_entry); - entry = next_entry; - } - entry->next = PartitionFreelistEntry::Transform(nullptr); - } else { - page->freelist_head = nullptr; - } - return return_object; -} - -bool PartitionBucket::SetNewActivePage() { - PartitionPage* page = this->active_pages_head; - if (page == PartitionPage::get_sentinel_page()) - return false; - - PartitionPage* next_page; - - for (; page; page = next_page) { - next_page = page->next_page; - DCHECK(page->bucket == this); - DCHECK(page != this->empty_pages_head); - DCHECK(page != this->decommitted_pages_head); - - if (LIKELY(page->is_active())) { - // This page is usable because it has freelist entries, or has - // unprovisioned slots we can create freelist entries from. - this->active_pages_head = page; - return true; - } - - // Deal with empty and decommitted pages. - if (LIKELY(page->is_empty())) { - page->next_page = this->empty_pages_head; - this->empty_pages_head = page; - } else if (LIKELY(page->is_decommitted())) { - page->next_page = this->decommitted_pages_head; - this->decommitted_pages_head = page; - } else { - DCHECK(page->is_full()); - // If we get here, we found a full page. Skip over it too, and also - // tag it as full (via a negative value). We need it tagged so that - // free'ing can tell, and move it back into the active page list. - page->num_allocated_slots = -page->num_allocated_slots; - ++this->num_full_pages; - // num_full_pages is a uint16_t for efficient packing so guard against - // overflow to be safe. - if (UNLIKELY(!this->num_full_pages)) - OnFull(); - // Not necessary but might help stop accidents. - page->next_page = nullptr; - } - } - - this->active_pages_head = PartitionPage::get_sentinel_page(); - return false; -} - -void* PartitionBucket::SlowPathAlloc(PartitionRootBase* root, - int flags, - size_t size) { - // The slow path is called when the freelist is empty. - DCHECK(!this->active_pages_head->freelist_head); - - PartitionPage* new_page = nullptr; - - // For the PartitionRootGeneric::Alloc() API, we have a bunch of buckets - // marked as special cases. We bounce them through to the slow path so that - // we can still have a blazing fast hot path due to lack of corner-case - // branches. - // - // Note: The ordering of the conditionals matter! In particular, - // SetNewActivePage() has a side-effect even when returning - // false where it sweeps the active page list and may move things into - // the empty or decommitted lists which affects the subsequent conditional. - bool return_null = flags & PartitionAllocReturnNull; - if (UNLIKELY(this->is_direct_mapped())) { - DCHECK(size > kGenericMaxBucketed); - DCHECK(this == get_sentinel_bucket()); - DCHECK(this->active_pages_head == PartitionPage::get_sentinel_page()); - if (size > kGenericMaxDirectMapped) { - if (return_null) - return nullptr; - PartitionExcessiveAllocationSize(); - } - new_page = PartitionDirectMap(root, flags, size); - } else if (LIKELY(this->SetNewActivePage())) { - // First, did we find an active page in the active pages list? - new_page = this->active_pages_head; - DCHECK(new_page->is_active()); - } else if (LIKELY(this->empty_pages_head != nullptr) || - LIKELY(this->decommitted_pages_head != nullptr)) { - // Second, look in our lists of empty and decommitted pages. - // Check empty pages first, which are preferred, but beware that an - // empty page might have been decommitted. - while (LIKELY((new_page = this->empty_pages_head) != nullptr)) { - DCHECK(new_page->bucket == this); - DCHECK(new_page->is_empty() || new_page->is_decommitted()); - this->empty_pages_head = new_page->next_page; - // Accept the empty page unless it got decommitted. - if (new_page->freelist_head) { - new_page->next_page = nullptr; - break; - } - DCHECK(new_page->is_decommitted()); - new_page->next_page = this->decommitted_pages_head; - this->decommitted_pages_head = new_page; - } - if (UNLIKELY(!new_page) && - LIKELY(this->decommitted_pages_head != nullptr)) { - new_page = this->decommitted_pages_head; - DCHECK(new_page->bucket == this); - DCHECK(new_page->is_decommitted()); - this->decommitted_pages_head = new_page->next_page; - void* addr = PartitionPage::ToPointer(new_page); - root->RecommitSystemPages(addr, new_page->bucket->get_bytes_per_span()); - new_page->Reset(); - } - DCHECK(new_page); - } else { - // Third. If we get here, we need a brand new page. - uint16_t num_partition_pages = this->get_pages_per_slot_span(); - void* rawPages = AllocNewSlotSpan(root, flags, num_partition_pages); - if (LIKELY(rawPages != nullptr)) { - new_page = PartitionPage::FromPointerNoAlignmentCheck(rawPages); - InitializeSlotSpan(new_page); - } - } - - // Bail if we had a memory allocation failure. - if (UNLIKELY(!new_page)) { - DCHECK(this->active_pages_head == PartitionPage::get_sentinel_page()); - if (return_null) - return nullptr; - root->OutOfMemory(); - } - - // TODO(ajwong): Is there a way to avoid the reading of bucket here? - // It seems like in many of the conditional branches above, |this| == - // |new_page->bucket|. Maybe pull this into another function? - PartitionBucket* bucket = new_page->bucket; - DCHECK(bucket != get_sentinel_bucket()); - bucket->active_pages_head = new_page; - new_page->set_raw_size(size); - - // If we found an active page with free slots, or an empty page, we have a - // usable freelist head. - if (LIKELY(new_page->freelist_head != nullptr)) { - PartitionFreelistEntry* entry = new_page->freelist_head; - PartitionFreelistEntry* new_head = - PartitionFreelistEntry::Transform(entry->next); - new_page->freelist_head = new_head; - new_page->num_allocated_slots++; - return entry; - } - // Otherwise, we need to build the freelist. - DCHECK(new_page->num_unprovisioned_slots); - return AllocAndFillFreelist(new_page); -} - -} // namespace internal -} // namespace base diff --git a/allocator/partition_allocator/partition_bucket.h b/allocator/partition_allocator/partition_bucket.h deleted file mode 100644 index a626dfa84..000000000 --- a/allocator/partition_allocator/partition_bucket.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_ - -#include -#include - -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/base_export.h" -#include "base/compiler_specific.h" - -namespace base { -namespace internal { - -struct PartitionPage; -struct PartitionRootBase; - -struct PartitionBucket { - // Accessed most in hot path => goes first. - PartitionPage* active_pages_head; - - PartitionPage* empty_pages_head; - PartitionPage* decommitted_pages_head; - uint32_t slot_size; - uint32_t num_system_pages_per_slot_span : 8; - uint32_t num_full_pages : 24; - - // Public API. - void Init(uint32_t new_slot_size); - - // Note the matching Free() functions are in PartitionPage. - BASE_EXPORT NOINLINE void* SlowPathAlloc(PartitionRootBase* root, - int flags, - size_t size); - - ALWAYS_INLINE bool is_direct_mapped() const { - return !num_system_pages_per_slot_span; - } - ALWAYS_INLINE size_t get_bytes_per_span() const { - // TODO(ajwong): Change to CheckedMul. https://crbug.com/787153 - // https://crbug.com/680657 - return num_system_pages_per_slot_span * kSystemPageSize; - } - ALWAYS_INLINE uint16_t get_slots_per_span() const { - // TODO(ajwong): Change to CheckedMul. https://crbug.com/787153 - // https://crbug.com/680657 - return static_cast(get_bytes_per_span() / slot_size); - } - - static ALWAYS_INLINE size_t get_direct_map_size(size_t size) { - // Caller must check that the size is not above the kGenericMaxDirectMapped - // limit before calling. This also guards against integer overflow in the - // calculation here. - DCHECK(size <= kGenericMaxDirectMapped); - return (size + kSystemPageOffsetMask) & kSystemPageBaseMask; - } - - // TODO(ajwong): Can this be made private? https://crbug.com/787153 - static PartitionBucket* get_sentinel_bucket(); - - // This helper function scans a bucket's active page list for a suitable new - // active page. When it finds a suitable new active page (one that has - // free slots and is not empty), it is set as the new active page. If there - // is no suitable new active page, the current active page is set to - // PartitionPage::get_sentinel_page(). As potential pages are scanned, they - // are tidied up according to their state. Empty pages are swept on to the - // empty page list, decommitted pages on to the decommitted page list and full - // pages are unlinked from any list. - // - // This is where the guts of the bucket maintenance is done! - bool SetNewActivePage(); - - private: - static void OutOfMemory(const PartitionRootBase* root); - static void OutOfMemoryWithLotsOfUncommitedPages(); - - static NOINLINE void OnFull(); - - // Returns a natural number of PartitionPages (calculated by - // get_system_pages_per_slot_span()) to allocate from the current - // SuperPage when the bucket runs out of slots. - ALWAYS_INLINE uint16_t get_pages_per_slot_span(); - - // Returns the number of system pages in a slot span. - // - // The calculation attemps to find the best number of System Pages to - // allocate for the given slot_size to minimize wasted space. It uses a - // heuristic that looks at number of bytes wasted after the last slot and - // attempts to account for the PTE usage of each System Page. - uint8_t get_system_pages_per_slot_span(); - - // Allocates a new slot span with size |num_partition_pages| from the - // current extent. Metadata within this slot span will be uninitialized. - // Returns nullptr on error. - ALWAYS_INLINE void* AllocNewSlotSpan(PartitionRootBase* root, - int flags, - uint16_t num_partition_pages); - - // Each bucket allocates a slot span when it runs out of slots. - // A slot span's size is equal to get_pages_per_slot_span() number of - // PartitionPages. This function initializes all PartitionPage within the - // span to point to the first PartitionPage which holds all the metadata - // for the span and registers this bucket as the owner of the span. It does - // NOT put the slots into the bucket's freelist. - ALWAYS_INLINE void InitializeSlotSpan(PartitionPage* page); - - // Allocates one slot from the given |page| and then adds the remainder to - // the current bucket. If the |page| was freshly allocated, it must have been - // passed through InitializeSlotSpan() first. - ALWAYS_INLINE char* AllocAndFillFreelist(PartitionPage* page); - - static PartitionBucket sentinel_bucket_; -}; - -} // namespace internal -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_ diff --git a/allocator/partition_allocator/partition_cookie.h b/allocator/partition_allocator/partition_cookie.h deleted file mode 100644 index 8e6cb2060..000000000 --- a/allocator/partition_allocator/partition_cookie.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_ - -#include "base/compiler_specific.h" -#include "base/logging.h" - -namespace base { -namespace internal { - -#if DCHECK_IS_ON() -// These two byte values match tcmalloc. -static const unsigned char kUninitializedByte = 0xAB; -static const unsigned char kFreedByte = 0xCD; -static const size_t kCookieSize = - 16; // Handles alignment up to XMM instructions on Intel. -static const unsigned char kCookieValue[kCookieSize] = { - 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xD0, 0x0D, - 0x13, 0x37, 0xF0, 0x05, 0xBA, 0x11, 0xAB, 0x1E}; -#endif - -ALWAYS_INLINE void PartitionCookieCheckValue(void* ptr) { -#if DCHECK_IS_ON() - unsigned char* cookie_ptr = reinterpret_cast(ptr); - for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr) - DCHECK(*cookie_ptr == kCookieValue[i]); -#endif -} - -ALWAYS_INLINE size_t PartitionCookieSizeAdjustAdd(size_t size) { -#if DCHECK_IS_ON() - // Add space for cookies, checking for integer overflow. TODO(palmer): - // Investigate the performance and code size implications of using - // CheckedNumeric throughout PA. - DCHECK(size + (2 * kCookieSize) > size); - size += 2 * kCookieSize; -#endif - return size; -} - -ALWAYS_INLINE void* PartitionCookieFreePointerAdjust(void* ptr) { -#if DCHECK_IS_ON() - // The value given to the application is actually just after the cookie. - ptr = static_cast(ptr) - kCookieSize; -#endif - return ptr; -} - -ALWAYS_INLINE size_t PartitionCookieSizeAdjustSubtract(size_t size) { -#if DCHECK_IS_ON() - // Remove space for cookies. - DCHECK(size >= 2 * kCookieSize); - size -= 2 * kCookieSize; -#endif - return size; -} - -ALWAYS_INLINE void PartitionCookieWriteValue(void* ptr) { -#if DCHECK_IS_ON() - unsigned char* cookie_ptr = reinterpret_cast(ptr); - for (size_t i = 0; i < kCookieSize; ++i, ++cookie_ptr) - *cookie_ptr = kCookieValue[i]; -#endif -} - -} // namespace internal -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_ diff --git a/allocator/partition_allocator/partition_direct_map_extent.h b/allocator/partition_allocator/partition_direct_map_extent.h deleted file mode 100644 index 2a0bb1938..000000000 --- a/allocator/partition_allocator/partition_direct_map_extent.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_ - -#include "base/allocator/partition_allocator/partition_bucket.h" -#include "base/allocator/partition_allocator/partition_page.h" - -namespace base { -namespace internal { - -struct PartitionDirectMapExtent { - PartitionDirectMapExtent* next_extent; - PartitionDirectMapExtent* prev_extent; - PartitionBucket* bucket; - size_t map_size; // Mapped size, not including guard pages and meta-data. - - ALWAYS_INLINE static PartitionDirectMapExtent* FromPage(PartitionPage* page); -}; - -ALWAYS_INLINE PartitionDirectMapExtent* PartitionDirectMapExtent::FromPage( - PartitionPage* page) { - DCHECK(page->bucket->is_direct_mapped()); - return reinterpret_cast( - reinterpret_cast(page) + 3 * kPageMetadataSize); -} - -} // namespace internal -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_ diff --git a/allocator/partition_allocator/partition_freelist_entry.h b/allocator/partition_allocator/partition_freelist_entry.h deleted file mode 100644 index 7e3282ef4..000000000 --- a/allocator/partition_allocator/partition_freelist_entry.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_ - -#include - -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/compiler_specific.h" -#include "base/sys_byteorder.h" -#include "build/build_config.h" - -namespace base { -namespace internal { - -// TODO(ajwong): Introduce an EncodedFreelistEntry type and then replace -// Transform() with Encode()/Decode() such that the API provides some static -// type safety. -// -// https://crbug.com/787153 -struct PartitionFreelistEntry { - PartitionFreelistEntry* next; - - static ALWAYS_INLINE PartitionFreelistEntry* Transform( - PartitionFreelistEntry* ptr) { -// We use bswap on little endian as a fast mask for two reasons: -// 1) If an object is freed and its vtable used where the attacker doesn't -// get the chance to run allocations between the free and use, the vtable -// dereference is likely to fault. -// 2) If the attacker has a linear buffer overflow and elects to try and -// corrupt a freelist pointer, partial pointer overwrite attacks are -// thwarted. -// For big endian, similar guarantees are arrived at with a negation. -#if defined(ARCH_CPU_BIG_ENDIAN) - uintptr_t masked = ~reinterpret_cast(ptr); -#else - uintptr_t masked = ByteSwapUintPtrT(reinterpret_cast(ptr)); -#endif - return reinterpret_cast(masked); - } -}; - -} // namespace internal -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_ diff --git a/allocator/partition_allocator/partition_oom.cc b/allocator/partition_allocator/partition_oom.cc deleted file mode 100644 index 5e1cf79ea..000000000 --- a/allocator/partition_allocator/partition_oom.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_oom.h" - -#include "base/allocator/partition_allocator/oom.h" -#include "build/build_config.h" - -namespace base { -namespace internal { - -void NOINLINE PartitionExcessiveAllocationSize() { - OOM_CRASH(); -} - -#if !defined(ARCH_CPU_64_BITS) -NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages() { - OOM_CRASH(); -} -#endif - -} // namespace internal -} // namespace base diff --git a/allocator/partition_allocator/partition_oom.h b/allocator/partition_allocator/partition_oom.h deleted file mode 100644 index da8fc15a5..000000000 --- a/allocator/partition_allocator/partition_oom.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Holds functions for generating OOM errors from PartitionAlloc. This is -// distinct from oom.h in that it is meant only for use in PartitionAlloc. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_ - -#include "base/compiler_specific.h" -#include "build/build_config.h" - -namespace base { -namespace internal { - -NOINLINE void PartitionExcessiveAllocationSize(); - -#if !defined(ARCH_CPU_64_BITS) -NOINLINE void PartitionOutOfMemoryWithLotsOfUncommitedPages(); -#endif - -} // namespace internal -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_ diff --git a/allocator/partition_allocator/partition_page.cc b/allocator/partition_allocator/partition_page.cc deleted file mode 100644 index 3c9e0419c..000000000 --- a/allocator/partition_allocator/partition_page.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_page.h" - -#include "base/allocator/partition_allocator/partition_direct_map_extent.h" -#include "base/allocator/partition_allocator/partition_root_base.h" - -namespace base { -namespace internal { - -namespace { - -ALWAYS_INLINE void PartitionDirectUnmap(PartitionPage* page) { - PartitionRootBase* root = PartitionRootBase::FromPage(page); - const PartitionDirectMapExtent* extent = - PartitionDirectMapExtent::FromPage(page); - size_t unmap_size = extent->map_size; - - // Maintain the doubly-linked list of all direct mappings. - if (extent->prev_extent) { - DCHECK(extent->prev_extent->next_extent == extent); - extent->prev_extent->next_extent = extent->next_extent; - } else { - root->direct_map_list = extent->next_extent; - } - if (extent->next_extent) { - DCHECK(extent->next_extent->prev_extent == extent); - extent->next_extent->prev_extent = extent->prev_extent; - } - - // Add on the size of the trailing guard page and preceeding partition - // page. - unmap_size += kPartitionPageSize + kSystemPageSize; - - size_t uncommitted_page_size = page->bucket->slot_size + kSystemPageSize; - root->DecreaseCommittedPages(uncommitted_page_size); - DCHECK(root->total_size_of_direct_mapped_pages >= uncommitted_page_size); - root->total_size_of_direct_mapped_pages -= uncommitted_page_size; - - DCHECK(!(unmap_size & kPageAllocationGranularityOffsetMask)); - - char* ptr = reinterpret_cast(PartitionPage::ToPointer(page)); - // Account for the mapping starting a partition page before the actual - // allocation address. - ptr -= kPartitionPageSize; - - FreePages(ptr, unmap_size); -} - -ALWAYS_INLINE void PartitionRegisterEmptyPage(PartitionPage* page) { - DCHECK(page->is_empty()); - PartitionRootBase* root = PartitionRootBase::FromPage(page); - - // If the page is already registered as empty, give it another life. - if (page->empty_cache_index != -1) { - DCHECK(page->empty_cache_index >= 0); - DCHECK(static_cast(page->empty_cache_index) < kMaxFreeableSpans); - DCHECK(root->global_empty_page_ring[page->empty_cache_index] == page); - root->global_empty_page_ring[page->empty_cache_index] = nullptr; - } - - int16_t current_index = root->global_empty_page_ring_index; - PartitionPage* page_to_decommit = root->global_empty_page_ring[current_index]; - // The page might well have been re-activated, filled up, etc. before we get - // around to looking at it here. - if (page_to_decommit) - page_to_decommit->DecommitIfPossible(root); - - // We put the empty slot span on our global list of "pages that were once - // empty". thus providing it a bit of breathing room to get re-used before - // we really free it. This improves performance, particularly on Mac OS X - // which has subpar memory management performance. - root->global_empty_page_ring[current_index] = page; - page->empty_cache_index = current_index; - ++current_index; - if (current_index == kMaxFreeableSpans) - current_index = 0; - root->global_empty_page_ring_index = current_index; -} - -} // namespace - -// static -PartitionPage PartitionPage::sentinel_page_; - -PartitionPage* PartitionPage::get_sentinel_page() { - return &sentinel_page_; -} - -void PartitionPage::FreeSlowPath() { - DCHECK(this != get_sentinel_page()); - if (LIKELY(this->num_allocated_slots == 0)) { - // Page became fully unused. - if (UNLIKELY(bucket->is_direct_mapped())) { - PartitionDirectUnmap(this); - return; - } - // If it's the current active page, change it. We bounce the page to - // the empty list as a force towards defragmentation. - if (LIKELY(this == bucket->active_pages_head)) - bucket->SetNewActivePage(); - DCHECK(bucket->active_pages_head != this); - - set_raw_size(0); - DCHECK(!get_raw_size()); - - PartitionRegisterEmptyPage(this); - } else { - DCHECK(!bucket->is_direct_mapped()); - // Ensure that the page is full. That's the only valid case if we - // arrive here. - DCHECK(this->num_allocated_slots < 0); - // A transition of num_allocated_slots from 0 to -1 is not legal, and - // likely indicates a double-free. - CHECK(this->num_allocated_slots != -1); - this->num_allocated_slots = -this->num_allocated_slots - 2; - DCHECK(this->num_allocated_slots == bucket->get_slots_per_span() - 1); - // Fully used page became partially used. It must be put back on the - // non-full page list. Also make it the current page to increase the - // chances of it being filled up again. The old current page will be - // the next page. - DCHECK(!this->next_page); - if (LIKELY(bucket->active_pages_head != get_sentinel_page())) - this->next_page = bucket->active_pages_head; - bucket->active_pages_head = this; - --bucket->num_full_pages; - // Special case: for a partition page with just a single slot, it may - // now be empty and we want to run it through the empty logic. - if (UNLIKELY(this->num_allocated_slots == 0)) - FreeSlowPath(); - } -} - -void PartitionPage::Decommit(PartitionRootBase* root) { - DCHECK(is_empty()); - DCHECK(!bucket->is_direct_mapped()); - void* addr = PartitionPage::ToPointer(this); - root->DecommitSystemPages(addr, bucket->get_bytes_per_span()); - - // We actually leave the decommitted page in the active list. We'll sweep - // it on to the decommitted page list when we next walk the active page - // list. - // Pulling this trick enables us to use a singly-linked page list for all - // cases, which is critical in keeping the page metadata structure down to - // 32 bytes in size. - freelist_head = nullptr; - num_unprovisioned_slots = 0; - DCHECK(is_decommitted()); -} - -void PartitionPage::DecommitIfPossible(PartitionRootBase* root) { - DCHECK(empty_cache_index >= 0); - DCHECK(static_cast(empty_cache_index) < kMaxFreeableSpans); - DCHECK(this == root->global_empty_page_ring[empty_cache_index]); - empty_cache_index = -1; - if (is_empty()) - Decommit(root); -} - -} // namespace internal -} // namespace base diff --git a/allocator/partition_allocator/partition_page.h b/allocator/partition_allocator/partition_page.h deleted file mode 100644 index e6a6eb74d..000000000 --- a/allocator/partition_allocator/partition_page.h +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_ - -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_bucket.h" -#include "base/allocator/partition_allocator/partition_cookie.h" -#include "base/allocator/partition_allocator/partition_freelist_entry.h" - -namespace base { -namespace internal { - -struct PartitionRootBase; - -// Some notes on page states. A page can be in one of four major states: -// 1) Active. -// 2) Full. -// 3) Empty. -// 4) Decommitted. -// An active page has available free slots. A full page has no free slots. An -// empty page has no free slots, and a decommitted page is an empty page that -// had its backing memory released back to the system. -// There are two linked lists tracking the pages. The "active page" list is an -// approximation of a list of active pages. It is an approximation because -// full, empty and decommitted pages may briefly be present in the list until -// we next do a scan over it. -// The "empty page" list is an accurate list of pages which are either empty -// or decommitted. -// -// The significant page transitions are: -// - free() will detect when a full page has a slot free()'d and immediately -// return the page to the head of the active list. -// - free() will detect when a page is fully emptied. It _may_ add it to the -// empty list or it _may_ leave it on the active list until a future list scan. -// - malloc() _may_ scan the active page list in order to fulfil the request. -// If it does this, full, empty and decommitted pages encountered will be -// booted out of the active list. If there are no suitable active pages found, -// an empty or decommitted page (if one exists) will be pulled from the empty -// list on to the active list. -// -// TODO(ajwong): Evaluate if this should be named PartitionSlotSpanMetadata or -// similar. If so, all uses of the term "page" in comments, member variables, -// local variables, and documentation that refer to this concept should be -// updated. -struct PartitionPage { - PartitionFreelistEntry* freelist_head; - PartitionPage* next_page; - PartitionBucket* bucket; - // Deliberately signed, 0 for empty or decommitted page, -n for full pages: - int16_t num_allocated_slots; - uint16_t num_unprovisioned_slots; - uint16_t page_offset; - int16_t empty_cache_index; // -1 if not in the empty cache. - - // Public API - - // Note the matching Alloc() functions are in PartitionPage. - BASE_EXPORT NOINLINE void FreeSlowPath(); - ALWAYS_INLINE void Free(void* ptr); - - void Decommit(PartitionRootBase* root); - void DecommitIfPossible(PartitionRootBase* root); - - // Pointer manipulation functions. These must be static as the input |page| - // pointer may be the result of an offset calculation and therefore cannot - // be trusted. The objective of these functions is to sanitize this input. - ALWAYS_INLINE static void* ToPointer(const PartitionPage* page); - ALWAYS_INLINE static PartitionPage* FromPointerNoAlignmentCheck(void* ptr); - ALWAYS_INLINE static PartitionPage* FromPointer(void* ptr); - - ALWAYS_INLINE const size_t* get_raw_size_ptr() const; - ALWAYS_INLINE size_t* get_raw_size_ptr() { - return const_cast( - const_cast(this)->get_raw_size_ptr()); - } - - ALWAYS_INLINE size_t get_raw_size() const; - ALWAYS_INLINE void set_raw_size(size_t size); - - ALWAYS_INLINE void Reset(); - - // TODO(ajwong): Can this be made private? https://crbug.com/787153 - BASE_EXPORT static PartitionPage* get_sentinel_page(); - - // Page State accessors. - // Note that it's only valid to call these functions on pages found on one of - // the page lists. Specifically, you can't call these functions on full pages - // that were detached from the active list. - // - // This restriction provides the flexibity for some of the status fields to - // be repurposed when a page is taken off a list. See the negation of - // |num_allocated_slots| when a full page is removed from the active list - // for an example of such repurposing. - ALWAYS_INLINE bool is_active() const; - ALWAYS_INLINE bool is_full() const; - ALWAYS_INLINE bool is_empty() const; - ALWAYS_INLINE bool is_decommitted() const; - - private: - // g_sentinel_page is used as a sentinel to indicate that there is no page - // in the active page list. We can use nullptr, but in that case we need - // to add a null-check branch to the hot allocation path. We want to avoid - // that. - // - // Note, this declaration is kept in the header as opposed to an anonymous - // namespace so the getter can be fully inlined. - static PartitionPage sentinel_page_; -}; -static_assert(sizeof(PartitionPage) <= kPageMetadataSize, - "PartitionPage must be able to fit in a metadata slot"); - -ALWAYS_INLINE char* PartitionSuperPageToMetadataArea(char* ptr) { - uintptr_t pointer_as_uint = reinterpret_cast(ptr); - DCHECK(!(pointer_as_uint & kSuperPageOffsetMask)); - // The metadata area is exactly one system page (the guard page) into the - // super page. - return reinterpret_cast(pointer_as_uint + kSystemPageSize); -} - -ALWAYS_INLINE PartitionPage* PartitionPage::FromPointerNoAlignmentCheck( - void* ptr) { - uintptr_t pointer_as_uint = reinterpret_cast(ptr); - char* super_page_ptr = - reinterpret_cast(pointer_as_uint & kSuperPageBaseMask); - uintptr_t partition_page_index = - (pointer_as_uint & kSuperPageOffsetMask) >> kPartitionPageShift; - // Index 0 is invalid because it is the metadata and guard area and - // the last index is invalid because it is a guard page. - DCHECK(partition_page_index); - DCHECK(partition_page_index < kNumPartitionPagesPerSuperPage - 1); - PartitionPage* page = reinterpret_cast( - PartitionSuperPageToMetadataArea(super_page_ptr) + - (partition_page_index << kPageMetadataShift)); - // Partition pages in the same slot span can share the same page object. - // Adjust for that. - size_t delta = page->page_offset << kPageMetadataShift; - page = - reinterpret_cast(reinterpret_cast(page) - delta); - return page; -} - -// Resturns start of the slot span for the PartitionPage. -ALWAYS_INLINE void* PartitionPage::ToPointer(const PartitionPage* page) { - uintptr_t pointer_as_uint = reinterpret_cast(page); - - uintptr_t super_page_offset = (pointer_as_uint & kSuperPageOffsetMask); - - // A valid |page| must be past the first guard System page and within - // the following metadata region. - DCHECK(super_page_offset > kSystemPageSize); - // Must be less than total metadata region. - DCHECK(super_page_offset < kSystemPageSize + (kNumPartitionPagesPerSuperPage * - kPageMetadataSize)); - uintptr_t partition_page_index = - (super_page_offset - kSystemPageSize) >> kPageMetadataShift; - // Index 0 is invalid because it is the superpage extent metadata and the - // last index is invalid because the whole PartitionPage is set as guard - // pages for the metadata region. - DCHECK(partition_page_index); - DCHECK(partition_page_index < kNumPartitionPagesPerSuperPage - 1); - uintptr_t super_page_base = (pointer_as_uint & kSuperPageBaseMask); - void* ret = reinterpret_cast( - super_page_base + (partition_page_index << kPartitionPageShift)); - return ret; -} - -ALWAYS_INLINE PartitionPage* PartitionPage::FromPointer(void* ptr) { - PartitionPage* page = PartitionPage::FromPointerNoAlignmentCheck(ptr); - // Checks that the pointer is a multiple of bucket size. - DCHECK(!((reinterpret_cast(ptr) - - reinterpret_cast(PartitionPage::ToPointer(page))) % - page->bucket->slot_size)); - return page; -} - -ALWAYS_INLINE const size_t* PartitionPage::get_raw_size_ptr() const { - // For single-slot buckets which span more than one partition page, we - // have some spare metadata space to store the raw allocation size. We - // can use this to report better statistics. - if (bucket->slot_size <= kMaxSystemPagesPerSlotSpan * kSystemPageSize) - return nullptr; - - DCHECK((bucket->slot_size % kSystemPageSize) == 0); - DCHECK(bucket->is_direct_mapped() || bucket->get_slots_per_span() == 1); - - const PartitionPage* the_next_page = this + 1; - return reinterpret_cast(&the_next_page->freelist_head); -} - -ALWAYS_INLINE size_t PartitionPage::get_raw_size() const { - const size_t* ptr = get_raw_size_ptr(); - if (UNLIKELY(ptr != nullptr)) - return *ptr; - return 0; -} - -ALWAYS_INLINE void PartitionPage::Free(void* ptr) { -// If these asserts fire, you probably corrupted memory. -#if DCHECK_IS_ON() - size_t slot_size = this->bucket->slot_size; - size_t raw_size = get_raw_size(); - if (raw_size) - slot_size = raw_size; - PartitionCookieCheckValue(ptr); - PartitionCookieCheckValue(reinterpret_cast(ptr) + slot_size - - kCookieSize); - memset(ptr, kFreedByte, slot_size); -#endif - DCHECK(this->num_allocated_slots); - // TODO(palmer): See if we can afford to make this a CHECK. - // FIX FIX FIX - // DCHECK(!freelist_head || PartitionRootBase::IsValidPage( - // PartitionPage::FromPointer(freelist_head))); - CHECK(ptr != freelist_head); // Catches an immediate double free. - // Look for double free one level deeper in debug. - DCHECK(!freelist_head || ptr != internal::PartitionFreelistEntry::Transform( - freelist_head->next)); - internal::PartitionFreelistEntry* entry = - static_cast(ptr); - entry->next = internal::PartitionFreelistEntry::Transform(freelist_head); - freelist_head = entry; - --this->num_allocated_slots; - if (UNLIKELY(this->num_allocated_slots <= 0)) { - FreeSlowPath(); - } else { - // All single-slot allocations must go through the slow path to - // correctly update the size metadata. - DCHECK(get_raw_size() == 0); - } -} - -ALWAYS_INLINE bool PartitionPage::is_active() const { - DCHECK(this != get_sentinel_page()); - DCHECK(!page_offset); - return (num_allocated_slots > 0 && - (freelist_head || num_unprovisioned_slots)); -} - -ALWAYS_INLINE bool PartitionPage::is_full() const { - DCHECK(this != get_sentinel_page()); - DCHECK(!page_offset); - bool ret = (num_allocated_slots == bucket->get_slots_per_span()); - if (ret) { - DCHECK(!freelist_head); - DCHECK(!num_unprovisioned_slots); - } - return ret; -} - -ALWAYS_INLINE bool PartitionPage::is_empty() const { - DCHECK(this != get_sentinel_page()); - DCHECK(!page_offset); - return (!num_allocated_slots && freelist_head); -} - -ALWAYS_INLINE bool PartitionPage::is_decommitted() const { - DCHECK(this != get_sentinel_page()); - DCHECK(!page_offset); - bool ret = (!num_allocated_slots && !freelist_head); - if (ret) { - DCHECK(!num_unprovisioned_slots); - DCHECK(empty_cache_index == -1); - } - return ret; -} - -ALWAYS_INLINE void PartitionPage::set_raw_size(size_t size) { - size_t* raw_size_ptr = get_raw_size_ptr(); - if (UNLIKELY(raw_size_ptr != nullptr)) - *raw_size_ptr = size; -} - -ALWAYS_INLINE void PartitionPage::Reset() { - DCHECK(this->is_decommitted()); - - num_unprovisioned_slots = bucket->get_slots_per_span(); - DCHECK(num_unprovisioned_slots); - - next_page = nullptr; -} - -} // namespace internal -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_ diff --git a/allocator/partition_allocator/partition_root_base.cc b/allocator/partition_allocator/partition_root_base.cc deleted file mode 100644 index 91b998fbf..000000000 --- a/allocator/partition_allocator/partition_root_base.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/partition_root_base.h" - -#include "base/allocator/partition_allocator/oom.h" -#include "base/allocator/partition_allocator/partition_oom.h" -#include "base/allocator/partition_allocator/partition_page.h" -#include "build/build_config.h" - -namespace base { -namespace internal { - -NOINLINE void PartitionRootBase::OutOfMemory() { -#if !defined(ARCH_CPU_64_BITS) - // Check whether this OOM is due to a lot of super pages that are allocated - // but not committed, probably due to http://crbug.com/421387. - if (total_size_of_super_pages + total_size_of_direct_mapped_pages - - total_size_of_committed_pages > - kReasonableSizeOfUnusedPages) { - PartitionOutOfMemoryWithLotsOfUncommitedPages(); - } -#endif - if (PartitionRootBase::gOomHandlingFunction) - (*PartitionRootBase::gOomHandlingFunction)(); - OOM_CRASH(); -} - -void PartitionRootBase::DecommitEmptyPages() { - for (size_t i = 0; i < kMaxFreeableSpans; ++i) { - internal::PartitionPage* page = global_empty_page_ring[i]; - if (page) - page->DecommitIfPossible(this); - global_empty_page_ring[i] = nullptr; - } -} - -} // namespace internal -} // namespace base diff --git a/allocator/partition_allocator/partition_root_base.h b/allocator/partition_allocator/partition_root_base.h deleted file mode 100644 index e20990e40..000000000 --- a/allocator/partition_allocator/partition_root_base.h +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_BASE_H_ -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_BASE_H_ - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/allocator/partition_allocator/partition_alloc_constants.h" -#include "base/allocator/partition_allocator/partition_bucket.h" -#include "base/allocator/partition_allocator/partition_direct_map_extent.h" -#include "base/allocator/partition_allocator/partition_page.h" - -namespace base { -namespace internal { - -struct PartitionPage; -struct PartitionRootBase; - -// An "extent" is a span of consecutive superpages. We link to the partition's -// next extent (if there is one) to the very start of a superpage's metadata -// area. -struct PartitionSuperPageExtentEntry { - PartitionRootBase* root; - char* super_page_base; - char* super_pages_end; - PartitionSuperPageExtentEntry* next; -}; -static_assert( - sizeof(PartitionSuperPageExtentEntry) <= kPageMetadataSize, - "PartitionSuperPageExtentEntry must be able to fit in a metadata slot"); - -struct BASE_EXPORT PartitionRootBase { - PartitionRootBase(); - virtual ~PartitionRootBase(); - size_t total_size_of_committed_pages = 0; - size_t total_size_of_super_pages = 0; - size_t total_size_of_direct_mapped_pages = 0; - // Invariant: total_size_of_committed_pages <= - // total_size_of_super_pages + - // total_size_of_direct_mapped_pages. - unsigned num_buckets = 0; - unsigned max_allocation = 0; - bool initialized = false; - char* next_super_page = nullptr; - char* next_partition_page = nullptr; - char* next_partition_page_end = nullptr; - PartitionSuperPageExtentEntry* current_extent = nullptr; - PartitionSuperPageExtentEntry* first_extent = nullptr; - PartitionDirectMapExtent* direct_map_list = nullptr; - PartitionPage* global_empty_page_ring[kMaxFreeableSpans] = {}; - int16_t global_empty_page_ring_index = 0; - uintptr_t inverted_self = 0; - - // Public API - - // Allocates out of the given bucket. Properly, this function should probably - // be in PartitionBucket, but because the implementation needs to be inlined - // for performance, and because it needs to inspect PartitionPage, - // it becomes impossible to have it in PartitionBucket as this causes a - // cyclical dependency on PartitionPage function implementations. - // - // Moving it a layer lower couples PartitionRootBase and PartitionBucket, but - // preserves the layering of the includes. - // - // Note the matching Free() functions are in PartitionPage. - ALWAYS_INLINE void* AllocFromBucket(PartitionBucket* bucket, - int flags, - size_t size); - - ALWAYS_INLINE static bool IsValidPage(PartitionPage* page); - ALWAYS_INLINE static PartitionRootBase* FromPage(PartitionPage* page); - - // gOomHandlingFunction is invoked when PartitionAlloc hits OutOfMemory. - static void (*gOomHandlingFunction)(); - NOINLINE void OutOfMemory(); - - ALWAYS_INLINE void IncreaseCommittedPages(size_t len); - ALWAYS_INLINE void DecreaseCommittedPages(size_t len); - ALWAYS_INLINE void DecommitSystemPages(void* address, size_t length); - ALWAYS_INLINE void RecommitSystemPages(void* address, size_t length); - - void DecommitEmptyPages(); -}; - -ALWAYS_INLINE void* PartitionRootBase::AllocFromBucket(PartitionBucket* bucket, - int flags, - size_t size) { - PartitionPage* page = bucket->active_pages_head; - // Check that this page is neither full nor freed. - DCHECK(page->num_allocated_slots >= 0); - void* ret = page->freelist_head; - if (LIKELY(ret != 0)) { - // If these DCHECKs fire, you probably corrupted memory. - // TODO(palmer): See if we can afford to make this a CHECK. - DCHECK(PartitionRootBase::IsValidPage(page)); - // All large allocations must go through the slow path to correctly - // update the size metadata. - DCHECK(page->get_raw_size() == 0); - internal::PartitionFreelistEntry* new_head = - internal::PartitionFreelistEntry::Transform( - static_cast(ret)->next); - page->freelist_head = new_head; - page->num_allocated_slots++; - } else { - ret = bucket->SlowPathAlloc(this, flags, size); - // TODO(palmer): See if we can afford to make this a CHECK. - DCHECK(!ret || - PartitionRootBase::IsValidPage(PartitionPage::FromPointer(ret))); - } -#if DCHECK_IS_ON() - if (!ret) - return 0; - // Fill the uninitialized pattern, and write the cookies. - page = PartitionPage::FromPointer(ret); - // TODO(ajwong): Can |page->bucket| ever not be |this|? If not, can this just - // be bucket->slot_size? - size_t new_slot_size = page->bucket->slot_size; - size_t raw_size = page->get_raw_size(); - if (raw_size) { - DCHECK(raw_size == size); - new_slot_size = raw_size; - } - size_t no_cookie_size = PartitionCookieSizeAdjustSubtract(new_slot_size); - char* char_ret = static_cast(ret); - // The value given to the application is actually just after the cookie. - ret = char_ret + kCookieSize; - - // Debug fill region kUninitializedByte and surround it with 2 cookies. - PartitionCookieWriteValue(char_ret); - memset(ret, kUninitializedByte, no_cookie_size); - PartitionCookieWriteValue(char_ret + kCookieSize + no_cookie_size); -#endif - return ret; -} - -ALWAYS_INLINE bool PartitionRootBase::IsValidPage(PartitionPage* page) { - PartitionRootBase* root = PartitionRootBase::FromPage(page); - return root->inverted_self == ~reinterpret_cast(root); -} - -ALWAYS_INLINE PartitionRootBase* PartitionRootBase::FromPage( - PartitionPage* page) { - PartitionSuperPageExtentEntry* extent_entry = - reinterpret_cast( - reinterpret_cast(page) & kSystemPageBaseMask); - return extent_entry->root; -} - -ALWAYS_INLINE void PartitionRootBase::IncreaseCommittedPages(size_t len) { - total_size_of_committed_pages += len; - DCHECK(total_size_of_committed_pages <= - total_size_of_super_pages + total_size_of_direct_mapped_pages); -} - -ALWAYS_INLINE void PartitionRootBase::DecreaseCommittedPages(size_t len) { - total_size_of_committed_pages -= len; - DCHECK(total_size_of_committed_pages <= - total_size_of_super_pages + total_size_of_direct_mapped_pages); -} - -ALWAYS_INLINE void PartitionRootBase::DecommitSystemPages(void* address, - size_t length) { - ::base::DecommitSystemPages(address, length); - DecreaseCommittedPages(length); -} - -ALWAYS_INLINE void PartitionRootBase::RecommitSystemPages(void* address, - size_t length) { - CHECK(::base::RecommitSystemPages(address, length, PageReadWrite)); - IncreaseCommittedPages(length); -} - -} // namespace internal -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_BASE_H_ diff --git a/allocator/partition_allocator/spin_lock.cc b/allocator/partition_allocator/spin_lock.cc deleted file mode 100644 index 46f496586..000000000 --- a/allocator/partition_allocator/spin_lock.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/spin_lock.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#endif - -#include "base/threading/platform_thread.h" - -// The YIELD_PROCESSOR macro wraps an architecture specific-instruction that -// informs the processor we're in a busy wait, so it can handle the branch more -// intelligently and e.g. reduce power to our core or give more resources to the -// other hyper-thread on this core. See the following for context: -// https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops -// -// The YIELD_THREAD macro tells the OS to relinquish our quantum. This is -// basically a worst-case fallback, and if you're hitting it with any frequency -// you really should be using a proper lock (such as |base::Lock|)rather than -// these spinlocks. -#if defined(OS_WIN) - -#define YIELD_PROCESSOR YieldProcessor() -#define YIELD_THREAD SwitchToThread() - -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - -#if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_X86) -#define YIELD_PROCESSOR __asm__ __volatile__("pause") -#elif (defined(ARCH_CPU_ARMEL) && __ARM_ARCH >= 6) || defined(ARCH_CPU_ARM64) -#define YIELD_PROCESSOR __asm__ __volatile__("yield") -#elif defined(ARCH_CPU_MIPSEL) -// The MIPS32 docs state that the PAUSE instruction is a no-op on older -// architectures (first added in MIPS32r2). To avoid assembler errors when -// targeting pre-r2, we must encode the instruction manually. -#define YIELD_PROCESSOR __asm__ __volatile__(".word 0x00000140") -#elif defined(ARCH_CPU_MIPS64EL) && __mips_isa_rev >= 2 -// Don't bother doing using .word here since r2 is the lowest supported mips64 -// that Chromium supports. -#define YIELD_PROCESSOR __asm__ __volatile__("pause") -#elif defined(ARCH_CPU_PPC64_FAMILY) -#define YIELD_PROCESSOR __asm__ __volatile__("or 31,31,31") -#elif defined(ARCH_CPU_S390_FAMILY) -// just do nothing -#define YIELD_PROCESSOR ((void)0) -#endif // ARCH - -#ifndef YIELD_PROCESSOR -#warning "Processor yield not supported on this architecture." -#define YIELD_PROCESSOR ((void)0) -#endif - -#define YIELD_THREAD sched_yield() - -#else // Other OS - -#warning "Thread yield not supported on this OS." -#define YIELD_THREAD ((void)0) - -#endif // OS_WIN - -namespace base { -namespace subtle { - -void SpinLock::LockSlow() { - // The value of |kYieldProcessorTries| is cargo culted from TCMalloc, Windows - // critical section defaults, and various other recommendations. - // TODO(jschuh): Further tuning may be warranted. - static const int kYieldProcessorTries = 1000; - // The value of |kYieldThreadTries| is completely made up. - static const int kYieldThreadTries = 10; - int yield_thread_count = 0; - do { - do { - for (int count = 0; count < kYieldProcessorTries; ++count) { - // Let the processor know we're spinning. - YIELD_PROCESSOR; - if (!lock_.load(std::memory_order_relaxed) && - LIKELY(!lock_.exchange(true, std::memory_order_acquire))) - return; - } - - if (yield_thread_count < kYieldThreadTries) { - ++yield_thread_count; - // Give the OS a chance to schedule something on this core. - YIELD_THREAD; - } else { - // At this point, it's likely that the lock is held by a lower priority - // thread that is unavailable to finish its work because of higher - // priority threads spinning here. Sleeping should ensure that they make - // progress. - PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1)); - } - } while (lock_.load(std::memory_order_relaxed)); - } while (UNLIKELY(lock_.exchange(true, std::memory_order_acquire))); -} - -} // namespace subtle -} // namespace base diff --git a/allocator/partition_allocator/spin_lock.h b/allocator/partition_allocator/spin_lock.h deleted file mode 100644 index e698b565b..000000000 --- a/allocator/partition_allocator/spin_lock.h +++ /dev/null @@ -1,50 +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_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H -#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H - -#include -#include -#include - -#include "base/base_export.h" -#include "base/compiler_specific.h" - -// Spinlock is a simple spinlock class based on the standard CPU primitive of -// atomic increment and decrement of an int at a given memory address. These are -// intended only for very short duration locks and assume a system with multiple -// cores. For any potentially longer wait you should use a real lock, such as -// |base::Lock|. -namespace base { -namespace subtle { - -class BASE_EXPORT SpinLock { - public: - constexpr SpinLock() = default; - ~SpinLock() = default; - using Guard = std::lock_guard; - - ALWAYS_INLINE void lock() { - static_assert(sizeof(lock_) == sizeof(int), - "int and lock_ are different sizes"); - if (LIKELY(!lock_.exchange(true, std::memory_order_acquire))) - return; - LockSlow(); - } - - ALWAYS_INLINE void unlock() { lock_.store(false, std::memory_order_release); } - - private: - // This is called if the initial attempt to acquire the lock fails. It's - // slower, but has a much better scheduling and power consumption behavior. - void LockSlow(); - - std::atomic_int lock_{0}; -}; - -} // namespace subtle -} // namespace base - -#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SPIN_LOCK_H diff --git a/allocator/partition_allocator/spin_lock_unittest.cc b/allocator/partition_allocator/spin_lock_unittest.cc deleted file mode 100644 index 6a1fd6b5d..000000000 --- a/allocator/partition_allocator/spin_lock_unittest.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/allocator/partition_allocator/spin_lock.h" - -#include -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/threading/thread.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -static const size_t kBufferSize = 16; - -static subtle::SpinLock g_lock; - -static void FillBuffer(volatile char* buffer, char fill_pattern) { - for (size_t i = 0; i < kBufferSize; ++i) - buffer[i] = fill_pattern; -} - -static void ChangeAndCheckBuffer(volatile char* buffer) { - FillBuffer(buffer, '\0'); - int total = 0; - for (size_t i = 0; i < kBufferSize; ++i) - total += buffer[i]; - - EXPECT_EQ(0, total); - - // This will mess with the other thread's calculation if we accidentally get - // concurrency. - FillBuffer(buffer, '!'); -} - -static void ThreadMain(volatile char* buffer) { - for (int i = 0; i < 500000; ++i) { - subtle::SpinLock::Guard guard(g_lock); - ChangeAndCheckBuffer(buffer); - } -} - -TEST(SpinLockTest, Torture) { - char shared_buffer[kBufferSize]; - - Thread thread1("thread1"); - Thread thread2("thread2"); - - thread1.StartAndWaitForTesting(); - thread2.StartAndWaitForTesting(); - - thread1.task_runner()->PostTask( - FROM_HERE, - BindOnce(&ThreadMain, Unretained(static_cast(shared_buffer)))); - thread2.task_runner()->PostTask( - FROM_HERE, - BindOnce(&ThreadMain, Unretained(static_cast(shared_buffer)))); -} - -} // namespace base diff --git a/allocator/tcmalloc_unittest.cc b/allocator/tcmalloc_unittest.cc deleted file mode 100644 index 78c4f84ce..000000000 --- a/allocator/tcmalloc_unittest.cc +++ /dev/null @@ -1,235 +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 -#include - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/process/process_metrics.h" -#include "base/sys_info.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(USE_TCMALLOC) -namespace { - -using std::min; - -#ifdef NDEBUG -// We wrap malloc and free in noinline functions to ensure that we test the real -// implementation of the allocator. Otherwise, the compiler may specifically -// recognize the calls to malloc and free in our tests and optimize them away. -NOINLINE void* TCMallocDoMallocForTest(size_t size) { - return malloc(size); -} - -NOINLINE void TCMallocDoFreeForTest(void* ptr) { - free(ptr); -} -#endif - -// Fill a buffer of the specified size with a predetermined pattern -static void Fill(unsigned char* buffer, int n) { - for (int i = 0; i < n; i++) { - buffer[i] = (i & 0xff); - } -} - -// Check that the specified buffer has the predetermined pattern -// generated by Fill() -static bool Valid(unsigned char* buffer, int n) { - for (int i = 0; i < n; i++) { - if (buffer[i] != (i & 0xff)) { - return false; - } - } - return true; -} - -// Return the next interesting size/delta to check. Returns -1 if no more. -static int NextSize(int size) { - if (size < 100) - return size + 1; - - if (size < 100000) { - // Find next power of two - int power = 1; - while (power < size) - power <<= 1; - - // Yield (power-1, power, power+1) - if (size < power - 1) - return power - 1; - - if (size == power - 1) - return power; - - CHECK_EQ(size, power); - return power + 1; - } else { - return -1; - } -} - -static void TestCalloc(size_t n, size_t s, bool ok) { - char* p = reinterpret_cast(calloc(n, s)); - if (!ok) { - EXPECT_EQ(nullptr, p) << "calloc(n, s) should not succeed"; - } else { - EXPECT_NE(reinterpret_cast(NULL), p) - << "calloc(n, s) should succeed"; - for (size_t i = 0; i < n * s; i++) { - EXPECT_EQ('\0', p[i]); - } - free(p); - } -} - -bool IsLowMemoryDevice() { - return base::SysInfo::AmountOfPhysicalMemory() <= 256LL * 1024 * 1024; -} - -} // namespace - -TEST(TCMallocTest, Malloc) { - // Try allocating data with a bunch of alignments and sizes - for (int size = 1; size < 1048576; size *= 2) { - unsigned char* ptr = reinterpret_cast(malloc(size)); - // Should be 2 byte aligned - EXPECT_EQ(0u, reinterpret_cast(ptr) & 1); - Fill(ptr, size); - EXPECT_TRUE(Valid(ptr, size)); - free(ptr); - } -} - -TEST(TCMallocTest, Calloc) { - TestCalloc(0, 0, true); - TestCalloc(0, 1, true); - TestCalloc(1, 1, true); - TestCalloc(1 << 10, 0, true); - TestCalloc(1 << 20, 0, true); - TestCalloc(0, 1 << 10, true); - TestCalloc(0, 1 << 20, true); - TestCalloc(1 << 20, 2, true); - TestCalloc(2, 1 << 20, true); - TestCalloc(1000, 1000, true); -} - -#ifdef NDEBUG -// This makes sure that reallocing a small number of bytes in either -// direction doesn't cause us to allocate new memory. Tcmalloc in debug mode -// does not follow this. -TEST(TCMallocTest, ReallocSmallDelta) { - int start_sizes[] = {100, 1000, 10000, 100000}; - int deltas[] = {1, -2, 4, -8, 16, -32, 64, -128}; - - for (unsigned s = 0; s < sizeof(start_sizes) / sizeof(*start_sizes); ++s) { - void* p = malloc(start_sizes[s]); - ASSERT_TRUE(p); - // The larger the start-size, the larger the non-reallocing delta. - for (unsigned d = 0; d < s * 2; ++d) { - void* new_p = realloc(p, start_sizes[s] + deltas[d]); - ASSERT_EQ(p, new_p); // realloc should not allocate new memory - } - // Test again, but this time reallocing smaller first. - for (unsigned d = 0; d < s * 2; ++d) { - void* new_p = realloc(p, start_sizes[s] - deltas[d]); - ASSERT_EQ(p, new_p); // realloc should not allocate new memory - } - free(p); - } -} -#endif - -TEST(TCMallocTest, Realloc) { - for (int src_size = 0; src_size >= 0; src_size = NextSize(src_size)) { - for (int dst_size = 0; dst_size >= 0; dst_size = NextSize(dst_size)) { - unsigned char* src = reinterpret_cast(malloc(src_size)); - Fill(src, src_size); - unsigned char* dst = - reinterpret_cast(realloc(src, dst_size)); - EXPECT_TRUE(Valid(dst, min(src_size, dst_size))); - Fill(dst, dst_size); - EXPECT_TRUE(Valid(dst, dst_size)); - if (dst != nullptr) - free(dst); - } - } - - // The logic below tries to allocate kNumEntries * 9000 ~= 130 MB of memory. - // This would cause the test to crash on low memory devices with no VM - // overcommit (e.g., chromecast). - if (IsLowMemoryDevice()) - return; - - // Now make sure realloc works correctly even when we overflow the - // packed cache, so some entries are evicted from the cache. - // The cache has 2^12 entries, keyed by page number. - const int kNumEntries = 1 << 14; - int** p = reinterpret_cast(malloc(sizeof(*p) * kNumEntries)); - int sum = 0; - for (int i = 0; i < kNumEntries; i++) { - // no page size is likely to be bigger than 8192? - p[i] = reinterpret_cast(malloc(8192)); - p[i][1000] = i; // use memory deep in the heart of p - } - for (int i = 0; i < kNumEntries; i++) { - p[i] = reinterpret_cast(realloc(p[i], 9000)); - } - for (int i = 0; i < kNumEntries; i++) { - sum += p[i][1000]; - free(p[i]); - } - EXPECT_EQ(kNumEntries / 2 * (kNumEntries - 1), sum); // assume kNE is even - free(p); -} - -#ifdef NDEBUG -TEST(TCMallocFreeTest, BadPointerInFirstPageOfTheLargeObject) { - const size_t kPageSize = base::GetPageSize(); - char* p = - reinterpret_cast(TCMallocDoMallocForTest(10 * kPageSize + 1)); - for (unsigned offset = 1; offset < kPageSize; offset <<= 1) { - ASSERT_DEATH(TCMallocDoFreeForTest(p + offset), - "Pointer is not pointing to the start of a span"); - } - TCMallocDoFreeForTest(p); -} - -// TODO(ssid): Fix flakiness and enable the test, crbug.com/571549. -TEST(TCMallocFreeTest, DISABLED_BadPageAlignedPointerInsideLargeObject) { - const size_t kPageSize = base::GetPageSize(); - const size_t kMaxSize = 10 * kPageSize; - char* p = reinterpret_cast(TCMallocDoMallocForTest(kMaxSize + 1)); - - for (unsigned offset = kPageSize; offset < kMaxSize; offset += kPageSize) { - // Only the first and last page of a span are in heap map. So for others - // tcmalloc will give a general error of invalid pointer. - ASSERT_DEATH(TCMallocDoFreeForTest(p + offset), ""); - } - ASSERT_DEATH(TCMallocDoFreeForTest(p + kMaxSize), - "Pointer is not pointing to the start of a span"); - TCMallocDoFreeForTest(p); -} - -TEST(TCMallocFreeTest, DoubleFreeLargeObject) { - const size_t kMaxSize = 10 * base::GetPageSize(); - char* p = reinterpret_cast(TCMallocDoMallocForTest(kMaxSize + 1)); - ASSERT_DEATH(TCMallocDoFreeForTest(p); TCMallocDoFreeForTest(p), - "Object was not in-use"); -} - -TEST(TCMallocFreeTest, DoubleFreeSmallObject) { - const size_t kPageSize = base::GetPageSize(); - for (size_t size = 1; size <= kPageSize; size <<= 1) { - char* p = reinterpret_cast(TCMallocDoMallocForTest(size)); - ASSERT_DEATH(TCMallocDoFreeForTest(p); TCMallocDoFreeForTest(p), - "Circular loop in list detected"); - } -} -#endif // NDEBUG - -#endif diff --git a/allocator/unittest_utils.cc b/allocator/unittest_utils.cc deleted file mode 100644 index 051d56856..000000000 --- a/allocator/unittest_utils.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// The unittests need a this in order to link up without pulling in tons -// of other libraries - -#include -#include - -inline int snprintf(char* buffer, size_t count, const char* format, ...) { - int result; - va_list args; - va_start(args, format); - result = _vsnprintf(buffer, count, format, args); - va_end(args); - return result; -} - diff --git a/allocator/winheap_stubs_win.cc b/allocator/winheap_stubs_win.cc deleted file mode 100644 index 8aa52981f..000000000 --- a/allocator/winheap_stubs_win.cc +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2016 The Chromium 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 code should move into the default Windows shim once the win-specific -// allocation shim has been removed, and the generic shim has becaome the -// default. - -#include "winheap_stubs_win.h" - -#include -#include -#include -#include - -namespace base { -namespace allocator { - -bool g_is_win_shim_layer_initialized = false; - -namespace { - -const size_t kWindowsPageSize = 4096; -const size_t kMaxWindowsAllocation = INT_MAX - kWindowsPageSize; - -inline HANDLE get_heap_handle() { - return reinterpret_cast(_get_heap_handle()); -} - -} // namespace - -void* WinHeapMalloc(size_t size) { - if (size < kMaxWindowsAllocation) - return HeapAlloc(get_heap_handle(), 0, size); - return nullptr; -} - -void WinHeapFree(void* ptr) { - if (!ptr) - return; - - HeapFree(get_heap_handle(), 0, ptr); -} - -void* WinHeapRealloc(void* ptr, size_t size) { - if (!ptr) - return WinHeapMalloc(size); - if (!size) { - WinHeapFree(ptr); - return nullptr; - } - if (size < kMaxWindowsAllocation) - return HeapReAlloc(get_heap_handle(), 0, ptr, size); - return nullptr; -} - -size_t WinHeapGetSizeEstimate(void* ptr) { - if (!ptr) - return 0; - - // Get the user size of the allocation. - size_t size = HeapSize(get_heap_handle(), 0, ptr); - - // Account for the 8-byte HEAP_HEADER preceding the block. - size += 8; - -// Round up to the nearest allocation granularity, which is 8 for -// 32 bit machines, and 16 for 64 bit machines. -#if defined(ARCH_CPU_64_BITS) - const size_t kAllocationGranularity = 16; -#else - const size_t kAllocationGranularity = 8; -#endif - - return (size + kAllocationGranularity - 1) & ~(kAllocationGranularity - 1); -} - -// Call the new handler, if one has been set. -// Returns true on successfully calling the handler, false otherwise. -bool WinCallNewHandler(size_t size) { -#ifdef _CPPUNWIND -#error "Exceptions in allocator shim are not supported!" -#endif // _CPPUNWIND - // Get the current new handler. - _PNH nh = _query_new_handler(); - if (!nh) - return false; - // Since exceptions are disabled, we don't really know if new_handler - // failed. Assume it will abort if it fails. - return nh(size) ? true : false; -} - -} // namespace allocator -} // namespace base diff --git a/allocator/winheap_stubs_win.h b/allocator/winheap_stubs_win.h deleted file mode 100644 index 422dfe0da..000000000 --- a/allocator/winheap_stubs_win.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Thin allocation wrappers for the windows heap. This file should be deleted -// once the win-specific allocation shim has been removed, and the generic shim -// has becaome the default. - -#ifndef BASE_ALLOCATOR_WINHEAP_STUBS_H_ -#define BASE_ALLOCATOR_WINHEAP_STUBS_H_ - -#include - -namespace base { -namespace allocator { - -// Set to true if the link-time magic has successfully hooked into the CRT's -// heap initialization. -extern bool g_is_win_shim_layer_initialized; - -// Thin wrappers to implement the standard C allocation semantics on the -// CRT's Windows heap. -void* WinHeapMalloc(size_t size); -void WinHeapFree(void* ptr); -void* WinHeapRealloc(void* ptr, size_t size); - -// Returns a lower-bound estimate for the full amount of memory consumed by the -// the allocation |ptr|. -size_t WinHeapGetSizeEstimate(void* ptr); - -// Call the new handler, if one has been set. -// Returns true on successfully calling the handler, false otherwise. -bool WinCallNewHandler(size_t size); - -} // namespace allocator -} // namespace base - -#endif // BASE_ALLOCATOR_WINHEAP_STUBS_H_ \ No newline at end of file diff --git a/android/OWNERS b/android/OWNERS deleted file mode 100644 index 5c4095832..000000000 --- a/android/OWNERS +++ /dev/null @@ -1,8 +0,0 @@ -agrieve@chromium.org -nyquist@chromium.org -rmcilroy@chromium.org -torne@chromium.org -yfriedman@chromium.org - -per-file *.aidl=set noparent -per-file *.aidl=file://ipc/SECURITY_OWNERS diff --git a/android/android_hardware_buffer_abi.h b/android/android_hardware_buffer_abi.h deleted file mode 100644 index 7012532d1..000000000 --- a/android/android_hardware_buffer_abi.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2017 The Chromium 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_ANDROID_HARDWARE_BUFFER_ABI_H_ -#define BASE_ANDROID_ANDROID_HARDWARE_BUFFER_ABI_H_ - -// Minimal binary interface definitions for AHardwareBuffer based on -// include/android/hardware_buffer.h from the Android NDK for platform level -// 26+. This is only intended for use from the AndroidHardwareBufferCompat -// wrapper for building without NDK platform level support, it is not a -// general-use header and is not complete. -// -// TODO(crbug.com/771171): Delete this file when third_party/android_ndk/ -// is updated to a version that contains the android/hardware_buffer.h file. -// -// Please refer to the API documentation for details: -// https://developer.android.com/ndk/reference/hardware__buffer_8h.html - -#include - -// Use "C" linkage to match the original header file. This isn't strictly -// required since the file is not declaring global functions, but the types -// should remain in the global namespace for compatibility, and it's a reminder -// that forward declarations elsewhere should use "extern "C" to avoid -// namespace issues. -extern "C" { - -typedef struct AHardwareBuffer AHardwareBuffer; -typedef struct ARect ARect; - -enum { - AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM = 1, - AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM = 2, - AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM = 3, - AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM = 4, - AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT = 0x16, - AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM = 0x2b, - AHARDWAREBUFFER_FORMAT_BLOB = 0x21, -}; - -enum { - AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = 0UL, - AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = 2UL, - AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = 3UL, - AHARDWAREBUFFER_USAGE_CPU_READ_MASK = 0xFUL, - AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = 0UL << 4, - AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = 2UL << 4, - AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN = 3UL << 4, - AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 0xFUL << 4, - AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8, - AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = 1UL << 9, - AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14, - AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16, - AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23, - AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24, -}; - -typedef struct AHardwareBuffer_Desc { - uint32_t width; - uint32_t height; - uint32_t layers; - uint32_t format; - uint64_t usage; - uint32_t stride; - uint32_t rfu0; - uint64_t rfu1; -} AHardwareBuffer_Desc; - -using PFAHardwareBuffer_allocate = void (*)(const AHardwareBuffer_Desc* desc, - AHardwareBuffer** outBuffer); -using PFAHardwareBuffer_acquire = void (*)(AHardwareBuffer* buffer); -using PFAHardwareBuffer_describe = void (*)(const AHardwareBuffer* buffer, - AHardwareBuffer_Desc* outDesc); -using PFAHardwareBuffer_lock = int (*)(AHardwareBuffer* buffer, - uint64_t usage, - int32_t fence, - const ARect* rect, - void** outVirtualAddress); -using PFAHardwareBuffer_recvHandleFromUnixSocket = - int (*)(int socketFd, AHardwareBuffer** outBuffer); -using PFAHardwareBuffer_release = void (*)(AHardwareBuffer* buffer); -using PFAHardwareBuffer_sendHandleToUnixSocket = - int (*)(const AHardwareBuffer* buffer, int socketFd); -using PFAHardwareBuffer_unlock = int (*)(AHardwareBuffer* buffer, - int32_t* fence); - -} // extern "C" - -#endif // BASE_ANDROID_ANDROID_HARDWARE_BUFFER_ABI_H_ diff --git a/android/android_hardware_buffer_compat.cc b/android/android_hardware_buffer_compat.cc deleted file mode 100644 index 70f058947..000000000 --- a/android/android_hardware_buffer_compat.cc +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2017 The Chromium 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/android_hardware_buffer_compat.h" - -#include "base/android/build_info.h" -#include "base/lazy_instance.h" -#include "base/logging.h" - -#include - -namespace base { - -namespace { - -static base::LazyInstance::Leaky g_compat = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -AndroidHardwareBufferCompat::AndroidHardwareBufferCompat() { - DCHECK(IsSupportAvailable()); - - // TODO(klausw): If the Chromium build requires __ANDROID_API__ >= 26 at some - // point in the future, we could directly use the global functions instead of - // dynamic loading. However, since this would be incompatible with pre-Oreo - // devices, this is unlikely to happen in the foreseeable future, so just - // unconditionally use dynamic loading. - - // cf. base/android/linker/modern_linker_jni.cc - void* main_dl_handle = dlopen(nullptr, RTLD_NOW); - - *reinterpret_cast(&allocate_) = - dlsym(main_dl_handle, "AHardwareBuffer_allocate"); - DCHECK(allocate_); - - *reinterpret_cast(&acquire_) = - dlsym(main_dl_handle, "AHardwareBuffer_acquire"); - DCHECK(acquire_); - - *reinterpret_cast(&describe_) = - dlsym(main_dl_handle, "AHardwareBuffer_describe"); - DCHECK(describe_); - - *reinterpret_cast(&lock_) = - dlsym(main_dl_handle, "AHardwareBuffer_lock"); - DCHECK(lock_); - - *reinterpret_cast(&recv_handle_) = - dlsym(main_dl_handle, "AHardwareBuffer_recvHandleFromUnixSocket"); - DCHECK(recv_handle_); - - *reinterpret_cast(&release_) = - dlsym(main_dl_handle, "AHardwareBuffer_release"); - DCHECK(release_); - - *reinterpret_cast(&send_handle_) = - dlsym(main_dl_handle, "AHardwareBuffer_sendHandleToUnixSocket"); - DCHECK(send_handle_); - - *reinterpret_cast(&unlock_) = - dlsym(main_dl_handle, "AHardwareBuffer_unlock"); - DCHECK(unlock_); -} - -// static -bool AndroidHardwareBufferCompat::IsSupportAvailable() { - return base::android::BuildInfo::GetInstance()->sdk_int() >= - base::android::SDK_VERSION_OREO; -} - -// static -AndroidHardwareBufferCompat AndroidHardwareBufferCompat::GetInstance() { - return g_compat.Get(); -} - -void AndroidHardwareBufferCompat::Allocate(const AHardwareBuffer_Desc* desc, - AHardwareBuffer** out_buffer) { - DCHECK(IsSupportAvailable()); - allocate_(desc, out_buffer); -} - -void AndroidHardwareBufferCompat::Acquire(AHardwareBuffer* buffer) { - DCHECK(IsSupportAvailable()); - acquire_(buffer); -} - -void AndroidHardwareBufferCompat::Describe(const AHardwareBuffer* buffer, - AHardwareBuffer_Desc* out_desc) { - DCHECK(IsSupportAvailable()); - describe_(buffer, out_desc); -} - -int AndroidHardwareBufferCompat::Lock(AHardwareBuffer* buffer, - uint64_t usage, - int32_t fence, - const ARect* rect, - void** out_virtual_address) { - DCHECK(IsSupportAvailable()); - return lock_(buffer, usage, fence, rect, out_virtual_address); -} - -int AndroidHardwareBufferCompat::RecvHandleFromUnixSocket( - int socket_fd, - AHardwareBuffer** out_buffer) { - DCHECK(IsSupportAvailable()); - return recv_handle_(socket_fd, out_buffer); -} - -void AndroidHardwareBufferCompat::Release(AHardwareBuffer* buffer) { - DCHECK(IsSupportAvailable()); - release_(buffer); -} - -int AndroidHardwareBufferCompat::SendHandleToUnixSocket( - const AHardwareBuffer* buffer, - int socket_fd) { - DCHECK(IsSupportAvailable()); - return send_handle_(buffer, socket_fd); -} - -int AndroidHardwareBufferCompat::Unlock(AHardwareBuffer* buffer, - int32_t* fence) { - DCHECK(IsSupportAvailable()); - return unlock_(buffer, fence); -} - -} // namespace base diff --git a/android/android_hardware_buffer_compat.h b/android/android_hardware_buffer_compat.h deleted file mode 100644 index 14be3d5b9..000000000 --- a/android/android_hardware_buffer_compat.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2017 The Chromium 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_ANDROID_HARDWARE_BUFFER_COMPAT_H_ -#define BASE_ANDROID_ANDROID_HARDWARE_BUFFER_COMPAT_H_ - -#include "base/android/android_hardware_buffer_abi.h" -#include "base/base_export.h" -#include "base/lazy_instance.h" - -namespace base { - -// This class provides runtime support for working with AHardwareBuffer objects -// on Android O systems without requiring building for the Android O NDK level. -// Don't call GetInstance() unless IsSupportAvailable() returns true. -class BASE_EXPORT AndroidHardwareBufferCompat { - public: - static bool IsSupportAvailable(); - static AndroidHardwareBufferCompat GetInstance(); - - void Allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer); - void Acquire(AHardwareBuffer* buffer); - void Describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc); - int Lock(AHardwareBuffer* buffer, - uint64_t usage, - int32_t fence, - const ARect* rect, - void** out_virtual_address); - int RecvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer); - void Release(AHardwareBuffer* buffer); - int SendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd); - int Unlock(AHardwareBuffer* buffer, int32_t* fence); - - private: - friend struct base::LazyInstanceTraitsBase; - AndroidHardwareBufferCompat(); - - PFAHardwareBuffer_allocate allocate_; - PFAHardwareBuffer_acquire acquire_; - PFAHardwareBuffer_describe describe_; - PFAHardwareBuffer_lock lock_; - PFAHardwareBuffer_recvHandleFromUnixSocket recv_handle_; - PFAHardwareBuffer_release release_; - PFAHardwareBuffer_sendHandleToUnixSocket send_handle_; - PFAHardwareBuffer_unlock unlock_; -}; - -} // namespace base - -#endif // BASE_ANDROID_ANDROID_HARDWARE_BUFFER_COMPAT_H_ diff --git a/android/animation_frame_time_histogram.cc b/android/animation_frame_time_histogram.cc deleted file mode 100644 index 23dffd84a..000000000 --- a/android/animation_frame_time_histogram.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/jni_string.h" -#include "base/metrics/histogram_macros.h" -#include "jni/AnimationFrameTimeHistogram_jni.h" - -using base::android::JavaParamRef; - -// static -void JNI_AnimationFrameTimeHistogram_SaveHistogram( - JNIEnv* env, - const JavaParamRef& jcaller, - const JavaParamRef& j_histogram_name, - const JavaParamRef& j_frame_times_ms, - jint j_count) { - jlong *frame_times_ms = env->GetLongArrayElements(j_frame_times_ms, NULL); - std::string histogram_name = base::android::ConvertJavaStringToUTF8( - env, j_histogram_name); - - for (int i = 0; i < j_count; ++i) { - UMA_HISTOGRAM_TIMES(histogram_name.c_str(), - base::TimeDelta::FromMilliseconds(frame_times_ms[i])); - } -} diff --git a/android/apk_assets.cc b/android/apk_assets.cc deleted file mode 100644 index de468b497..000000000 --- a/android/apk_assets.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "base/android/apk_assets.h" - -#include "base/android/jni_array.h" -#include "base/android/jni_string.h" -#include "base/android/scoped_java_ref.h" -#include "base/file_descriptor_store.h" -#include "jni/ApkAssets_jni.h" - -namespace base { -namespace android { - -int OpenApkAsset(const std::string& file_path, - base::MemoryMappedFile::Region* region) { - // The AssetManager API of the NDK does not expose a method for accessing raw - // resources :( - JNIEnv* env = base::android::AttachCurrentThread(); - ScopedJavaLocalRef jarr = Java_ApkAssets_open( - env, base::android::ConvertUTF8ToJavaString(env, file_path)); - std::vector results; - base::android::JavaLongArrayToLongVector(env, jarr.obj(), &results); - CHECK_EQ(3U, results.size()); - int fd = static_cast(results[0]); - region->offset = results[1]; - region->size = results[2]; - return fd; -} - -bool RegisterApkAssetWithFileDescriptorStore(const std::string& key, - const base::FilePath& file_path) { - base::MemoryMappedFile::Region region = - base::MemoryMappedFile::Region::kWholeFile; - int asset_fd = OpenApkAsset(file_path.value(), ®ion); - if (asset_fd == -1) - return false; - base::FileDescriptorStore::GetInstance().Set(key, base::ScopedFD(asset_fd), - region); - return true; -} - -} // namespace android -} // namespace base diff --git a/android/apk_assets.h b/android/apk_assets.h deleted file mode 100644 index cdac0000a..000000000 --- a/android/apk_assets.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_APK_ASSETS_H_ -#define BASE_ANDROID_APK_ASSETS_H_ - -#include - -#include "base/android/jni_android.h" -#include "base/files/file_path.h" -#include "base/files/memory_mapped_file.h" - -namespace base { -namespace android { - -// Opens an asset (e.g. a .pak file) from the apk. -// Can be used from renderer process. -// Fails if the asset is not stored uncompressed within the .apk. -// Returns: The File Descriptor of the asset, or -1 upon failure. -// Input arguments: -// - |file_path|: Path to file within .apk. e.g.: assets/foo.pak -// Output arguments: -// - |region|: size & offset (in bytes) within the .apk of the asset. -BASE_EXPORT int OpenApkAsset( - const std::string& file_path, - base::MemoryMappedFile::Region* region); - -// Registers an uncompressed asset from within the apk in the -// FileDescriptorStore. -// Returns: true in case of success, false otherwise. -BASE_EXPORT bool RegisterApkAssetWithFileDescriptorStore( - const std::string& key, - const base::FilePath& file_path); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_APK_ASSETS_H_ diff --git a/android/application_status_listener.cc b/android/application_status_listener.cc deleted file mode 100644 index c8c2cc68e..000000000 --- a/android/application_status_listener.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/application_status_listener.h" - -#include - -#include "base/lazy_instance.h" -#include "base/observer_list_threadsafe.h" -#include "jni/ApplicationStatus_jni.h" - -namespace base { -namespace android { - -namespace { - -struct LeakyLazyObserverListTraits : - base::internal::LeakyLazyInstanceTraits< - ObserverListThreadSafe > { - static ObserverListThreadSafe* - New(void* instance) { - ObserverListThreadSafe* ret = - base::internal::LeakyLazyInstanceTraits>::New(instance); - // Leaky. - ret->AddRef(); - return ret; - } -}; - -LazyInstance, - LeakyLazyObserverListTraits> g_observers = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -ApplicationStatusListener::ApplicationStatusListener( - const ApplicationStatusListener::ApplicationStateChangeCallback& callback) - : callback_(callback) { - DCHECK(!callback_.is_null()); - g_observers.Get().AddObserver(this); - - Java_ApplicationStatus_registerThreadSafeNativeApplicationStateListener( - AttachCurrentThread()); -} - -ApplicationStatusListener::~ApplicationStatusListener() { - g_observers.Get().RemoveObserver(this); -} - -void ApplicationStatusListener::Notify(ApplicationState state) { - callback_.Run(state); -} - -// static -void ApplicationStatusListener::NotifyApplicationStateChange( - ApplicationState state) { - g_observers.Get().Notify(FROM_HERE, &ApplicationStatusListener::Notify, - state); -} - -// static -ApplicationState ApplicationStatusListener::GetState() { - return static_cast( - Java_ApplicationStatus_getStateForApplication(AttachCurrentThread())); -} - -static void JNI_ApplicationStatus_OnApplicationStateChange( - JNIEnv* env, - const JavaParamRef& clazz, - jint new_state) { - ApplicationState application_state = static_cast(new_state); - ApplicationStatusListener::NotifyApplicationStateChange(application_state); -} - -} // namespace android -} // namespace base diff --git a/android/application_status_listener.h b/android/application_status_listener.h deleted file mode 100644 index fcc26a294..000000000 --- a/android/application_status_listener.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_ -#define BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_ - -#include - -#include "base/android/jni_android.h" -#include "base/base_export.h" -#include "base/macros.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. -// -// 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. -// -// A Java counterpart will be generated for this enum. -// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base -enum ApplicationState { - APPLICATION_STATE_UNKNOWN = 0, - APPLICATION_STATE_HAS_RUNNING_ACTIVITIES = 1, - APPLICATION_STATE_HAS_PAUSED_ACTIVITIES = 2, - APPLICATION_STATE_HAS_STOPPED_ACTIVITIES = 3, - APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES = 4 -}; - -// 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 ApplicationStateChangeCallback; - - explicit ApplicationStatusListener( - const ApplicationStateChangeCallback& callback); - ~ApplicationStatusListener(); - - // Internal use only: must be public to be called from JNI and unit tests. - static void NotifyApplicationStateChange(ApplicationState state); - - // Expose jni call for ApplicationStatus.getStateForApplication. - static ApplicationState GetState(); - - 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/android/application_status_listener_unittest.cc b/android/application_status_listener_unittest.cc deleted file mode 100644 index 803dedb12..000000000 --- a/android/application_status_listener_unittest.cc +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/application_status_listener.h" - -#include - -#include "base/bind.h" -#include "base/callback_forward.h" -#include "base/logging.h" -#include "base/message_loop/message_loop.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 android { - -namespace { - -using base::android::ScopedJavaLocalRef; - -// An invalid ApplicationState value. -const ApplicationState kInvalidApplicationState = - static_cast(100); - -// Used to generate a callback that stores the new state at a given location. -void StoreStateTo(ApplicationState* target, ApplicationState state) { - *target = state; -} - -void RunTasksUntilIdle() { - RunLoop run_loop; - run_loop.RunUntilIdle(); -} - -// Shared state for the multi-threaded test. -// This uses a thread to register for events and listen to them, while state -// changes are forced on the main thread. -class MultiThreadedTest { - public: - MultiThreadedTest() - : state_(kInvalidApplicationState), - event_(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED), - thread_("ApplicationStatusTest thread"), - main_() {} - - void Run() { - // Start the thread and tell it to register for events. - thread_.Start(); - thread_.task_runner()->PostTask( - FROM_HERE, base::Bind(&MultiThreadedTest::RegisterThreadForEvents, - base::Unretained(this))); - - // Wait for its completion. - event_.Wait(); - - // Change state, then wait for the thread to modify state. - ApplicationStatusListener::NotifyApplicationStateChange( - APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); - event_.Wait(); - EXPECT_EQ(APPLICATION_STATE_HAS_RUNNING_ACTIVITIES, state_); - - // Again - ApplicationStatusListener::NotifyApplicationStateChange( - APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES); - event_.Wait(); - EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, state_); - } - - private: - void ExpectOnThread() { - EXPECT_EQ(thread_.message_loop(), base::MessageLoop::current()); - } - - void RegisterThreadForEvents() { - ExpectOnThread(); - listener_.reset(new ApplicationStatusListener(base::Bind( - &MultiThreadedTest::StoreStateAndSignal, base::Unretained(this)))); - EXPECT_TRUE(listener_.get()); - event_.Signal(); - } - - void StoreStateAndSignal(ApplicationState state) { - ExpectOnThread(); - state_ = state; - event_.Signal(); - } - - ApplicationState state_; - base::WaitableEvent event_; - base::Thread thread_; - base::MessageLoop main_; - std::unique_ptr listener_; -}; - -} // namespace - -TEST(ApplicationStatusListenerTest, SingleThread) { - MessageLoop message_loop; - - ApplicationState result = kInvalidApplicationState; - - // Create a new listener that stores the new state into |result| on every - // state change. - ApplicationStatusListener listener( - base::Bind(&StoreStateTo, base::Unretained(&result))); - - EXPECT_EQ(kInvalidApplicationState, result); - - ApplicationStatusListener::NotifyApplicationStateChange( - APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); - RunTasksUntilIdle(); - EXPECT_EQ(APPLICATION_STATE_HAS_RUNNING_ACTIVITIES, result); - - ApplicationStatusListener::NotifyApplicationStateChange( - APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES); - RunTasksUntilIdle(); - EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, result); -} - -TEST(ApplicationStatusListenerTest, TwoThreads) { - MultiThreadedTest test; - test.Run(); -} - -} // namespace android -} // namespace base diff --git a/android/base_jni_onload.cc b/android/base_jni_onload.cc deleted file mode 100644 index 170dd8402..000000000 --- a/android/base_jni_onload.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/base_jni_onload.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_utils.h" -#include "base/android/library_loader/library_loader_hooks.h" -#include "base/bind.h" - -namespace base { -namespace android { - -bool OnJNIOnLoadInit() { - InitAtExitManager(); - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::InitReplacementClassLoader(env, - base::android::GetClassLoader(env)); - return true; -} - -} // namespace android -} // namespace base diff --git a/android/base_jni_onload.h b/android/base_jni_onload.h deleted file mode 100644 index 5d7cc0ad2..000000000 --- a/android/base_jni_onload.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_BASE_JNI_ONLOAD_H_ -#define BASE_ANDROID_BASE_JNI_ONLOAD_H_ - -#include -#include - -#include "base/base_export.h" -#include "base/callback.h" - -namespace base { -namespace android { - -// Returns whether initialization succeeded. -BASE_EXPORT bool OnJNIOnLoadInit(); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_BASE_JNI_ONLOAD_H_ diff --git a/android/build_info.cc b/android/build_info.cc deleted file mode 100644 index bebf90160..000000000 --- a/android/build_info.cc +++ /dev/null @@ -1,87 +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/android/build_info.h" - -#include - -#include "base/android/jni_android.h" -#include "base/android/jni_array.h" -#include "base/android/scoped_java_ref.h" -#include "base/logging.h" -#include "base/memory/singleton.h" -#include "base/strings/string_number_conversions.h" -#include "jni/BuildInfo_jni.h" - -namespace base { -namespace android { - -namespace { - -// We are leaking these strings. -const char* StrDupParam(const std::vector& params, int index) { - return strdup(params[index].c_str()); -} - -int GetIntParam(const std::vector& params, int index) { - int ret = 0; - bool success = StringToInt(params[index], &ret); - DCHECK(success); - return ret; -} - -} // namespace - -struct BuildInfoSingletonTraits { - static BuildInfo* New() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef params_objs = Java_BuildInfo_getAll(env); - std::vector params; - AppendJavaStringArrayToStringVector(env, params_objs.obj(), ¶ms); - return new BuildInfo(params); - } - - static void Delete(BuildInfo* x) { - // We're leaking this type, see kRegisterAtExit. - NOTREACHED(); - } - - static const bool kRegisterAtExit = false; -#if DCHECK_IS_ON() - static const bool kAllowedToAccessOnNonjoinableThread = true; -#endif -}; - -BuildInfo::BuildInfo(const std::vector& params) - : brand_(StrDupParam(params, 0)), - device_(StrDupParam(params, 1)), - android_build_id_(StrDupParam(params, 2)), - manufacturer_(StrDupParam(params, 3)), - model_(StrDupParam(params, 4)), - sdk_int_(GetIntParam(params, 5)), - build_type_(StrDupParam(params, 6)), - board_(StrDupParam(params, 7)), - host_package_name_(StrDupParam(params, 8)), - host_version_code_(StrDupParam(params, 9)), - host_package_label_(StrDupParam(params, 10)), - package_name_(StrDupParam(params, 11)), - package_version_code_(StrDupParam(params, 12)), - package_version_name_(StrDupParam(params, 13)), - android_build_fp_(StrDupParam(params, 14)), - gms_version_code_(StrDupParam(params, 15)), - installer_package_name_(StrDupParam(params, 16)), - abi_name_(StrDupParam(params, 17)), - firebase_app_id_(StrDupParam(params, 18)), - custom_themes_(StrDupParam(params, 19)), - resources_version_(StrDupParam(params, 20)), - extracted_file_suffix_(params[21]), - is_at_least_p_(GetIntParam(params, 22)) {} - -// static -BuildInfo* BuildInfo::GetInstance() { - return Singleton::get(); -} - -} // namespace android -} // namespace base diff --git a/android/build_info.h b/android/build_info.h deleted file mode 100644 index bfe4db249..000000000 --- a/android/build_info.h +++ /dev/null @@ -1,165 +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_ANDROID_BUILD_INFO_H_ -#define BASE_ANDROID_BUILD_INFO_H_ - -#include - -#include -#include - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/memory/singleton.h" - -namespace base { -namespace android { - -// This enumeration maps to the values returned by BuildInfo::sdk_int(), -// indicating the Android release associated with a given SDK version. -enum SdkVersion { - SDK_VERSION_JELLY_BEAN = 16, - SDK_VERSION_JELLY_BEAN_MR1 = 17, - SDK_VERSION_JELLY_BEAN_MR2 = 18, - SDK_VERSION_KITKAT = 19, - SDK_VERSION_KITKAT_WEAR = 20, - SDK_VERSION_LOLLIPOP = 21, - SDK_VERSION_LOLLIPOP_MR1 = 22, - SDK_VERSION_MARSHMALLOW = 23, - SDK_VERSION_NOUGAT = 24, - SDK_VERSION_NOUGAT_MR1 = 25, - SDK_VERSION_OREO = 26, -}; - -// BuildInfo is a singleton class that stores android build and device -// information. It will be called from Android specific code and gets used -// primarily in crash reporting. -class BASE_EXPORT BuildInfo { - public: - - ~BuildInfo() {} - - // Static factory method for getting the singleton BuildInfo instance. - // Note that ownership is not conferred on the caller and the BuildInfo in - // question isn't actually freed until shutdown. This is ok because there - // should only be one instance of BuildInfo ever created. - static BuildInfo* GetInstance(); - - // Const char* is used instead of std::strings because these values must be - // available even if the process is in a crash state. Sadly - // std::string.c_str() doesn't guarantee that memory won't be allocated when - // it is called. - const char* device() const { - return device_; - } - - const char* manufacturer() const { - return manufacturer_; - } - - const char* model() const { - return model_; - } - - const char* brand() const { - return brand_; - } - - const char* android_build_id() const { - return android_build_id_; - } - - const char* android_build_fp() const { - return android_build_fp_; - } - - const char* gms_version_code() const { - return gms_version_code_; - } - - const char* host_package_name() const { return host_package_name_; } - - const char* host_version_code() const { return host_version_code_; } - - const char* host_package_label() const { return host_package_label_; } - - const char* package_version_code() const { - return package_version_code_; - } - - const char* package_version_name() const { - return package_version_name_; - } - - const char* package_name() const { - return package_name_; - } - - // Will be empty string if no app id is assigned. - const char* firebase_app_id() const { return firebase_app_id_; } - - const char* custom_themes() const { return custom_themes_; } - - const char* resources_version() const { return resources_version_; } - - const char* build_type() const { - return build_type_; - } - - const char* board() const { return board_; } - - const char* installer_package_name() const { return installer_package_name_; } - - const char* abi_name() const { return abi_name_; } - - std::string extracted_file_suffix() const { return extracted_file_suffix_; } - - int sdk_int() const { - return sdk_int_; - } - - bool is_at_least_p() const { return is_at_least_p_; } - - private: - friend struct BuildInfoSingletonTraits; - - explicit BuildInfo(const std::vector& params); - - // Const char* is used instead of std::strings because these values must be - // available even if the process is in a crash state. Sadly - // std::string.c_str() doesn't guarantee that memory won't be allocated when - // it is called. - const char* const brand_; - const char* const device_; - const char* const android_build_id_; - const char* const manufacturer_; - const char* const model_; - const int sdk_int_; - const char* const build_type_; - const char* const board_; - const char* const host_package_name_; - const char* const host_version_code_; - const char* const host_package_label_; - const char* const package_name_; - const char* const package_version_code_; - const char* const package_version_name_; - const char* const android_build_fp_; - const char* const gms_version_code_; - const char* const installer_package_name_; - const char* const abi_name_; - const char* const firebase_app_id_; - const char* const custom_themes_; - const char* const resources_version_; - // Not needed by breakpad. - const std::string extracted_file_suffix_; - const int is_at_least_p_; - - DISALLOW_COPY_AND_ASSIGN(BuildInfo); -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_BUILD_INFO_H_ diff --git a/android/callback_android.cc b/android/callback_android.cc deleted file mode 100644 index 714366415..000000000 --- a/android/callback_android.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2016 The Chromium 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/callback_android.h" - -#include "base/android/jni_array.h" -#include "base/android/jni_string.h" -#include "base/android/scoped_java_ref.h" -#include "jni/Callback_jni.h" - -namespace base { -namespace android { - -void RunObjectCallbackAndroid(const JavaRef& callback, - const JavaRef& arg) { - Java_Helper_onObjectResultFromNative(AttachCurrentThread(), callback, arg); -} - -void RunBooleanCallbackAndroid(const JavaRef& callback, bool arg) { - Java_Helper_onBooleanResultFromNative(AttachCurrentThread(), callback, - static_cast(arg)); -} - -void RunIntCallbackAndroid(const JavaRef& callback, int arg) { - Java_Helper_onIntResultFromNative(AttachCurrentThread(), callback, arg); -} - -void RunStringCallbackAndroid(const JavaRef& callback, - const std::string& arg) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef java_string = ConvertUTF8ToJavaString(env, arg); - Java_Helper_onObjectResultFromNative(env, callback, java_string); -} - -void RunByteArrayCallbackAndroid(const JavaRef& callback, - const std::vector& arg) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef j_bytes = ToJavaByteArray(env, arg); - Java_Helper_onObjectResultFromNative(env, callback, j_bytes); -} - -} // namespace android -} // namespace base diff --git a/android/callback_android.h b/android/callback_android.h deleted file mode 100644 index 8a14c1fa3..000000000 --- a/android/callback_android.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 The Chromium 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_CALLBACK_ANDROID_H_ -#define BASE_ANDROID_CALLBACK_ANDROID_H_ - -#include -#include -#include - -#include "base/android/scoped_java_ref.h" -#include "base/base_export.h" - -// Provides helper utility methods that run the given callback with the -// specified argument. -namespace base { -namespace android { - -void BASE_EXPORT RunObjectCallbackAndroid(const JavaRef& callback, - const JavaRef& arg); - -void BASE_EXPORT RunBooleanCallbackAndroid(const JavaRef& callback, - bool arg); - -void BASE_EXPORT RunIntCallbackAndroid(const JavaRef& callback, - int arg); - -void BASE_EXPORT RunStringCallbackAndroid(const JavaRef& callback, - const std::string& arg); - -void BASE_EXPORT RunByteArrayCallbackAndroid(const JavaRef& callback, - const std::vector& arg); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_CALLBACK_ANDROID_H_ diff --git a/android/child_process_binding_types.h b/android/child_process_binding_types.h deleted file mode 100644 index a3900d5e8..000000000 --- a/android/child_process_binding_types.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018 The Chromium 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_CHILD_PROCESS_BINDING_TYPES_H_ -#define BASE_ANDROID_CHILD_PROCESS_BINDING_TYPES_H_ - -namespace base { -namespace android { - -// Defines the state of bindgings with child process. See ChildProcessConnection -// to see what the bindings are. Note these values are used as array indices. -// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base -enum class ChildBindingState { - UNBOUND, - WAIVED, - MODERATE, - STRONG, - MAX_VALUE = STRONG -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_CHILD_PROCESS_BINDING_TYPES_H_ diff --git a/android/child_process_service.cc b/android/child_process_service.cc deleted file mode 100644 index 013a70b56..000000000 --- a/android/child_process_service.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2017 The Chromium 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/jni_array.h" -#include "base/android/jni_string.h" -#include "base/android/library_loader/library_loader_hooks.h" -#include "base/file_descriptor_store.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/optional.h" -#include "base/posix/global_descriptors.h" -#include "jni/ChildProcessService_jni.h" - -using base::android::JavaIntArrayToIntVector; -using base::android::JavaParamRef; - -namespace base { -namespace android { - -void JNI_ChildProcessService_RegisterFileDescriptors( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& j_keys, - const JavaParamRef& j_ids, - const JavaParamRef& j_fds, - const JavaParamRef& j_offsets, - const JavaParamRef& j_sizes) { - std::vector> keys; - jsize keys_size = env->GetArrayLength(j_keys); - keys.reserve(keys_size); - for (jsize i = 0; i < keys_size; i++) { - base::android::ScopedJavaLocalRef str( - env, static_cast(env->GetObjectArrayElement(j_keys, i))); - base::Optional key; - if (!str.is_null()) { - key = base::android::ConvertJavaStringToUTF8(env, str); - } - keys.push_back(std::move(key)); - } - - std::vector ids; - base::android::JavaIntArrayToIntVector(env, j_ids, &ids); - std::vector fds; - base::android::JavaIntArrayToIntVector(env, j_fds, &fds); - std::vector offsets; - base::android::JavaLongArrayToInt64Vector(env, j_offsets, &offsets); - std::vector sizes; - base::android::JavaLongArrayToInt64Vector(env, j_sizes, &sizes); - - DCHECK_EQ(keys.size(), ids.size()); - DCHECK_EQ(ids.size(), fds.size()); - DCHECK_EQ(fds.size(), offsets.size()); - DCHECK_EQ(offsets.size(), sizes.size()); - - for (size_t i = 0; i < ids.size(); i++) { - base::MemoryMappedFile::Region region = {offsets.at(i), sizes.at(i)}; - const base::Optional& key = keys.at(i); - int id = ids.at(i); - int fd = fds.at(i); - if (key) { - base::FileDescriptorStore::GetInstance().Set(*key, base::ScopedFD(fd), - region); - } else { - base::GlobalDescriptors::GetInstance()->Set(id, fd, region); - } - } -} - -void JNI_ChildProcessService_ExitChildProcess( - JNIEnv* env, - const JavaParamRef& clazz) { - VLOG(0) << "ChildProcessService: Exiting child process."; - base::android::LibraryLoaderExitHook(); - _exit(0); -} - -} // namespace android -} // namespace base diff --git a/android/command_line_android.cc b/android/command_line_android.cc deleted file mode 100644 index c9b545f52..000000000 --- a/android/command_line_android.cc +++ /dev/null @@ -1,89 +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/jni_array.h" -#include "base/android/jni_string.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "jni/CommandLine_jni.h" - -using base::android::ConvertUTF8ToJavaString; -using base::android::ConvertJavaStringToUTF8; -using base::android::JavaParamRef; -using base::android::ScopedJavaLocalRef; -using base::CommandLine; - -namespace { - -void JNI_CommandLine_AppendJavaStringArrayToCommandLine( - JNIEnv* env, - const JavaParamRef& array, - bool includes_program) { - std::vector vec; - if (array) - base::android::AppendJavaStringArrayToStringVector(env, array, &vec); - if (!includes_program) - vec.insert(vec.begin(), std::string()); - CommandLine extra_command_line(vec); - CommandLine::ForCurrentProcess()->AppendArguments(extra_command_line, - includes_program); -} - -} // namespace - -static jboolean JNI_CommandLine_HasSwitch( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jswitch) { - std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); - return CommandLine::ForCurrentProcess()->HasSwitch(switch_string); -} - -static ScopedJavaLocalRef JNI_CommandLine_GetSwitchValue( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jswitch) { - std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); - std::string value(CommandLine::ForCurrentProcess()->GetSwitchValueNative( - switch_string)); - if (value.empty()) - return ScopedJavaLocalRef(); - return ConvertUTF8ToJavaString(env, value); -} - -static void JNI_CommandLine_AppendSwitch(JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jswitch) { - std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); - CommandLine::ForCurrentProcess()->AppendSwitch(switch_string); -} - -static void JNI_CommandLine_AppendSwitchWithValue( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jswitch, - const JavaParamRef& jvalue) { - std::string switch_string(ConvertJavaStringToUTF8(env, jswitch)); - std::string value_string (ConvertJavaStringToUTF8(env, jvalue)); - CommandLine::ForCurrentProcess()->AppendSwitchASCII(switch_string, - value_string); -} - -static void JNI_CommandLine_AppendSwitchesAndArguments( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& array) { - JNI_CommandLine_AppendJavaStringArrayToCommandLine(env, array, false); -} - -static void JNI_CommandLine_Init( - JNIEnv* env, - const JavaParamRef& jclazz, - const JavaParamRef& init_command_line) { - // TODO(port): Make an overload of Init() that takes StringVector rather than - // have to round-trip via AppendArguments. - CommandLine::Init(0, nullptr); - JNI_CommandLine_AppendJavaStringArrayToCommandLine(env, init_command_line, - true); -} diff --git a/android/content_uri_utils.cc b/android/content_uri_utils.cc deleted file mode 100644 index a7955dca6..000000000 --- a/android/content_uri_utils.cc +++ /dev/null @@ -1,45 +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/content_uri_utils.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "jni/ContentUriUtils_jni.h" - -using base::android::ConvertUTF8ToJavaString; -using base::android::ScopedJavaLocalRef; - -namespace base { - -bool ContentUriExists(const FilePath& content_uri) { - JNIEnv* env = base::android::AttachCurrentThread(); - ScopedJavaLocalRef j_uri = - ConvertUTF8ToJavaString(env, content_uri.value()); - return Java_ContentUriUtils_contentUriExists(env, j_uri); -} - -File OpenContentUriForRead(const FilePath& content_uri) { - JNIEnv* env = base::android::AttachCurrentThread(); - ScopedJavaLocalRef j_uri = - ConvertUTF8ToJavaString(env, content_uri.value()); - jint fd = Java_ContentUriUtils_openContentUriForRead(env, j_uri); - if (fd < 0) - return File(); - return File(fd); -} - -std::string GetContentUriMimeType(const FilePath& content_uri) { - JNIEnv* env = base::android::AttachCurrentThread(); - ScopedJavaLocalRef j_uri = - ConvertUTF8ToJavaString(env, content_uri.value()); - ScopedJavaLocalRef j_mime = - Java_ContentUriUtils_getMimeType(env, j_uri); - if (j_mime.is_null()) - return std::string(); - - return base::android::ConvertJavaStringToUTF8(env, j_mime.obj()); -} - -} // namespace base diff --git a/android/content_uri_utils.h b/android/content_uri_utils.h deleted file mode 100644 index 6d817c051..000000000 --- a/android/content_uri_utils.h +++ /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. - -#ifndef BASE_ANDROID_CONTENT_URI_UTILS_H_ -#define BASE_ANDROID_CONTENT_URI_UTILS_H_ - -#include - -#include "base/base_export.h" -#include "base/files/file.h" -#include "base/files/file_path.h" - -namespace base { - -// Opens a content URI for read and returns the file descriptor to the caller. -// Returns -1 if the URI is invalid. -BASE_EXPORT File OpenContentUriForRead(const FilePath& content_uri); - -// Check whether a content URI exists. -BASE_EXPORT bool ContentUriExists(const FilePath& content_uri); - -// Gets MIME type from a content URI. Returns an empty string if the URI is -// invalid. -BASE_EXPORT std::string GetContentUriMimeType(const FilePath& content_uri); - -} // namespace base - -#endif // BASE_ANDROID_CONTENT_URI_UTILS_H_ diff --git a/android/content_uri_utils_unittest.cc b/android/content_uri_utils_unittest.cc deleted file mode 100644 index 0e5cf0cbb..000000000 --- a/android/content_uri_utils_unittest.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/content_uri_utils.h" -#include "base/files/file_util.h" -#include "base/path_service.h" -#include "base/test/test_file_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace android { - -// Disable on Android ASAN bot due to consistent failures: crbug.com/807080. -#if !defined(ADDRESS_SANITIZER) -TEST(ContentUriUtilsTest, ContentUriMimeTest) { - // Get the test image path. - FilePath data_dir; - ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir)); - data_dir = data_dir.AppendASCII("file_util"); - ASSERT_TRUE(PathExists(data_dir)); - FilePath image_file = data_dir.Append(FILE_PATH_LITERAL("red.png")); - - // Insert the image into MediaStore. MediaStore will do some conversions, and - // return the content URI. - FilePath path = base::InsertImageIntoMediaStore(image_file); - EXPECT_TRUE(path.IsContentUri()); - EXPECT_TRUE(PathExists(path)); - - std::string mime = GetContentUriMimeType(path); - EXPECT_EQ(mime, std::string("image/png")); - - FilePath invalid_path("content://foo.bar"); - mime = GetContentUriMimeType(invalid_path); - EXPECT_TRUE(mime.empty()); -} -#endif - -} // namespace android -} // namespace base diff --git a/android/cpu_features.cc b/android/cpu_features.cc deleted file mode 100644 index 7c1dbe314..000000000 --- a/android/cpu_features.cc +++ /dev/null @@ -1,22 +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 - -#include "base/android/jni_android.h" -#include "jni/CpuFeatures_jni.h" - -namespace base { -namespace android { - -jint JNI_CpuFeatures_GetCoreCount(JNIEnv*, const JavaParamRef&) { - return android_getCpuCount(); -} - -jlong JNI_CpuFeatures_GetCpuFeatures(JNIEnv*, const JavaParamRef&) { - return android_getCpuFeatures(); -} - -} // namespace android -} // namespace base diff --git a/android/early_trace_event_binding.cc b/android/early_trace_event_binding.cc deleted file mode 100644 index bf6b91067..000000000 --- a/android/early_trace_event_binding.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 The Chromium 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 - -#include "base/android/jni_string.h" -#include "base/time/time.h" -#include "base/trace_event/trace_event.h" -#include "jni/EarlyTraceEvent_jni.h" - -namespace base { -namespace android { - -const char kEarlyJavaCategory[] = "EarlyJava"; - -static void JNI_EarlyTraceEvent_RecordEarlyEvent( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jname, - jlong begin_time_ns, - jlong end_time_ns, - jint thread_id, - jlong thread_duration_ms) { - std::string name = ConvertJavaStringToUTF8(env, jname); - int64_t begin_us = begin_time_ns / 1000; - int64_t end_us = end_time_ns / 1000; - int64_t thread_duration_us = thread_duration_ms * 1000; - - INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMPS( - kEarlyJavaCategory, name.c_str(), trace_event_internal::kNoId, thread_id, - TimeTicks::FromInternalValue(begin_us), - TimeTicks::FromInternalValue(end_us), - ThreadTicks::Now() + TimeDelta::FromMicroseconds(thread_duration_us), - TRACE_EVENT_FLAG_COPY); -} - -static void JNI_EarlyTraceEvent_RecordEarlyStartAsyncEvent( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jname, - jlong id, - jlong timestamp_ns) { - std::string name = ConvertJavaStringToUTF8(env, jname); - int64_t timestamp_us = timestamp_ns / 1000; - - TRACE_EVENT_COPY_ASYNC_BEGIN_WITH_TIMESTAMP0( - kEarlyJavaCategory, name.c_str(), id, - base::TimeTicks() + base::TimeDelta::FromMicroseconds(timestamp_us)); -} - -static void JNI_EarlyTraceEvent_RecordEarlyFinishAsyncEvent( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jname, - jlong id, - jlong timestamp_ns) { - std::string name = ConvertJavaStringToUTF8(env, jname); - int64_t timestamp_us = timestamp_ns / 1000; - - TRACE_EVENT_COPY_ASYNC_END_WITH_TIMESTAMP0( - kEarlyJavaCategory, name.c_str(), id, - base::TimeTicks() + base::TimeDelta::FromMicroseconds(timestamp_us)); -} - -} // namespace android -} // namespace base diff --git a/android/event_log.cc b/android/event_log.cc deleted file mode 100644 index 3eb5926b1..000000000 --- a/android/event_log.cc +++ /dev/null @@ -1,16 +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/event_log.h" -#include "jni/EventLog_jni.h" - -namespace base { -namespace android { - -void EventLogWriteInt(int tag, int value) { - Java_EventLog_writeEvent(AttachCurrentThread(), tag, value); -} - -} // namespace android -} // namespace base diff --git a/android/event_log.h b/android/event_log.h deleted file mode 100644 index ebd5919a5..000000000 --- a/android/event_log.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_EVENT_LOG_H_ -#define BASE_ANDROID_EVENT_LOG_H_ - -#include - -#include "base/base_export.h" - -namespace base { -namespace android { - -void BASE_EXPORT EventLogWriteInt(int tag, int value); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_EVENT_LOG_H_ diff --git a/android/field_trial_list.cc b/android/field_trial_list.cc deleted file mode 100644 index 1dec5b537..000000000 --- a/android/field_trial_list.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include -#include - -#include "base/android/jni_string.h" -#include "base/metrics/field_trial.h" -#include "base/metrics/field_trial_params.h" -#include "jni/FieldTrialList_jni.h" - -using base::android::ConvertJavaStringToUTF8; -using base::android::ConvertUTF8ToJavaString; -using base::android::JavaParamRef; -using base::android::ScopedJavaLocalRef; - -static ScopedJavaLocalRef JNI_FieldTrialList_FindFullName( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jtrial_name) { - std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name)); - return ConvertUTF8ToJavaString( - env, base::FieldTrialList::FindFullName(trial_name)); -} - -static jboolean JNI_FieldTrialList_TrialExists( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jtrial_name) { - std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name)); - return base::FieldTrialList::TrialExists(trial_name); -} - -static ScopedJavaLocalRef JNI_FieldTrialList_GetVariationParameter( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jtrial_name, - const JavaParamRef& jparameter_key) { - std::map parameters; - base::GetFieldTrialParams(ConvertJavaStringToUTF8(env, jtrial_name), - ¶meters); - return ConvertUTF8ToJavaString( - env, parameters[ConvertJavaStringToUTF8(env, jparameter_key)]); -} diff --git a/android/important_file_writer_android.cc b/android/important_file_writer_android.cc deleted file mode 100644 index fcaa3b1ea..000000000 --- a/android/important_file_writer_android.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 - -#include "base/android/jni_string.h" -#include "base/files/important_file_writer.h" -#include "base/threading/thread_restrictions.h" -#include "jni/ImportantFileWriterAndroid_jni.h" - -namespace base { -namespace android { - -static jboolean JNI_ImportantFileWriterAndroid_WriteFileAtomically( - JNIEnv* env, - const JavaParamRef& /* clazz */, - const JavaParamRef& file_name, - const JavaParamRef& data) { - // This is called on the UI thread during shutdown to save tab data, so - // needs to enable IO. - base::ThreadRestrictions::ScopedAllowIO allow_io; - std::string native_file_name; - base::android::ConvertJavaStringToUTF8(env, file_name, &native_file_name); - base::FilePath path(native_file_name); - int data_length = env->GetArrayLength(data); - jbyte* native_data = env->GetByteArrayElements(data, NULL); - std::string native_data_string(reinterpret_cast(native_data), - data_length); - bool result = base::ImportantFileWriter::WriteFileAtomically( - path, native_data_string); - env->ReleaseByteArrayElements(data, native_data, JNI_ABORT); - return result; -} - -} // namespace android -} // namespace base diff --git a/android/java/src/org/chromium/base/ActivityState.java b/android/java/src/org/chromium/base/ActivityState.java deleted file mode 100644 index b14814c1c..000000000 --- a/android/java/src/org/chromium/base/ActivityState.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.support.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * A set of states that represent the last state change of an Activity. - */ -@Retention(RetentionPolicy.SOURCE) -@IntDef({ActivityState.CREATED, ActivityState.STARTED, ActivityState.RESUMED, ActivityState.PAUSED, - ActivityState.STOPPED, ActivityState.DESTROYED}) -public @interface ActivityState { - /** - * Represents Activity#onCreate(). - */ - int CREATED = 1; - - /** - * Represents Activity#onStart(). - */ - int STARTED = 2; - - /** - * Represents Activity#onResume(). - */ - int RESUMED = 3; - - /** - * Represents Activity#onPause(). - */ - int PAUSED = 4; - - /** - * Represents Activity#onStop(). - */ - int STOPPED = 5; - - /** - * Represents Activity#onDestroy(). This is also used when the state of an Activity is unknown. - */ - int DESTROYED = 6; -} diff --git a/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java b/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java deleted file mode 100644 index ad5cdd815..000000000 --- a/android/java/src/org/chromium/base/AnimationFrameTimeHistogram.java +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.animation.AnimatorListenerAdapter; -import android.animation.TimeAnimator; -import android.animation.TimeAnimator.TimeListener; -import android.util.Log; - -/** - * Record Android animation frame rate and save it to UMA histogram. This is mainly for monitoring - * any jankiness of short Chrome Android animations. It is limited to few seconds of recording. - */ -public class AnimationFrameTimeHistogram { - private static final String TAG = "AnimationFrameTimeHistogram"; - private static final int MAX_FRAME_TIME_NUM = 600; // 10 sec on 60 fps. - - private final Recorder mRecorder = new Recorder(); - private final String mHistogramName; - - /** - * @param histogramName The histogram name that the recorded frame times will be saved. - * This must be also defined in histograms.xml - * @return An AnimatorListener instance that records frame time histogram on start and end - * automatically. - */ - public static AnimatorListener getAnimatorRecorder(final String histogramName) { - return new AnimatorListenerAdapter() { - private final AnimationFrameTimeHistogram mAnimationFrameTimeHistogram = - new AnimationFrameTimeHistogram(histogramName); - - @Override - public void onAnimationStart(Animator animation) { - mAnimationFrameTimeHistogram.startRecording(); - } - - @Override - public void onAnimationEnd(Animator animation) { - mAnimationFrameTimeHistogram.endRecording(); - } - - @Override - public void onAnimationCancel(Animator animation) { - mAnimationFrameTimeHistogram.endRecording(); - } - }; - } - - /** - * @param histogramName The histogram name that the recorded frame times will be saved. - * This must be also defined in histograms.xml - */ - public AnimationFrameTimeHistogram(String histogramName) { - mHistogramName = histogramName; - } - - /** - * Start recording frame times. The recording can fail if it exceeds a few seconds. - */ - public void startRecording() { - mRecorder.startRecording(); - } - - /** - * End recording and save it to histogram. It won't save histogram if the recording wasn't - * successful. - */ - public void endRecording() { - if (mRecorder.endRecording()) { - nativeSaveHistogram(mHistogramName, - mRecorder.getFrameTimesMs(), mRecorder.getFrameTimesCount()); - } - mRecorder.cleanUp(); - } - - /** - * Record Android animation frame rate and return the result. - */ - private static class Recorder implements TimeListener { - // TODO(kkimlabs): If we can use in the future, migrate to Choreographer for minimal - // workload. - private final TimeAnimator mAnimator = new TimeAnimator(); - private long[] mFrameTimesMs; - private int mFrameTimesCount; - - private Recorder() { - mAnimator.setTimeListener(this); - } - - private void startRecording() { - assert !mAnimator.isRunning(); - mFrameTimesCount = 0; - mFrameTimesMs = new long[MAX_FRAME_TIME_NUM]; - mAnimator.start(); - } - - /** - * @return Whether the recording was successful. If successful, the result is available via - * getFrameTimesNs and getFrameTimesCount. - */ - private boolean endRecording() { - boolean succeeded = mAnimator.isStarted(); - mAnimator.end(); - return succeeded; - } - - private long[] getFrameTimesMs() { - return mFrameTimesMs; - } - - private int getFrameTimesCount() { - return mFrameTimesCount; - } - - /** - * Deallocates the temporary buffer to record frame times. Must be called after ending - * the recording and getting the result. - */ - private void cleanUp() { - mFrameTimesMs = null; - } - - @Override - public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { - if (mFrameTimesCount == mFrameTimesMs.length) { - mAnimator.end(); - cleanUp(); - Log.w(TAG, "Animation frame time recording reached the maximum number. It's either" - + "the animation took too long or recording end is not called."); - return; - } - - // deltaTime is 0 for the first frame. - if (deltaTime > 0) { - mFrameTimesMs[mFrameTimesCount++] = deltaTime; - } - } - } - - private native void nativeSaveHistogram(String histogramName, long[] frameTimesMs, int count); -} diff --git a/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/android/java/src/org/chromium/base/ApiCompatibilityUtils.java deleted file mode 100644 index b3e880124..000000000 --- a/android/java/src/org/chromium/base/ApiCompatibilityUtils.java +++ /dev/null @@ -1,687 +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. - -package org.chromium.base; - -import android.annotation.TargetApi; -import android.app.Activity; -import android.app.ActivityManager; -import android.app.ActivityOptions; -import android.app.PendingIntent; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.content.res.Resources.NotFoundException; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.VectorDrawable; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.PowerManager; -import android.os.Process; -import android.os.StatFs; -import android.os.StrictMode; -import android.os.UserManager; -import android.provider.Settings; -import android.support.annotation.NonNull; -import android.text.Html; -import android.text.Spanned; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.view.inputmethod.InputMethodSubtype; -import android.view.textclassifier.TextClassifier; -import android.widget.TextView; - -import java.io.File; -import java.io.UnsupportedEncodingException; - -/** - * Utility class to use new APIs that were added after ICS (API level 14). - */ -@TargetApi(Build.VERSION_CODES.LOLLIPOP) -public class ApiCompatibilityUtils { - private ApiCompatibilityUtils() { - } - - /** - * Compares two long values numerically. The value returned is identical to what would be - * returned by {@link Long#compare(long, long)} which is available since API level 19. - */ - public static int compareLong(long lhs, long rhs) { - return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1); - } - - /** - * Compares two boolean values. The value returned is identical to what would be returned by - * {@link Boolean#compare(boolean, boolean)} which is available since API level 19. - */ - public static int compareBoolean(boolean lhs, boolean rhs) { - return lhs == rhs ? 0 : lhs ? 1 : -1; - } - - /** - * Checks that the object reference is not null and throws NullPointerException if it is. - * See {@link Objects#requireNonNull} which is available since API level 19. - * @param obj The object to check - */ - @NonNull - public static T requireNonNull(T obj) { - if (obj == null) throw new NullPointerException(); - return obj; - } - - /** - * Checks that the object reference is not null and throws NullPointerException if it is. - * See {@link Objects#requireNonNull} which is available since API level 19. - * @param obj The object to check - * @param message The message to put into NullPointerException - */ - @NonNull - public static T requireNonNull(T obj, String message) { - if (obj == null) throw new NullPointerException(message); - return obj; - } - - /** - * {@link String#getBytes()} but specifying UTF-8 as the encoding and capturing the resulting - * UnsupportedEncodingException. - */ - public static byte[] getBytesUtf8(String str) { - try { - return str.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("UTF-8 encoding not available.", e); - } - } - - /** - * Returns true if view's layout direction is right-to-left. - * - * @param view the View whose layout is being considered - */ - public static boolean isLayoutRtl(View view) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - return view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; - } else { - // All layouts are LTR before JB MR1. - return false; - } - } - - /** - * @see Configuration#getLayoutDirection() - */ - public static int getLayoutDirection(Configuration configuration) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - return configuration.getLayoutDirection(); - } else { - // All layouts are LTR before JB MR1. - return View.LAYOUT_DIRECTION_LTR; - } - } - - /** - * @return True if the running version of the Android supports printing. - */ - public static boolean isPrintingSupported() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - } - - /** - * @return True if the running version of the Android supports elevation. Elevation of a view - * determines the visual appearance of its shadow. - */ - public static boolean isElevationSupported() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; - } - - /** - * @see android.view.View#setLayoutDirection(int) - */ - public static void setLayoutDirection(View view, int layoutDirection) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - view.setLayoutDirection(layoutDirection); - } else { - // Do nothing. RTL layouts aren't supported before JB MR1. - } - } - - /** - * @see android.view.View#setTextAlignment(int) - */ - public static void setTextAlignment(View view, int textAlignment) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - view.setTextAlignment(textAlignment); - } else { - // Do nothing. RTL text isn't supported before JB MR1. - } - } - - /** - * @see android.view.View#setTextDirection(int) - */ - public static void setTextDirection(View view, int textDirection) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - view.setTextDirection(textDirection); - } else { - // Do nothing. RTL text isn't supported before JB MR1. - } - } - - /** - * See {@link android.view.View#setLabelFor(int)}. - */ - public static void setLabelFor(View labelView, int id) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - labelView.setLabelFor(id); - } else { - // Do nothing. #setLabelFor() isn't supported before JB MR1. - } - } - - /** - * @see android.widget.TextView#getCompoundDrawablesRelative() - */ - public static Drawable[] getCompoundDrawablesRelative(TextView textView) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - return textView.getCompoundDrawablesRelative(); - } else { - return textView.getCompoundDrawables(); - } - } - - /** - * @see android.widget.TextView#setCompoundDrawablesRelative(Drawable, Drawable, Drawable, - * Drawable) - */ - public static void setCompoundDrawablesRelative(TextView textView, Drawable start, Drawable top, - Drawable end, Drawable bottom) { - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) { - // On JB MR1, due to a platform bug, setCompoundDrawablesRelative() is a no-op if the - // view has ever been measured. As a workaround, use setCompoundDrawables() directly. - // See: http://crbug.com/368196 and http://crbug.com/361709 - boolean isRtl = isLayoutRtl(textView); - textView.setCompoundDrawables(isRtl ? end : start, top, isRtl ? start : end, bottom); - } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) { - textView.setCompoundDrawablesRelative(start, top, end, bottom); - } else { - textView.setCompoundDrawables(start, top, end, bottom); - } - } - - /** - * @see android.widget.TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable, - * Drawable, Drawable, Drawable) - */ - public static void setCompoundDrawablesRelativeWithIntrinsicBounds(TextView textView, - Drawable start, Drawable top, Drawable end, Drawable bottom) { - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) { - // Work around the platform bug described in setCompoundDrawablesRelative() above. - boolean isRtl = isLayoutRtl(textView); - textView.setCompoundDrawablesWithIntrinsicBounds(isRtl ? end : start, top, - isRtl ? start : end, bottom); - } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) { - textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom); - } else { - textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom); - } - } - - /** - * @see android.widget.TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, - * int) - */ - public static void setCompoundDrawablesRelativeWithIntrinsicBounds(TextView textView, - int start, int top, int end, int bottom) { - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) { - // Work around the platform bug described in setCompoundDrawablesRelative() above. - boolean isRtl = isLayoutRtl(textView); - textView.setCompoundDrawablesWithIntrinsicBounds(isRtl ? end : start, top, - isRtl ? start : end, bottom); - } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) { - textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom); - } else { - textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom); - } - } - - /** - * @see android.text.Html#toHtml(Spanned, int) - * @param option is ignored on below N - */ - @SuppressWarnings("deprecation") - public static String toHtml(Spanned spanned, int option) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - return Html.toHtml(spanned, option); - } else { - return Html.toHtml(spanned); - } - } - - // These methods have a new name, and the old name is deprecated. - - /** - * @see android.app.PendingIntent#getCreatorPackage() - */ - @SuppressWarnings("deprecation") - public static String getCreatorPackage(PendingIntent intent) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - return intent.getCreatorPackage(); - } else { - return intent.getTargetPackage(); - } - } - - /** - * @see android.provider.Settings.Global#DEVICE_PROVISIONED - */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - public static boolean isDeviceProvisioned(Context context) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) return true; - if (context == null) return true; - if (context.getContentResolver() == null) return true; - return Settings.Global.getInt( - context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; - } - - /** - * @see android.app.Activity#finishAndRemoveTask() - */ - public static void finishAndRemoveTask(Activity activity) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { - activity.finishAndRemoveTask(); - } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { - // crbug.com/395772 : Fallback for Activity.finishAndRemoveTask() failing. - new FinishAndRemoveTaskWithRetry(activity).run(); - } else { - activity.finish(); - } - } - - /** - * Set elevation if supported. - */ - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public static boolean setElevation(View view, float elevationValue) { - if (!isElevationSupported()) return false; - - view.setElevation(elevationValue); - return true; - } - - private static class FinishAndRemoveTaskWithRetry implements Runnable { - private static final long RETRY_DELAY_MS = 500; - private static final long MAX_TRY_COUNT = 3; - private final Activity mActivity; - private int mTryCount; - - FinishAndRemoveTaskWithRetry(Activity activity) { - mActivity = activity; - } - - @Override - public void run() { - mActivity.finishAndRemoveTask(); - mTryCount++; - if (!mActivity.isFinishing()) { - if (mTryCount < MAX_TRY_COUNT) { - ThreadUtils.postOnUiThreadDelayed(this, RETRY_DELAY_MS); - } else { - mActivity.finish(); - } - } - } - } - - /** - * @return Whether the screen of the device is interactive. - */ - @SuppressWarnings("deprecation") - public static boolean isInteractive(Context context) { - PowerManager manager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { - return manager.isInteractive(); - } else { - return manager.isScreenOn(); - } - } - - @SuppressWarnings("deprecation") - public static int getActivityNewDocumentFlag() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return Intent.FLAG_ACTIVITY_NEW_DOCUMENT; - } else { - return Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET; - } - } - - /** - * @see android.provider.Settings.Secure#SKIP_FIRST_USE_HINTS - */ - public static boolean shouldSkipFirstUseHints(ContentResolver contentResolver) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return Settings.Secure.getInt( - contentResolver, Settings.Secure.SKIP_FIRST_USE_HINTS, 0) != 0; - } else { - return false; - } - } - - /** - * @param activity Activity that should get the task description update. - * @param title Title of the activity. - * @param icon Icon of the activity. - * @param color Color of the activity. It must be a fully opaque color. - */ - public static void setTaskDescription(Activity activity, String title, Bitmap icon, int color) { - // TaskDescription requires an opaque color. - assert Color.alpha(color) == 255; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - ActivityManager.TaskDescription description = - new ActivityManager.TaskDescription(title, icon, color); - activity.setTaskDescription(description); - } - } - - /** - * @see android.view.Window#setStatusBarColor(int color). - */ - public static void setStatusBarColor(Window window, int statusBarColor) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; - - // If both system bars are black, we can remove these from our layout, - // removing or shrinking the SurfaceFlinger overlay required for our views. - // This benefits battery usage on L and M. However, this no longer provides a battery - // benefit as of N and starts to cause flicker bugs on O, so don't bother on O and up. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O && statusBarColor == Color.BLACK - && window.getNavigationBarColor() == Color.BLACK) { - window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - } else { - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - } - window.setStatusBarColor(statusBarColor); - } - - /** - * Sets the status bar icons to dark or light. Note that this is only valid for - * Android M+. - * - * @param rootView The root view used to request updates to the system UI theming. - * @param useDarkIcons Whether the status bar icons should be dark. - */ - public static void setStatusBarIconColor(View rootView, boolean useDarkIcons) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return; - - int systemUiVisibility = rootView.getSystemUiVisibility(); - if (useDarkIcons) { - systemUiVisibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; - } else { - systemUiVisibility &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; - } - rootView.setSystemUiVisibility(systemUiVisibility); - } - - /** - * @see android.content.res.Resources#getDrawable(int id). - * TODO(ltian): use {@link AppCompatResources} to parse drawable to prevent fail on - * {@link VectorDrawable}. (http://crbug.com/792129) - */ - @SuppressWarnings("deprecation") - public static Drawable getDrawable(Resources res, int id) throws NotFoundException { - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return res.getDrawable(id, null); - } else { - return res.getDrawable(id); - } - } finally { - StrictMode.setThreadPolicy(oldPolicy); - } - } - - /** - * @see android.content.res.Resources#getDrawableForDensity(int id, int density). - */ - @SuppressWarnings("deprecation") - public static Drawable getDrawableForDensity(Resources res, int id, int density) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return res.getDrawableForDensity(id, density, null); - } else { - return res.getDrawableForDensity(id, density); - } - } - - /** - * @see android.app.Activity#finishAfterTransition(). - */ - public static void finishAfterTransition(Activity activity) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - activity.finishAfterTransition(); - } else { - activity.finish(); - } - } - - /** - * @see android.content.pm.PackageManager#getUserBadgedIcon(Drawable, android.os.UserHandle). - */ - public static Drawable getUserBadgedIcon(Context context, int id) { - Drawable drawable = getDrawable(context.getResources(), id); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - PackageManager packageManager = context.getPackageManager(); - drawable = packageManager.getUserBadgedIcon(drawable, Process.myUserHandle()); - } - return drawable; - } - - /** - * @see android.content.pm.PackageManager#getUserBadgedDrawableForDensity(Drawable drawable, - * UserHandle user, Rect badgeLocation, int badgeDensity). - */ - public static Drawable getUserBadgedDrawableForDensity( - Context context, Drawable drawable, Rect badgeLocation, int density) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - PackageManager packageManager = context.getPackageManager(); - return packageManager.getUserBadgedDrawableForDensity( - drawable, Process.myUserHandle(), badgeLocation, density); - } - return drawable; - } - - /** - * @see android.content.res.Resources#getColor(int id). - */ - @SuppressWarnings("deprecation") - public static int getColor(Resources res, int id) throws NotFoundException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - return res.getColor(id, null); - } else { - return res.getColor(id); - } - } - - /** - * @see android.graphics.drawable.Drawable#getColorFilter(). - */ - @SuppressWarnings("NewApi") - public static ColorFilter getColorFilter(Drawable drawable) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return drawable.getColorFilter(); - } else { - return null; - } - } - - /** - * @see android.widget.TextView#setTextAppearance(int id). - */ - @SuppressWarnings("deprecation") - public static void setTextAppearance(TextView view, int id) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - view.setTextAppearance(id); - } else { - view.setTextAppearance(view.getContext(), id); - } - } - - /** - * See {@link android.os.StatFs#getAvailableBlocksLong}. - */ - @SuppressWarnings("deprecation") - public static long getAvailableBlocks(StatFs statFs) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - return statFs.getAvailableBlocksLong(); - } else { - return statFs.getAvailableBlocks(); - } - } - - /** - * See {@link android.os.StatFs#getBlockCount}. - */ - @SuppressWarnings("deprecation") - public static long getBlockCount(StatFs statFs) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - return statFs.getBlockCountLong(); - } else { - return statFs.getBlockCount(); - } - } - - /** - * See {@link android.os.StatFs#getBlockSize}. - */ - @SuppressWarnings("deprecation") - public static long getBlockSize(StatFs statFs) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - return statFs.getBlockSizeLong(); - } else { - return statFs.getBlockSize(); - } - } - - /** - * @param context The Android context, used to retrieve the UserManager system service. - * @return Whether the device is running in demo mode. - */ - @SuppressWarnings("NewApi") - public static boolean isDemoUser(Context context) { - // UserManager#isDemoUser() is only available in Android NMR1+. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) return false; - - UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - return userManager.isDemoUser(); - } - - /** - * @see Context#checkPermission(String, int, int) - */ - public static int checkPermission(Context context, String permission, int pid, int uid) { - try { - return context.checkPermission(permission, pid, uid); - } catch (RuntimeException e) { - // Some older versions of Android throw odd errors when checking for permissions, so - // just swallow the exception and treat it as the permission is denied. - // crbug.com/639099 - return PackageManager.PERMISSION_DENIED; - } - } - - /** - * @see android.view.inputmethod.InputMethodSubType#getLocate() - */ - @SuppressWarnings("deprecation") - public static String getLocale(InputMethodSubtype inputMethodSubType) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - return inputMethodSubType.getLanguageTag(); - } else { - return inputMethodSubType.getLocale(); - } - } - - /** - * Get a URI for |file| which has the image capture. This function assumes that path of |file| - * is based on the result of UiUtils.getDirectoryForImageCapture(). - * - * @param file image capture file. - * @return URI for |file|. - */ - public static Uri getUriForImageCaptureFile(File file) { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 - ? ContentUriUtils.getContentUriFromFile(file) - : Uri.fromFile(file); - } - - /** - * Get the URI for a downloaded file. - * - * @param file A downloaded file. - * @return URI for |file|. - */ - public static Uri getUriForDownloadedFile(File file) { - return Build.VERSION.SDK_INT > Build.VERSION_CODES.M - ? FileUtils.getUriForFile(file) - : Uri.fromFile(file); - } - - /** - * @see android.view.Window#FEATURE_INDETERMINATE_PROGRESS - */ - public static void setWindowIndeterminateProgress(Window window) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - @SuppressWarnings("deprecation") - int featureNumber = Window.FEATURE_INDETERMINATE_PROGRESS; - - @SuppressWarnings("deprecation") - int featureValue = Window.PROGRESS_VISIBILITY_OFF; - - window.setFeatureInt(featureNumber, featureValue); - } - } - - /** - * @param activity The {@link Activity} to check. - * @return Whether or not {@code activity} is currently in Android N+ multi-window mode. - */ - public static boolean isInMultiWindowMode(Activity activity) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { - return false; - } - return activity.isInMultiWindowMode(); - } - - /** - * Disables the Smart Select {@link TextClassifier} for the given {@link TextView} instance. - * @param textView The {@link TextView} that should have its classifier disabled. - */ - @TargetApi(Build.VERSION_CODES.O) - public static void disableSmartSelectionTextClassifier(TextView textView) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return; - - textView.setTextClassifier(TextClassifier.NO_OP); - } - - /** - * Creates an ActivityOptions Bundle with basic options and the LaunchDisplayId set. - * @param displayId The id of the display to launch into. - * @return The created bundle, or null if unsupported. - */ - public static Bundle createLaunchDisplayIdActivityOptions(int displayId) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return null; - - ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchDisplayId(displayId); - return options.toBundle(); - } -} diff --git a/android/java/src/org/chromium/base/ApkAssets.java b/android/java/src/org/chromium/base/ApkAssets.java deleted file mode 100644 index 19108e595..000000000 --- a/android/java/src/org/chromium/base/ApkAssets.java +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.content.res.AssetFileDescriptor; -import android.content.res.AssetManager; -import android.util.Log; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -import java.io.IOException; - -/** - * A utility class to retrieve references to uncompressed assets insides the apk. A reference is - * defined as tuple (file descriptor, offset, size) enabling direct mapping without deflation. - * This can be used even within the renderer process, since it just dup's the apk's fd. - */ -@JNINamespace("base::android") -public class ApkAssets { - private static final String LOGTAG = "ApkAssets"; - - @CalledByNative - public static long[] open(String fileName) { - AssetFileDescriptor afd = null; - try { - AssetManager manager = ContextUtils.getApplicationContext().getAssets(); - afd = manager.openNonAssetFd(fileName); - return new long[] {afd.getParcelFileDescriptor().detachFd(), afd.getStartOffset(), - afd.getLength()}; - } catch (IOException e) { - // As a general rule there's no point logging here because the caller should handle - // receiving an fd of -1 sensibly, and the log message is either mirrored later, or - // unwanted (in the case where a missing file is expected), or wanted but will be - // ignored, as most non-fatal logs are. - // It makes sense to log here when the file exists, but is unable to be opened as an fd - // because (for example) it is unexpectedly compressed in an apk. In that case, the log - // message might save someone some time working out what has gone wrong. - // For that reason, we only suppress the message when the exception message doesn't look - // informative (Android framework passes the filename as the message on actual file not - // found, and the empty string also wouldn't give any useful information for debugging). - if (!e.getMessage().equals("") && !e.getMessage().equals(fileName)) { - Log.e(LOGTAG, "Error while loading asset " + fileName + ": " + e); - } - return new long[] {-1, -1, -1}; - } finally { - try { - if (afd != null) { - afd.close(); - } - } catch (IOException e2) { - Log.e(LOGTAG, "Unable to close AssetFileDescriptor", e2); - } - } - } -} diff --git a/android/java/src/org/chromium/base/ApplicationStatus.java b/android/java/src/org/chromium/base/ApplicationStatus.java deleted file mode 100644 index 9496da8c1..000000000 --- a/android/java/src/org/chromium/base/ApplicationStatus.java +++ /dev/null @@ -1,620 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.Application; -import android.app.Application.ActivityLifecycleCallbacks; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.view.Window; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -import java.lang.ref.WeakReference; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Provides information about the current activity's status, and a way - * to register / unregister listeners for state changes. - */ -@JNINamespace("base::android") -public class ApplicationStatus { - private static final String TOOLBAR_CALLBACK_INTERNAL_WRAPPER_CLASS = - "android.support.v7.internal.app.ToolbarActionBar$ToolbarCallbackWrapper"; - // In builds using the --use_unpublished_apis flag, the ToolbarActionBar class name does not - // include the "internal" package. - private static final String TOOLBAR_CALLBACK_WRAPPER_CLASS = - "android.support.v7.app.ToolbarActionBar$ToolbarCallbackWrapper"; - private static final String WINDOW_PROFILER_CALLBACK = - "com.android.tools.profiler.support.event.WindowProfilerCallback"; - - private static class ActivityInfo { - private int mStatus = ActivityState.DESTROYED; - private ObserverList mListeners = new ObserverList<>(); - - /** - * @return The current {@link ActivityState} of the activity. - */ - @ActivityState - public int getStatus() { - return mStatus; - } - - /** - * @param status The new {@link ActivityState} of the activity. - */ - public void setStatus(@ActivityState int status) { - mStatus = status; - } - - /** - * @return A list of {@link ActivityStateListener}s listening to this activity. - */ - public ObserverList getListeners() { - return mListeners; - } - } - - static { - // Chrome initializes this only for the main process. This assert aims to try and catch - // usages from GPU / renderers, while still allowing tests. - assert ContextUtils.isMainProcess() - || ContextUtils.getProcessName().contains(":test") - : "Cannot use ApplicationState from process: " - + ContextUtils.getProcessName(); - } - - private static final Object sCurrentApplicationStateLock = new Object(); - - @SuppressLint("SupportAnnotationUsage") - @ApplicationState - // The getStateForApplication() historically returned ApplicationState.HAS_DESTROYED_ACTIVITIES - // when no activity has been observed. - private static Integer sCurrentApplicationState = ApplicationState.HAS_DESTROYED_ACTIVITIES; - - /** Last activity that was shown (or null if none or it was destroyed). */ - @SuppressLint("StaticFieldLeak") - private static Activity sActivity; - - /** A lazily initialized listener that forwards application state changes to native. */ - private static ApplicationStateListener sNativeApplicationStateListener; - - private static boolean sIsInitialized; - - /** - * A map of which observers listen to state changes from which {@link Activity}. - */ - private static final Map sActivityInfo = new ConcurrentHashMap<>(); - - /** - * A list of observers to be notified when any {@link Activity} has a state change. - */ - private static final ObserverList sGeneralActivityStateListeners = - new ObserverList<>(); - - /** - * A list of observers to be notified when the visibility state of this {@link Application} - * changes. See {@link #getStateForApplication()}. - */ - private static final ObserverList sApplicationStateListeners = - new ObserverList<>(); - - /** - * A list of observers to be notified when the window focus changes. - * See {@link #registerWindowFocusChangedListener}. - */ - private static final ObserverList sWindowFocusListeners = - new ObserverList<>(); - - /** - * Interface to be implemented by listeners. - */ - public interface ApplicationStateListener { - /** - * Called when the application's state changes. - * @param newState The application state. - */ - void onApplicationStateChange(@ApplicationState int newState); - } - - /** - * Interface to be implemented by listeners. - */ - public interface ActivityStateListener { - /** - * Called when the activity's state changes. - * @param activity The activity that had a state change. - * @param newState New activity state. - */ - void onActivityStateChange(Activity activity, @ActivityState int newState); - } - - /** - * Interface to be implemented by listeners for window focus events. - */ - public interface WindowFocusChangedListener { - /** - * Called when the window focus changes for {@code activity}. - * @param activity The {@link Activity} that has a window focus changed event. - * @param hasFocus Whether or not {@code activity} gained or lost focus. - */ - public void onWindowFocusChanged(Activity activity, boolean hasFocus); - } - - private ApplicationStatus() {} - - /** - * Registers a listener to receive window focus updates on activities in this application. - * @param listener Listener to receive window focus events. - */ - public static void registerWindowFocusChangedListener(WindowFocusChangedListener listener) { - sWindowFocusListeners.addObserver(listener); - } - - /** - * Unregisters a listener from receiving window focus updates on activities in this application. - * @param listener Listener that doesn't want to receive window focus events. - */ - public static void unregisterWindowFocusChangedListener(WindowFocusChangedListener listener) { - sWindowFocusListeners.removeObserver(listener); - } - - /** - * Intercepts calls to an existing Window.Callback. Most invocations are passed on directly - * to the composed Window.Callback but enables intercepting/manipulating others. - * - * This is used to relay window focus changes throughout the app and remedy a bug in the - * appcompat library. - */ - private static class WindowCallbackProxy implements InvocationHandler { - private final Window.Callback mCallback; - private final Activity mActivity; - - public WindowCallbackProxy(Activity activity, Window.Callback callback) { - mCallback = callback; - mActivity = activity; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.getName().equals("onWindowFocusChanged") && args.length == 1 - && args[0] instanceof Boolean) { - onWindowFocusChanged((boolean) args[0]); - return null; - } else { - try { - return method.invoke(mCallback, args); - } catch (InvocationTargetException e) { - // Special-case for when a method is not defined on the underlying - // Window.Callback object. Because we're using a Proxy to forward all method - // calls, this breaks the Android framework's handling for apps built against - // an older SDK. The framework expects an AbstractMethodError but due to - // reflection it becomes wrapped inside an InvocationTargetException. Undo the - // wrapping to signal the framework accordingly. - if (e.getCause() instanceof AbstractMethodError) { - throw e.getCause(); - } - throw e; - } - } - } - - public void onWindowFocusChanged(boolean hasFocus) { - mCallback.onWindowFocusChanged(hasFocus); - - for (WindowFocusChangedListener listener : sWindowFocusListeners) { - listener.onWindowFocusChanged(mActivity, hasFocus); - } - } - } - - /** - * Initializes the activity status for a specified application. - * - * @param application The application whose status you wish to monitor. - */ - public static void initialize(Application application) { - if (sIsInitialized) return; - sIsInitialized = true; - - registerWindowFocusChangedListener(new WindowFocusChangedListener() { - @Override - public void onWindowFocusChanged(Activity activity, boolean hasFocus) { - if (!hasFocus || activity == sActivity) return; - - int state = getStateForActivity(activity); - - if (state != ActivityState.DESTROYED && state != ActivityState.STOPPED) { - sActivity = activity; - } - - // TODO(dtrainor): Notify of active activity change? - } - }); - - application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { - @Override - public void onActivityCreated(final Activity activity, Bundle savedInstanceState) { - onStateChange(activity, ActivityState.CREATED); - Window.Callback callback = activity.getWindow().getCallback(); - activity.getWindow().setCallback((Window.Callback) Proxy.newProxyInstance( - Window.Callback.class.getClassLoader(), new Class[] {Window.Callback.class}, - new ApplicationStatus.WindowCallbackProxy(activity, callback))); - } - - @Override - public void onActivityDestroyed(Activity activity) { - onStateChange(activity, ActivityState.DESTROYED); - checkCallback(activity); - } - - @Override - public void onActivityPaused(Activity activity) { - onStateChange(activity, ActivityState.PAUSED); - checkCallback(activity); - } - - @Override - public void onActivityResumed(Activity activity) { - onStateChange(activity, ActivityState.RESUMED); - checkCallback(activity); - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) { - checkCallback(activity); - } - - @Override - public void onActivityStarted(Activity activity) { - onStateChange(activity, ActivityState.STARTED); - checkCallback(activity); - } - - @Override - public void onActivityStopped(Activity activity) { - onStateChange(activity, ActivityState.STOPPED); - checkCallback(activity); - } - - private void checkCallback(Activity activity) { - if (BuildConfig.DCHECK_IS_ON) { - Class callback = - activity.getWindow().getCallback().getClass(); - assert(Proxy.isProxyClass(callback) - || callback.getName().equals(TOOLBAR_CALLBACK_WRAPPER_CLASS) - || callback.getName().equals(TOOLBAR_CALLBACK_INTERNAL_WRAPPER_CLASS) - || callback.getName().equals(WINDOW_PROFILER_CALLBACK)); - } - } - }); - } - - /** - * Asserts that initialize method has been called. - */ - private static void assertInitialized() { - if (!sIsInitialized) { - throw new IllegalStateException("ApplicationStatus has not been initialized yet."); - } - } - - /** - * Must be called by the main activity when it changes state. - * - * @param activity Current activity. - * @param newState New state value. - */ - private static void onStateChange(Activity activity, @ActivityState int newState) { - if (activity == null) throw new IllegalArgumentException("null activity is not supported"); - - if (sActivity == null - || newState == ActivityState.CREATED - || newState == ActivityState.RESUMED - || newState == ActivityState.STARTED) { - sActivity = activity; - } - - int oldApplicationState = getStateForApplication(); - ActivityInfo info; - - synchronized (sCurrentApplicationStateLock) { - if (newState == ActivityState.CREATED) { - assert !sActivityInfo.containsKey(activity); - sActivityInfo.put(activity, new ActivityInfo()); - } - - info = sActivityInfo.get(activity); - info.setStatus(newState); - - // Remove before calling listeners so that isEveryActivityDestroyed() returns false when - // this was the last activity. - if (newState == ActivityState.DESTROYED) { - sActivityInfo.remove(activity); - if (activity == sActivity) sActivity = null; - } - - sCurrentApplicationState = determineApplicationState(); - } - - // Notify all state observers that are specifically listening to this activity. - for (ActivityStateListener listener : info.getListeners()) { - listener.onActivityStateChange(activity, newState); - } - - // Notify all state observers that are listening globally for all activity state - // changes. - for (ActivityStateListener listener : sGeneralActivityStateListeners) { - listener.onActivityStateChange(activity, newState); - } - - int applicationState = getStateForApplication(); - if (applicationState != oldApplicationState) { - for (ApplicationStateListener listener : sApplicationStateListeners) { - listener.onApplicationStateChange(applicationState); - } - } - } - - /** - * Testing method to update the state of the specified activity. - */ - @VisibleForTesting - public static void onStateChangeForTesting(Activity activity, int newState) { - onStateChange(activity, newState); - } - - /** - * @return The most recent focused {@link Activity} tracked by this class. Being focused means - * out of all the activities tracked here, it has most recently gained window focus. - */ - public static Activity getLastTrackedFocusedActivity() { - return sActivity; - } - - /** - * @return A {@link List} of all non-destroyed {@link Activity}s. - */ - public static List> getRunningActivities() { - assertInitialized(); - List> activities = new ArrayList<>(); - for (Activity activity : sActivityInfo.keySet()) { - activities.add(new WeakReference<>(activity)); - } - return activities; - } - - /** - * Query the state for a given activity. If the activity is not being tracked, this will - * return {@link ActivityState#DESTROYED}. - * - *

- * Please note that Chrome can have multiple activities running simultaneously. Please also - * look at {@link #getStateForApplication()} for more details. - * - *

- * When relying on this method, be familiar with the expected life cycle state - * transitions: - * - * Activity Lifecycle - * - * - *

- * During activity transitions (activity B launching in front of activity A), A will completely - * paused before the creation of activity B begins. - * - *

- * A basic flow for activity A starting, followed by activity B being opened and then closed: - *

    - *
  • -- Starting Activity A -- - *
  • Activity A - ActivityState.CREATED - *
  • Activity A - ActivityState.STARTED - *
  • Activity A - ActivityState.RESUMED - *
  • -- Starting Activity B -- - *
  • Activity A - ActivityState.PAUSED - *
  • Activity B - ActivityState.CREATED - *
  • Activity B - ActivityState.STARTED - *
  • Activity B - ActivityState.RESUMED - *
  • Activity A - ActivityState.STOPPED - *
  • -- Closing Activity B, Activity A regaining focus -- - *
  • Activity B - ActivityState.PAUSED - *
  • Activity A - ActivityState.STARTED - *
  • Activity A - ActivityState.RESUMED - *
  • Activity B - ActivityState.STOPPED - *
  • Activity B - ActivityState.DESTROYED - *
- * - * @param activity The activity whose state is to be returned. - * @return The state of the specified activity (see {@link ActivityState}). - */ - @ActivityState - public static int getStateForActivity(@Nullable Activity activity) { - ApplicationStatus.assertInitialized(); - if (activity == null) return ActivityState.DESTROYED; - ActivityInfo info = sActivityInfo.get(activity); - return info != null ? info.getStatus() : ActivityState.DESTROYED; - } - - /** - * @return The state of the application (see {@link ApplicationState}). - */ - @ApplicationState - @CalledByNative - public static int getStateForApplication() { - synchronized (sCurrentApplicationStateLock) { - return sCurrentApplicationState; - } - } - - /** - * Checks whether or not any Activity in this Application is visible to the user. Note that - * this includes the PAUSED state, which can happen when the Activity is temporarily covered - * by another Activity's Fragment (e.g.). - * @return Whether any Activity under this Application is visible. - */ - public static boolean hasVisibleActivities() { - int state = getStateForApplication(); - return state == ApplicationState.HAS_RUNNING_ACTIVITIES - || state == ApplicationState.HAS_PAUSED_ACTIVITIES; - } - - /** - * Checks to see if there are any active Activity instances being watched by ApplicationStatus. - * @return True if all Activities have been destroyed. - */ - public static boolean isEveryActivityDestroyed() { - return sActivityInfo.isEmpty(); - } - - /** - * Registers the given listener to receive state changes for all activities. - * @param listener Listener to receive state changes. - */ - public static void registerStateListenerForAllActivities(ActivityStateListener listener) { - sGeneralActivityStateListeners.addObserver(listener); - } - - /** - * Registers the given listener to receive state changes for {@code activity}. After a call to - * {@link ActivityStateListener#onActivityStateChange(Activity, int)} with - * {@link ActivityState#DESTROYED} all listeners associated with that particular - * {@link Activity} are removed. - * @param listener Listener to receive state changes. - * @param activity Activity to track or {@code null} to track all activities. - */ - @SuppressLint("NewApi") - public static void registerStateListenerForActivity(ActivityStateListener listener, - Activity activity) { - if (activity == null) { - throw new IllegalStateException("Attempting to register listener on a null activity."); - } - ApplicationStatus.assertInitialized(); - - ActivityInfo info = sActivityInfo.get(activity); - if (info == null) { - throw new IllegalStateException( - "Attempting to register listener on an untracked activity."); - } - assert info.getStatus() != ActivityState.DESTROYED; - info.getListeners().addObserver(listener); - } - - /** - * Unregisters the given listener from receiving activity state changes. - * @param listener Listener that doesn't want to receive state changes. - */ - public static void unregisterActivityStateListener(ActivityStateListener listener) { - sGeneralActivityStateListeners.removeObserver(listener); - - // Loop through all observer lists for all activities and remove the listener. - for (ActivityInfo info : sActivityInfo.values()) { - info.getListeners().removeObserver(listener); - } - } - - /** - * Registers the given listener to receive state changes for the application. - * @param listener Listener to receive state state changes. - */ - public static void registerApplicationStateListener(ApplicationStateListener listener) { - sApplicationStateListeners.addObserver(listener); - } - - /** - * Unregisters the given listener from receiving state changes. - * @param listener Listener that doesn't want to receive state changes. - */ - public static void unregisterApplicationStateListener(ApplicationStateListener listener) { - sApplicationStateListeners.removeObserver(listener); - } - - /** - * Robolectric JUnit tests create a new application between each test, while all the context - * in static classes isn't reset. This function allows to reset the application status to avoid - * being in a dirty state. - */ - public static void destroyForJUnitTests() { - sApplicationStateListeners.clear(); - sGeneralActivityStateListeners.clear(); - sActivityInfo.clear(); - sWindowFocusListeners.clear(); - sIsInitialized = false; - synchronized (sCurrentApplicationStateLock) { - sCurrentApplicationState = determineApplicationState(); - } - sActivity = null; - sNativeApplicationStateListener = null; - } - - /** - * Registers the single thread-safe native activity status listener. - * This handles the case where the caller is not on the main thread. - * Note that this is used by a leaky singleton object from the native - * side, hence lifecycle management is greatly simplified. - */ - @CalledByNative - private static void registerThreadSafeNativeApplicationStateListener() { - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - if (sNativeApplicationStateListener != null) return; - - sNativeApplicationStateListener = new ApplicationStateListener() { - @Override - public void onApplicationStateChange(int newState) { - nativeOnApplicationStateChange(newState); - } - }; - registerApplicationStateListener(sNativeApplicationStateListener); - } - }); - } - - /** - * Determines the current application state as defined by {@link ApplicationState}. This will - * loop over all the activities and check their state to determine what the general application - * state should be. - * @return HAS_RUNNING_ACTIVITIES if any activity is not paused, stopped, or destroyed. - * HAS_PAUSED_ACTIVITIES if none are running and one is paused. - * HAS_STOPPED_ACTIVITIES if none are running/paused and one is stopped. - * HAS_DESTROYED_ACTIVITIES if none are running/paused/stopped. - */ - @ApplicationState - private static int determineApplicationState() { - boolean hasPausedActivity = false; - boolean hasStoppedActivity = false; - - for (ActivityInfo info : sActivityInfo.values()) { - int state = info.getStatus(); - if (state != ActivityState.PAUSED - && state != ActivityState.STOPPED - && state != ActivityState.DESTROYED) { - return ApplicationState.HAS_RUNNING_ACTIVITIES; - } else if (state == ActivityState.PAUSED) { - hasPausedActivity = true; - } else if (state == ActivityState.STOPPED) { - hasStoppedActivity = true; - } - } - - if (hasPausedActivity) return ApplicationState.HAS_PAUSED_ACTIVITIES; - if (hasStoppedActivity) return ApplicationState.HAS_STOPPED_ACTIVITIES; - return ApplicationState.HAS_DESTROYED_ACTIVITIES; - } - - // Called to notify the native side of state changes. - // IMPORTANT: This is always called on the main thread! - private static native void nativeOnApplicationStateChange(@ApplicationState int newState); -} diff --git a/android/java/src/org/chromium/base/BaseSwitches.java b/android/java/src/org/chromium/base/BaseSwitches.java deleted file mode 100644 index fe47cdda1..000000000 --- a/android/java/src/org/chromium/base/BaseSwitches.java +++ /dev/null @@ -1,32 +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. - -package org.chromium.base; - -/** - * Contains all of the command line switches that are specific to the base/ - * portion of Chromium on Android. - */ -public abstract class BaseSwitches { - // Block ChildProcessMain thread of render process service until a Java debugger is attached. - // To pause even earlier: am set-debug-app org.chromium.chrome:sandbox_process0 - // However, this flag is convenient when you don't know the process number, or want - // all renderers to pause (set-debug-app applies to only one process at a time). - public static final String RENDERER_WAIT_FOR_JAVA_DEBUGGER = "renderer-wait-for-java-debugger"; - - // Force low-end device mode when set. - public static final String ENABLE_LOW_END_DEVICE_MODE = "enable-low-end-device-mode"; - - // Force disabling of low-end device mode when set. - public static final String DISABLE_LOW_END_DEVICE_MODE = "disable-low-end-device-mode"; - - // Adds additional thread idle time information into the trace event output. - public static final String ENABLE_IDLE_TRACING = "enable-idle-tracing"; - - // Default country code to be used for search engine localization. - public static final String DEFAULT_COUNTRY_CODE_AT_INSTALL = "default-country-code"; - - // Prevent instantiation. - private BaseSwitches() {} -} diff --git a/android/java/src/org/chromium/base/BuildInfo.java b/android/java/src/org/chromium/base/BuildInfo.java deleted file mode 100644 index 111bca8d0..000000000 --- a/android/java/src/org/chromium/base/BuildInfo.java +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 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. - -package org.chromium.base; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Build; -import android.os.Build.VERSION; -import android.text.TextUtils; - -import org.chromium.base.annotations.CalledByNative; - -/** - * BuildInfo is a utility class providing easy access to {@link PackageInfo} information. This is - * primarily of use for accessing package information from native code. - */ -public class BuildInfo { - private static final String TAG = "BuildInfo"; - private static final int MAX_FINGERPRINT_LENGTH = 128; - - private static PackageInfo sBrowserPackageInfo; - private static boolean sInitialized; - - /** The application name (e.g. "Chrome"). For WebView, this is name of the embedding app. */ - public final String hostPackageLabel; - /** By default: same as versionCode. For WebView: versionCode of the embedding app. */ - public final int hostVersionCode; - /** The packageName of Chrome/WebView. Use application context for host app packageName. */ - public final String packageName; - /** The versionCode of the apk. */ - public final int versionCode; - /** The versionName of Chrome/WebView. Use application context for host app versionName. */ - public final String versionName; - /** Result of PackageManager.getInstallerPackageName(). Never null, but may be "". */ - public final String installerPackageName; - /** The versionCode of Play Services (for crash reporting). */ - public final String gmsVersionCode; - /** Formatted ABI string (for crash reporting). */ - public final String abiString; - /** Truncated version of Build.FINGERPRINT (for crash reporting). */ - public final String androidBuildFingerprint; - /** A string that is different each time the apk changes. */ - public final String extractedFileSuffix; - /** Whether or not the device has apps installed for using custom themes. */ - public final String customThemes; - /** Product version as stored in Android resources. */ - public final String resourcesVersion; - - private static class Holder { private static BuildInfo sInstance = new BuildInfo(); } - - @CalledByNative - private static String[] getAll() { - BuildInfo buildInfo = getInstance(); - String hostPackageName = ContextUtils.getApplicationContext().getPackageName(); - return new String[] { - Build.BRAND, Build.DEVICE, Build.ID, Build.MANUFACTURER, Build.MODEL, - String.valueOf(Build.VERSION.SDK_INT), Build.TYPE, Build.BOARD, hostPackageName, - String.valueOf(buildInfo.hostVersionCode), buildInfo.hostPackageLabel, - buildInfo.packageName, String.valueOf(buildInfo.versionCode), buildInfo.versionName, - buildInfo.androidBuildFingerprint, buildInfo.gmsVersionCode, - buildInfo.installerPackageName, buildInfo.abiString, BuildConfig.FIREBASE_APP_ID, - buildInfo.customThemes, buildInfo.resourcesVersion, buildInfo.extractedFileSuffix, - isAtLeastP() ? "1" : "0", - }; - } - - private static String nullToEmpty(CharSequence seq) { - return seq == null ? "" : seq.toString(); - } - - /** - * @param packageInfo Package for Chrome/WebView (as opposed to host app). - */ - public static void setBrowserPackageInfo(PackageInfo packageInfo) { - assert !sInitialized; - sBrowserPackageInfo = packageInfo; - } - - public static BuildInfo getInstance() { - return Holder.sInstance; - } - - private BuildInfo() { - sInitialized = true; - try { - Context appContext = ContextUtils.getApplicationContext(); - String hostPackageName = appContext.getPackageName(); - PackageManager pm = appContext.getPackageManager(); - PackageInfo pi = pm.getPackageInfo(hostPackageName, 0); - hostVersionCode = pi.versionCode; - if (sBrowserPackageInfo != null) { - packageName = sBrowserPackageInfo.packageName; - versionCode = sBrowserPackageInfo.versionCode; - versionName = nullToEmpty(sBrowserPackageInfo.versionName); - sBrowserPackageInfo = null; - } else { - packageName = hostPackageName; - versionCode = hostVersionCode; - versionName = nullToEmpty(pi.versionName); - } - - hostPackageLabel = nullToEmpty(pm.getApplicationLabel(pi.applicationInfo)); - installerPackageName = nullToEmpty(pm.getInstallerPackageName(packageName)); - - PackageInfo gmsPackageInfo = null; - try { - gmsPackageInfo = pm.getPackageInfo("com.google.android.gms", 0); - } catch (NameNotFoundException e) { - Log.d(TAG, "GMS package is not found.", e); - } - gmsVersionCode = gmsPackageInfo != null ? String.valueOf(gmsPackageInfo.versionCode) - : "gms versionCode not available."; - - String hasCustomThemes = "true"; - try { - // Substratum is a theme engine that enables users to use custom themes provided - // by theme apps. Sometimes these can cause crashs if not installed correctly. - // These crashes can be difficult to debug, so knowing if the theme manager is - // present on the device is useful (http://crbug.com/820591). - pm.getPackageInfo("projekt.substratum", 0); - } catch (NameNotFoundException e) { - hasCustomThemes = "false"; - } - customThemes = hasCustomThemes; - - String currentResourcesVersion = "Not Enabled"; - // Controlled by target specific build flags. - if (BuildConfig.R_STRING_PRODUCT_VERSION != 0) { - try { - // This value can be compared with the actual product version to determine if - // corrupted resources were the cause of a crash. This can happen if the app - // loads resources from the outdated package during an update - // (http://crbug.com/820591). - currentResourcesVersion = ContextUtils.getApplicationContext().getString( - BuildConfig.R_STRING_PRODUCT_VERSION); - } catch (Exception e) { - currentResourcesVersion = "Not found"; - } - } - resourcesVersion = currentResourcesVersion; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - abiString = TextUtils.join(", ", Build.SUPPORTED_ABIS); - } else { - abiString = String.format("ABI1: %s, ABI2: %s", Build.CPU_ABI, Build.CPU_ABI2); - } - - // Use lastUpdateTime when developing locally, since versionCode does not normally - // change in this case. - long version = versionCode > 10 ? versionCode : pi.lastUpdateTime; - extractedFileSuffix = String.format("@%x", version); - - // The value is truncated, as this is used for crash and UMA reporting. - androidBuildFingerprint = Build.FINGERPRINT.substring( - 0, Math.min(Build.FINGERPRINT.length(), MAX_FINGERPRINT_LENGTH)); - } catch (NameNotFoundException e) { - throw new RuntimeException(e); - } - } - - /** - * Check if this is a debuggable build of Android. Use this to enable developer-only features. - */ - public static boolean isDebugAndroid() { - return "eng".equals(Build.TYPE) || "userdebug".equals(Build.TYPE); - } - - // The markers Begin:BuildCompat and End:BuildCompat delimit code - // that is autogenerated from Android sources. - // Begin:BuildCompat P - - /** - * Checks if the device is running on a pre-release version of Android P or newer. - *

- * @return {@code true} if P APIs are available for use, {@code false} otherwise - */ - public static boolean isAtLeastP() { - return VERSION.SDK_INT >= 28; - } - - /** - * Checks if the application targets at least released SDK P - */ - public static boolean targetsAtLeastP() { - return ContextUtils.getApplicationContext().getApplicationInfo().targetSdkVersion >= 28; - } - - // End:BuildCompat -} diff --git a/android/java/src/org/chromium/base/Callback.java b/android/java/src/org/chromium/base/Callback.java deleted file mode 100644 index f5f20b9c7..000000000 --- a/android/java/src/org/chromium/base/Callback.java +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import org.chromium.base.annotations.CalledByNative; - -/** - * A simple single-argument callback to handle the result of a computation. - * - * @param The type of the computation's result. - */ -public interface Callback { - /** - * Invoked with the result of a computation. - */ - void onResult(T result); - - /** - * JNI Generator does not know how to target static methods on interfaces - * (which is new in Java 8, and requires desugaring). - */ - abstract class Helper { - @SuppressWarnings("unchecked") - @CalledByNative("Helper") - static void onObjectResultFromNative(Callback callback, Object result) { - callback.onResult(result); - } - - @SuppressWarnings("unchecked") - @CalledByNative("Helper") - static void onBooleanResultFromNative(Callback callback, boolean result) { - callback.onResult(Boolean.valueOf(result)); - } - - @SuppressWarnings("unchecked") - @CalledByNative("Helper") - static void onIntResultFromNative(Callback callback, int result) { - callback.onResult(Integer.valueOf(result)); - } - } -} diff --git a/android/java/src/org/chromium/base/CollectionUtil.java b/android/java/src/org/chromium/base/CollectionUtil.java deleted file mode 100644 index 60933807b..000000000 --- a/android/java/src/org/chromium/base/CollectionUtil.java +++ /dev/null @@ -1,99 +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. - -package org.chromium.base; - -import android.support.annotation.NonNull; -import android.util.Pair; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Functions used for easier initialization of Java collections. Inspired by - * functionality in com.google.common.collect in Guava but cherry-picked to - * bare-minimum functionality to avoid bloat. (http://crbug.com/272790 provides - * further details) - */ -public final class CollectionUtil { - private CollectionUtil() {} - - @SafeVarargs - public static HashSet newHashSet(E... elements) { - HashSet set = new HashSet(elements.length); - Collections.addAll(set, elements); - return set; - } - - @SafeVarargs - public static ArrayList newArrayList(E... elements) { - ArrayList list = new ArrayList(elements.length); - Collections.addAll(list, elements); - return list; - } - - @VisibleForTesting - public static ArrayList newArrayList(Iterable iterable) { - ArrayList list = new ArrayList(); - for (E element : iterable) { - list.add(element); - } - return list; - } - - @SafeVarargs - public static HashMap newHashMap(Pair... entries) { - HashMap map = new HashMap<>(); - for (Pair entry : entries) { - map.put(entry.first, entry.second); - } - return map; - } - - public static boolean[] booleanListToBooleanArray(@NonNull List list) { - boolean[] array = new boolean[list.size()]; - for (int i = 0; i < list.size(); i++) { - array[i] = list.get(i); - } - return array; - } - - public static int[] integerListToIntArray(@NonNull List list) { - int[] array = new int[list.size()]; - for (int i = 0; i < list.size(); i++) { - array[i] = list.get(i); - } - return array; - } - - public static long[] longListToLongArray(@NonNull List list) { - long[] array = new long[list.size()]; - for (int i = 0; i < list.size(); i++) { - array[i] = list.get(i); - } - return array; - } - - // This is a utility helper method that adds functionality available in API 24 (see - // Collection.forEach). - public static void forEach(Collection collection, Callback worker) { - for (T entry : collection) worker.onResult(entry); - } - - // This is a utility helper method that adds functionality available in API 24 (see - // Collection.forEach). - @SuppressWarnings("unchecked") - public static void forEach( - Map map, Callback> worker) { - for (Map.Entry entry : map.entrySet()) { - worker.onResult((Map.Entry) entry); - } - } -} diff --git a/android/java/src/org/chromium/base/CommandLine.java b/android/java/src/org/chromium/base/CommandLine.java deleted file mode 100644 index 963b1464a..000000000 --- a/android/java/src/org/chromium/base/CommandLine.java +++ /dev/null @@ -1,389 +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. - -package org.chromium.base; - -import android.support.annotation.Nullable; -import android.text.TextUtils; -import android.util.Log; - -import org.chromium.base.annotations.MainDex; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.concurrent.atomic.AtomicReference; - -/** - * Java mirror of base/command_line.h. - * Android applications don't have command line arguments. Instead, they're "simulated" by reading a - * file at a specific location early during startup. Applications each define their own files, e.g., - * ContentShellApplication.COMMAND_LINE_FILE. -**/ -@MainDex -public abstract class CommandLine { - // Public abstract interface, implemented in derived classes. - // All these methods reflect their native-side counterparts. - /** - * Returns true if this command line contains the given switch. - * (Switch names ARE case-sensitive). - */ - @VisibleForTesting - public abstract boolean hasSwitch(String switchString); - - /** - * Return the value associated with the given switch, or null. - * @param switchString The switch key to lookup. It should NOT start with '--' ! - * @return switch value, or null if the switch is not set or set to empty. - */ - public abstract String getSwitchValue(String switchString); - - /** - * Return the value associated with the given switch, or {@code defaultValue} if the switch - * was not specified. - * @param switchString The switch key to lookup. It should NOT start with '--' ! - * @param defaultValue The default value to return if the switch isn't set. - * @return Switch value, or {@code defaultValue} if the switch is not set or set to empty. - */ - public String getSwitchValue(String switchString, String defaultValue) { - String value = getSwitchValue(switchString); - return TextUtils.isEmpty(value) ? defaultValue : value; - } - - /** - * Append a switch to the command line. There is no guarantee - * this action happens before the switch is needed. - * @param switchString the switch to add. It should NOT start with '--' ! - */ - @VisibleForTesting - public abstract void appendSwitch(String switchString); - - /** - * Append a switch and value to the command line. There is no - * guarantee this action happens before the switch is needed. - * @param switchString the switch to add. It should NOT start with '--' ! - * @param value the value for this switch. - * For example, --foo=bar becomes 'foo', 'bar'. - */ - public abstract void appendSwitchWithValue(String switchString, String value); - - /** - * Append switch/value items in "command line" format (excluding argv[0] program name). - * E.g. { '--gofast', '--username=fred' } - * @param array an array of switch or switch/value items in command line format. - * Unlike the other append routines, these switches SHOULD start with '--' . - * Unlike init(), this does not include the program name in array[0]. - */ - public abstract void appendSwitchesAndArguments(String[] array); - - /** - * Determine if the command line is bound to the native (JNI) implementation. - * @return true if the underlying implementation is delegating to the native command line. - */ - public boolean isNativeImplementation() { - return false; - } - - /** - * Returns the switches and arguments passed into the program, with switches and their - * values coming before all of the arguments. - */ - protected abstract String[] getCommandLineArguments(); - - /** - * Destroy the command line. Called when a different instance is set. - * @see #setInstance - */ - protected void destroy() {} - - private static final AtomicReference sCommandLine = - new AtomicReference(); - - /** - * @return true if the command line has already been initialized. - */ - public static boolean isInitialized() { - return sCommandLine.get() != null; - } - - // Equivalent to CommandLine::ForCurrentProcess in C++. - @VisibleForTesting - public static CommandLine getInstance() { - CommandLine commandLine = sCommandLine.get(); - assert commandLine != null; - return commandLine; - } - - /** - * Initialize the singleton instance, must be called exactly once (either directly or - * via one of the convenience wrappers below) before using the static singleton instance. - * @param args command line flags in 'argv' format: args[0] is the program name. - */ - public static void init(@Nullable String[] args) { - setInstance(new JavaCommandLine(args)); - } - - /** - * Initialize the command line from the command-line file. - * - * @param file The fully qualified command line file. - */ - public static void initFromFile(String file) { - char[] buffer = readFileAsUtf8(file); - init(buffer == null ? null : tokenizeQuotedArguments(buffer)); - } - - /** - * Resets both the java proxy and the native command lines. This allows the entire - * command line initialization to be re-run including the call to onJniLoaded. - */ - @VisibleForTesting - public static void reset() { - setInstance(null); - } - - /** - * Parse command line flags from a flat buffer, supporting double-quote enclosed strings - * containing whitespace. argv elements are derived by splitting the buffer on whitepace; - * double quote characters may enclose tokens containing whitespace; a double-quote literal - * may be escaped with back-slash. (Otherwise backslash is taken as a literal). - * @param buffer A command line in command line file format as described above. - * @return the tokenized arguments, suitable for passing to init(). - */ - @VisibleForTesting - static String[] tokenizeQuotedArguments(char[] buffer) { - // Just field trials can take up to 10K of command line. - if (buffer.length > 64 * 1024) { - // Check that our test runners are setting a reasonable number of flags. - throw new RuntimeException("Flags file too big: " + buffer.length); - } - - ArrayList args = new ArrayList(); - StringBuilder arg = null; - final char noQuote = '\0'; - final char singleQuote = '\''; - final char doubleQuote = '"'; - char currentQuote = noQuote; - for (char c : buffer) { - // Detect start or end of quote block. - if ((currentQuote == noQuote && (c == singleQuote || c == doubleQuote)) - || c == currentQuote) { - if (arg != null && arg.length() > 0 && arg.charAt(arg.length() - 1) == '\\') { - // Last char was a backslash; pop it, and treat c as a literal. - arg.setCharAt(arg.length() - 1, c); - } else { - currentQuote = currentQuote == noQuote ? c : noQuote; - } - } else if (currentQuote == noQuote && Character.isWhitespace(c)) { - if (arg != null) { - args.add(arg.toString()); - arg = null; - } - } else { - if (arg == null) arg = new StringBuilder(); - arg.append(c); - } - } - if (arg != null) { - if (currentQuote != noQuote) { - Log.w(TAG, "Unterminated quoted string: " + arg); - } - args.add(arg.toString()); - } - return args.toArray(new String[args.size()]); - } - - private static final String TAG = "CommandLine"; - private static final String SWITCH_PREFIX = "--"; - private static final String SWITCH_TERMINATOR = SWITCH_PREFIX; - private static final String SWITCH_VALUE_SEPARATOR = "="; - - public static void enableNativeProxy() { - // Make a best-effort to ensure we make a clean (atomic) switch over from the old to - // the new command line implementation. If another thread is modifying the command line - // when this happens, all bets are off. (As per the native CommandLine). - sCommandLine.set(new NativeCommandLine(getJavaSwitchesOrNull())); - } - - @Nullable - public static String[] getJavaSwitchesOrNull() { - CommandLine commandLine = sCommandLine.get(); - if (commandLine != null) { - return commandLine.getCommandLineArguments(); - } - return null; - } - - private static void setInstance(CommandLine commandLine) { - CommandLine oldCommandLine = sCommandLine.getAndSet(commandLine); - if (oldCommandLine != null) { - oldCommandLine.destroy(); - } - } - - /** - * @param fileName the file to read in. - * @return Array of chars read from the file, or null if the file cannot be read. - */ - private static char[] readFileAsUtf8(String fileName) { - File f = new File(fileName); - try (FileReader reader = new FileReader(f)) { - char[] buffer = new char[(int) f.length()]; - int charsRead = reader.read(buffer); - // charsRead < f.length() in the case of multibyte characters. - return Arrays.copyOfRange(buffer, 0, charsRead); - } catch (IOException e) { - return null; // Most likely file not found. - } - } - - private CommandLine() {} - - private static class JavaCommandLine extends CommandLine { - private HashMap mSwitches = new HashMap(); - private ArrayList mArgs = new ArrayList(); - - // The arguments begin at index 1, since index 0 contains the executable name. - private int mArgsBegin = 1; - - JavaCommandLine(@Nullable String[] args) { - if (args == null || args.length == 0 || args[0] == null) { - mArgs.add(""); - } else { - mArgs.add(args[0]); - appendSwitchesInternal(args, 1); - } - // Invariant: we always have the argv[0] program name element. - assert mArgs.size() > 0; - } - - @Override - protected String[] getCommandLineArguments() { - return mArgs.toArray(new String[mArgs.size()]); - } - - @Override - public boolean hasSwitch(String switchString) { - return mSwitches.containsKey(switchString); - } - - @Override - public String getSwitchValue(String switchString) { - // This is slightly round about, but needed for consistency with the NativeCommandLine - // version which does not distinguish empty values from key not present. - String value = mSwitches.get(switchString); - return value == null || value.isEmpty() ? null : value; - } - - @Override - public void appendSwitch(String switchString) { - appendSwitchWithValue(switchString, null); - } - - /** - * Appends a switch to the current list. - * @param switchString the switch to add. It should NOT start with '--' ! - * @param value the value for this switch. - */ - @Override - public void appendSwitchWithValue(String switchString, String value) { - mSwitches.put(switchString, value == null ? "" : value); - - // Append the switch and update the switches/arguments divider mArgsBegin. - String combinedSwitchString = SWITCH_PREFIX + switchString; - if (value != null && !value.isEmpty()) { - combinedSwitchString += SWITCH_VALUE_SEPARATOR + value; - } - - mArgs.add(mArgsBegin++, combinedSwitchString); - } - - @Override - public void appendSwitchesAndArguments(String[] array) { - appendSwitchesInternal(array, 0); - } - - // Add the specified arguments, but skipping the first |skipCount| elements. - private void appendSwitchesInternal(String[] array, int skipCount) { - boolean parseSwitches = true; - for (String arg : array) { - if (skipCount > 0) { - --skipCount; - continue; - } - - if (arg.equals(SWITCH_TERMINATOR)) { - parseSwitches = false; - } - - if (parseSwitches && arg.startsWith(SWITCH_PREFIX)) { - String[] parts = arg.split(SWITCH_VALUE_SEPARATOR, 2); - String value = parts.length > 1 ? parts[1] : null; - appendSwitchWithValue(parts[0].substring(SWITCH_PREFIX.length()), value); - } else { - mArgs.add(arg); - } - } - } - } - - private static class NativeCommandLine extends CommandLine { - public NativeCommandLine(@Nullable String[] args) { - nativeInit(args); - } - - @Override - public boolean hasSwitch(String switchString) { - return nativeHasSwitch(switchString); - } - - @Override - public String getSwitchValue(String switchString) { - return nativeGetSwitchValue(switchString); - } - - @Override - public void appendSwitch(String switchString) { - nativeAppendSwitch(switchString); - } - - @Override - public void appendSwitchWithValue(String switchString, String value) { - nativeAppendSwitchWithValue(switchString, value); - } - - @Override - public void appendSwitchesAndArguments(String[] array) { - nativeAppendSwitchesAndArguments(array); - } - - @Override - public boolean isNativeImplementation() { - return true; - } - - @Override - protected String[] getCommandLineArguments() { - assert false; - return null; - } - - @Override - protected void destroy() { - // TODO(https://crbug.com/771205): Downgrade this to an assert once we have eliminated - // tests that do this. - throw new IllegalStateException("Can't destroy native command line after startup"); - } - } - - private static native void nativeInit(String[] args); - private static native boolean nativeHasSwitch(String switchString); - private static native String nativeGetSwitchValue(String switchString); - private static native void nativeAppendSwitch(String switchString); - private static native void nativeAppendSwitchWithValue(String switchString, String value); - private static native void nativeAppendSwitchesAndArguments(String[] array); -} diff --git a/android/java/src/org/chromium/base/CommandLineInitUtil.java b/android/java/src/org/chromium/base/CommandLineInitUtil.java deleted file mode 100644 index e51b95d6b..000000000 --- a/android/java/src/org/chromium/base/CommandLineInitUtil.java +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Build; -import android.provider.Settings; -import android.support.annotation.Nullable; - -import java.io.File; - -/** - * Provides implementation of command line initialization for Android. - */ -public final class CommandLineInitUtil { - /** - * The location of the command line file needs to be in a protected - * directory so requires root access to be tweaked, i.e., no other app in a - * regular (non-rooted) device can change this file's contents. - * See below for debugging on a regular (non-rooted) device. - */ - private static final String COMMAND_LINE_FILE_PATH = "/data/local"; - - /** - * This path (writable by the shell in regular non-rooted "user" builds) is used when: - * 1) The "debug app" is set to the application calling this. - * and - * 2) ADB is enabled. - * 3) Force enabled by the embedder. - */ - private static final String COMMAND_LINE_FILE_PATH_DEBUG_APP = "/data/local/tmp"; - - private CommandLineInitUtil() { - } - - /** - * Initializes the CommandLine class, pulling command line arguments from {@code fileName}. - * @param fileName The name of the command line file to pull arguments from. - */ - public static void initCommandLine(String fileName) { - initCommandLine(fileName, null); - } - - /** - * Initializes the CommandLine class, pulling command line arguments from {@code fileName}. - * @param fileName The name of the command line file to pull arguments from. - * @param shouldUseDebugFlags If non-null, returns whether debug flags are allowed to be used. - */ - public static void initCommandLine( - String fileName, @Nullable Supplier shouldUseDebugFlags) { - assert !CommandLine.isInitialized(); - File commandLineFile = new File(COMMAND_LINE_FILE_PATH_DEBUG_APP, fileName); - // shouldUseDebugCommandLine() uses IPC, so don't bother calling it if no flags file exists. - boolean debugFlagsExist = commandLineFile.exists(); - if (!debugFlagsExist || !shouldUseDebugCommandLine(shouldUseDebugFlags)) { - commandLineFile = new File(COMMAND_LINE_FILE_PATH, fileName); - } - CommandLine.initFromFile(commandLineFile.getPath()); - } - - /** - * Use an alternative path if: - * - The current build is "eng" or "userdebug", OR - * - adb is enabled and this is the debug app, OR - * - Force enabled by the embedder. - * @param shouldUseDebugFlags If non-null, returns whether debug flags are allowed to be used. - */ - private static boolean shouldUseDebugCommandLine( - @Nullable Supplier shouldUseDebugFlags) { - if (shouldUseDebugFlags != null && shouldUseDebugFlags.get()) return true; - Context context = ContextUtils.getApplicationContext(); - String debugApp = Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 - ? getDebugAppPreJBMR1(context) - : getDebugAppJBMR1(context); - // Check isDebugAndroid() last to get full code coverage when using userdebug devices. - return context.getPackageName().equals(debugApp) || BuildInfo.isDebugAndroid(); - } - - @SuppressLint("NewApi") - private static String getDebugAppJBMR1(Context context) { - boolean adbEnabled = Settings.Global.getInt(context.getContentResolver(), - Settings.Global.ADB_ENABLED, 0) == 1; - if (adbEnabled) { - return Settings.Global.getString(context.getContentResolver(), - Settings.Global.DEBUG_APP); - } - return null; - } - - @SuppressWarnings("deprecation") - private static String getDebugAppPreJBMR1(Context context) { - boolean adbEnabled = Settings.System.getInt(context.getContentResolver(), - Settings.System.ADB_ENABLED, 0) == 1; - if (adbEnabled) { - return Settings.System.getString(context.getContentResolver(), - Settings.System.DEBUG_APP); - } - return null; - } -} diff --git a/android/java/src/org/chromium/base/ContentUriUtils.java b/android/java/src/org/chromium/base/ContentUriUtils.java deleted file mode 100644 index ba92a56c4..000000000 --- a/android/java/src/org/chromium/base/ContentUriUtils.java +++ /dev/null @@ -1,251 +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. - -package org.chromium.base; - -import android.content.ContentResolver; -import android.content.Context; -import android.content.res.AssetFileDescriptor; -import android.database.Cursor; -import android.net.Uri; -import android.os.Build; -import android.os.ParcelFileDescriptor; -import android.provider.DocumentsContract; -import android.util.Log; -import android.webkit.MimeTypeMap; - -import org.chromium.base.annotations.CalledByNative; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; - -/** - * This class provides methods to access content URI schemes. - */ -public abstract class ContentUriUtils { - private static final String TAG = "ContentUriUtils"; - private static FileProviderUtil sFileProviderUtil; - - // Guards access to sFileProviderUtil. - private static final Object sLock = new Object(); - - /** - * Provides functionality to translate a file into a content URI for use - * with a content provider. - */ - public interface FileProviderUtil { - /** - * Generate a content URI from the given file. - * - * @param file The file to be translated. - */ - Uri getContentUriFromFile(File file); - } - - // Prevent instantiation. - private ContentUriUtils() {} - - public static void setFileProviderUtil(FileProviderUtil util) { - synchronized (sLock) { - sFileProviderUtil = util; - } - } - - public static Uri getContentUriFromFile(File file) { - synchronized (sLock) { - if (sFileProviderUtil != null) { - return sFileProviderUtil.getContentUriFromFile(file); - } - } - return null; - } - - /** - * Opens the content URI for reading, and returns the file descriptor to - * the caller. The caller is responsible for closing the file descriptor. - * - * @param uriString the content URI to open - * @return file descriptor upon success, or -1 otherwise. - */ - @CalledByNative - public static int openContentUriForRead(String uriString) { - AssetFileDescriptor afd = getAssetFileDescriptor(uriString); - if (afd != null) { - return afd.getParcelFileDescriptor().detachFd(); - } - return -1; - } - - /** - * Check whether a content URI exists. - * - * @param uriString the content URI to query. - * @return true if the URI exists, or false otherwise. - */ - @CalledByNative - public static boolean contentUriExists(String uriString) { - AssetFileDescriptor asf = null; - try { - asf = getAssetFileDescriptor(uriString); - return asf != null; - } finally { - // Do not use StreamUtil.closeQuietly here, as AssetFileDescriptor - // does not implement Closeable until KitKat. - if (asf != null) { - try { - asf.close(); - } catch (IOException e) { - // Closing quietly. - } - } - } - } - - /** - * Retrieve the MIME type for the content URI. - * - * @param uriString the content URI to look up. - * @return MIME type or null if the input params are empty or invalid. - */ - @CalledByNative - public static String getMimeType(String uriString) { - ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver(); - Uri uri = Uri.parse(uriString); - if (isVirtualDocument(uri)) { - String[] streamTypes = resolver.getStreamTypes(uri, "*/*"); - return (streamTypes != null && streamTypes.length > 0) ? streamTypes[0] : null; - } - return resolver.getType(uri); - } - - /** - * Helper method to open a content URI and returns the ParcelFileDescriptor. - * - * @param uriString the content URI to open. - * @return AssetFileDescriptor of the content URI, or NULL if the file does not exist. - */ - private static AssetFileDescriptor getAssetFileDescriptor(String uriString) { - ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver(); - Uri uri = Uri.parse(uriString); - - try { - if (isVirtualDocument(uri)) { - String[] streamTypes = resolver.getStreamTypes(uri, "*/*"); - if (streamTypes != null && streamTypes.length > 0) { - AssetFileDescriptor afd = - resolver.openTypedAssetFileDescriptor(uri, streamTypes[0], null); - if (afd != null && afd.getStartOffset() != 0) { - // Do not use StreamUtil.closeQuietly here, as AssetFileDescriptor - // does not implement Closeable until KitKat. - try { - afd.close(); - } catch (IOException e) { - // Closing quietly. - } - throw new SecurityException("Cannot open files with non-zero offset type."); - } - return afd; - } - } else { - ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r"); - if (pfd != null) { - return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH); - } - } - } catch (FileNotFoundException e) { - Log.w(TAG, "Cannot find content uri: " + uriString, e); - } catch (SecurityException e) { - Log.w(TAG, "Cannot open content uri: " + uriString, e); - } catch (Exception e) { - Log.w(TAG, "Unknown content uri: " + uriString, e); - } - return null; - } - - /** - * Method to resolve the display name of a content URI. - * - * @param uri the content URI to be resolved. - * @param context {@link Context} in interest. - * @param columnField the column field to query. - * @return the display name of the @code uri if present in the database - * or an empty string otherwise. - */ - public static String getDisplayName(Uri uri, Context context, String columnField) { - if (uri == null) return ""; - ContentResolver contentResolver = context.getContentResolver(); - try (Cursor cursor = contentResolver.query(uri, null, null, null, null)) { - if (cursor != null && cursor.getCount() >= 1) { - cursor.moveToFirst(); - int displayNameIndex = cursor.getColumnIndex(columnField); - if (displayNameIndex == -1) { - return ""; - } - String displayName = cursor.getString(displayNameIndex); - // For Virtual documents, try to modify the file extension so it's compatible - // with the alternative MIME type. - if (hasVirtualFlag(cursor)) { - String[] mimeTypes = contentResolver.getStreamTypes(uri, "*/*"); - if (mimeTypes != null && mimeTypes.length > 0) { - String ext = - MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeTypes[0]); - if (ext != null) { - // Just append, it's simpler and more secure than altering an - // existing extension. - displayName += "." + ext; - } - } - } - return displayName; - } - } catch (NullPointerException e) { - // Some android models don't handle the provider call correctly. - // see crbug.com/345393 - return ""; - } - return ""; - } - - /** - * Checks whether the passed Uri represents a virtual document. - * - * @param uri the content URI to be resolved. - * @return True for virtual file, false for any other file. - */ - private static boolean isVirtualDocument(Uri uri) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return false; - if (uri == null) return false; - if (!DocumentsContract.isDocumentUri(ContextUtils.getApplicationContext(), uri)) { - return false; - } - ContentResolver contentResolver = ContextUtils.getApplicationContext().getContentResolver(); - try (Cursor cursor = contentResolver.query(uri, null, null, null, null)) { - if (cursor != null && cursor.getCount() >= 1) { - cursor.moveToFirst(); - return hasVirtualFlag(cursor); - } - } catch (NullPointerException e) { - // Some android models don't handle the provider call correctly. - // see crbug.com/345393 - return false; - } - return false; - } - - /** - * Checks whether the passed cursor for a document has a virtual document flag. - * - * The called must close the passed cursor. - * - * @param cursor Cursor with COLUMN_FLAGS. - * @return True for virtual file, false for any other file. - */ - private static boolean hasVirtualFlag(Cursor cursor) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return false; - int index = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS); - return index > -1 - && (cursor.getLong(index) & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0; - } -} diff --git a/android/java/src/org/chromium/base/ContextUtils.java b/android/java/src/org/chromium/base/ContextUtils.java deleted file mode 100644 index 8284cd127..000000000 --- a/android/java/src/org/chromium/base/ContextUtils.java +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.app.Application; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.SharedPreferences; -import android.content.res.AssetManager; -import android.os.Process; -import android.preference.PreferenceManager; - -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.MainDex; - -/** - * This class provides Android application context related utility methods. - */ -@JNINamespace("base::android") -public class ContextUtils { - private static final String TAG = "ContextUtils"; - private static Context sApplicationContext; - // TODO(agrieve): Remove sProcessName caching when we stop supporting JB. - private static String sProcessName; - - /** - * Initialization-on-demand holder. This exists for thread-safe lazy initialization. - */ - private static class Holder { - // Not final for tests. - private static SharedPreferences sSharedPreferences = fetchAppSharedPreferences(); - } - - /** - * Get the Android application context. - * - * Under normal circumstances there is only one application context in a process, so it's safe - * to treat this as a global. In WebView it's possible for more than one app using WebView to be - * running in a single process, but this mechanism is rarely used and this is not the only - * problem in that scenario, so we don't currently forbid using it as a global. - * - * Do not downcast the context returned by this method to Application (or any subclass). It may - * not be an Application object; it may be wrapped in a ContextWrapper. The only assumption you - * may make is that it is a Context whose lifetime is the same as the lifetime of the process. - */ - public static Context getApplicationContext() { - return sApplicationContext; - } - - /** - * Initializes the java application context. - * - * This should be called exactly once early on during startup, before native is loaded and - * before any other clients make use of the application context through this class. - * - * @param appContext The application context. - */ - @MainDex // TODO(agrieve): Could add to whole class if not for ApplicationStatus.initialize(). - public static void initApplicationContext(Context appContext) { - // Conceding that occasionally in tests, native is loaded before the browser process is - // started, in which case the browser process re-sets the application context. - if (sApplicationContext != null && sApplicationContext != appContext) { - throw new RuntimeException("Attempting to set multiple global application contexts."); - } - initJavaSideApplicationContext(appContext); - } - - /** - * Only called by the static holder class and tests. - * - * @return The application-wide shared preferences. - */ - private static SharedPreferences fetchAppSharedPreferences() { - return PreferenceManager.getDefaultSharedPreferences(sApplicationContext); - } - - /** - * This is used to ensure that we always use the application context to fetch the default shared - * preferences. This avoids needless I/O for android N and above. It also makes it clear that - * the app-wide shared preference is desired, rather than the potentially context-specific one. - * - * @return application-wide shared preferences. - */ - public static SharedPreferences getAppSharedPreferences() { - return Holder.sSharedPreferences; - } - - /** - * Occasionally tests cannot ensure the application context doesn't change between tests (junit) - * and sometimes specific tests has its own special needs, initApplicationContext should be used - * as much as possible, but this method can be used to override it. - * - * @param appContext The new application context. - */ - @VisibleForTesting - public static void initApplicationContextForTests(Context appContext) { - // ApplicationStatus.initialize should be called to setup activity tracking for tests - // that use Robolectric and set the application context manually. Instead of changing all - // tests that do so, the call was put here instead. - // TODO(mheikal): Require param to be of type Application - if (appContext instanceof Application) { - ApplicationStatus.initialize((Application) appContext); - } - initJavaSideApplicationContext(appContext); - Holder.sSharedPreferences = fetchAppSharedPreferences(); - } - - private static void initJavaSideApplicationContext(Context appContext) { - if (appContext == null) { - throw new RuntimeException("Global application context cannot be set to null."); - } - sApplicationContext = appContext; - } - - /** - * In most cases, {@link Context#getAssets()} can be used directly. Modified resources are - * used downstream and are set up on application startup, and this method provides access to - * regular assets before that initialization is complete. - * - * This method should ONLY be used for accessing files within the assets folder. - * - * @return Application assets. - */ - public static AssetManager getApplicationAssets() { - Context context = getApplicationContext(); - while (context instanceof ContextWrapper) { - context = ((ContextWrapper) context).getBaseContext(); - } - return context.getAssets(); - } - - /** - * @return Whether the process is isolated. - */ - public static boolean isIsolatedProcess() { - try { - return (Boolean) Process.class.getMethod("isIsolated").invoke(null); - } catch (Exception e) { // No multi-catch below API level 19 for reflection exceptions. - // If fallback logic is ever needed, refer to: - // https://chromium-review.googlesource.com/c/chromium/src/+/905563/1 - throw new RuntimeException(e); - } - } - - /** @return The name of the current process. E.g. "org.chromium.chrome:privileged_process0". */ - public static String getProcessName() { - // Once we drop support JB, this method can be simplified to not cache sProcessName and call - // ActivityThread.currentProcessName(). - if (sProcessName != null) { - return sProcessName; - } - try { - // An even more convenient ActivityThread.currentProcessName() exists, but was not added - // until JB MR2. - Class activityThreadClazz = Class.forName("android.app.ActivityThread"); - Object activityThread = - activityThreadClazz.getMethod("currentActivityThread").invoke(null); - // Before JB MR2, currentActivityThread() returns null when called on a non-UI thread. - // Cache the name to allow other threads to access it. - sProcessName = - (String) activityThreadClazz.getMethod("getProcessName").invoke(activityThread); - return sProcessName; - } catch (Exception e) { // No multi-catch below API level 19 for reflection exceptions. - // If fallback logic is ever needed, refer to: - // https://chromium-review.googlesource.com/c/chromium/src/+/905563/1 - throw new RuntimeException(e); - } - } - - public static boolean isMainProcess() { - return !getProcessName().contains(":"); - } -} diff --git a/android/java/src/org/chromium/base/CpuFeatures.java b/android/java/src/org/chromium/base/CpuFeatures.java deleted file mode 100644 index ae4969c99..000000000 --- a/android/java/src/org/chromium/base/CpuFeatures.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 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. - -package org.chromium.base; - -import org.chromium.base.annotations.JNINamespace; - -// The only purpose of this class is to allow sending CPU properties -// from the browser process to sandboxed renderer processes. This is -// needed because sandboxed processes cannot, on ARM, query the kernel -// about the CPU's properties by parsing /proc, so this operation must -// be performed in the browser process, and the result passed to -// renderer ones. -// -// For more context, see http://crbug.com/164154 -// -// Technically, this is a wrapper around the native NDK cpufeatures -// library. The exact CPU features bits are never used in Java so -// there is no point in duplicating their definitions here. -// -@JNINamespace("base::android") -public abstract class CpuFeatures { - /** - * Return the number of CPU Cores on the device. - */ - public static int getCount() { - return nativeGetCoreCount(); - } - - /** - * Return the CPU feature mask. - * This is a 64-bit integer that corresponds to the CPU's features. - * The value comes directly from android_getCpuFeatures(). - */ - public static long getMask() { - return nativeGetCpuFeatures(); - } - - private static native int nativeGetCoreCount(); - private static native long nativeGetCpuFeatures(); -} diff --git a/android/java/src/org/chromium/base/DiscardableReferencePool.java b/android/java/src/org/chromium/base/DiscardableReferencePool.java deleted file mode 100644 index 566df70a0..000000000 --- a/android/java/src/org/chromium/base/DiscardableReferencePool.java +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.support.annotation.Nullable; - -import java.util.Collections; -import java.util.Set; -import java.util.WeakHashMap; - -/** - * A DiscardableReferencePool allows handing out typed references to objects ("payloads") that can - * be dropped in one batch ("drained"), e.g. under memory pressure. In contrast to {@link - * java.lang.ref.WeakReference}s, which drop their referents when they get garbage collected, a - * reference pool gives more precise control over when exactly it is drained. - * - *

Internally it uses a {@link WeakHashMap} with the reference itself as a key to allow the - * payloads to be garbage collected regularly when the last reference goes away before the pool is - * drained. - * - *

This class and its references are not thread-safe and should not be used simultaneously by - * multiple threads. - */ -public class DiscardableReferencePool { - /** - * The underlying data storage. The wildcard type parameter allows using a single pool for - * references of any type. - */ - private final Set> mPool; - - public DiscardableReferencePool() { - WeakHashMap, Boolean> map = new WeakHashMap<>(); - mPool = Collections.newSetFromMap(map); - } - - /** - * A reference to an object in the pool. Will be nulled out when the pool is drained. - * @param The type of the object. - */ - public static class DiscardableReference { - @Nullable - private T mPayload; - - private DiscardableReference(T payload) { - assert payload != null; - mPayload = payload; - } - - /** - * @return The referent, or null if the pool has been drained. - */ - @Nullable - public T get() { - return mPayload; - } - - /** - * Clear the referent. - */ - private void discard() { - assert mPayload != null; - mPayload = null; - } - } - - /** - * @param The type of the object. - * @param payload The payload to add to the pool. - * @return A new reference to the {@code payload}. - */ - public DiscardableReference put(T payload) { - assert payload != null; - DiscardableReference reference = new DiscardableReference<>(payload); - mPool.add(reference); - return reference; - } - - /** - * Drains the pool, removing all references to objects in the pool and therefore allowing them - * to be garbage collected. - */ - public void drain() { - for (DiscardableReference ref : mPool) { - ref.discard(); - } - mPool.clear(); - } -} diff --git a/android/java/src/org/chromium/base/EarlyTraceEvent.java b/android/java/src/org/chromium/base/EarlyTraceEvent.java deleted file mode 100644 index 0f64fc232..000000000 --- a/android/java/src/org/chromium/base/EarlyTraceEvent.java +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.annotation.SuppressLint; -import android.os.Build; -import android.os.Process; -import android.os.StrictMode; -import android.os.SystemClock; - -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.MainDex; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** Support for early tracing, before the native library is loaded. - * - * This is limited, as: - * - Arguments are not supported - * - Thread time is not reported - * - Two events with the same name cannot be in progress at the same time. - * - * Events recorded here are buffered in Java until the native library is available. Then it waits - * for the completion of pending events, and sends the events to the native side. - * - * Locking: This class is threadsafe. It is enabled when general tracing is, and then disabled when - * tracing is enabled from the native side. Event completions are still processed as long - * as some are pending, then early tracing is permanently disabled after dumping the - * events. This means that if any early event is still pending when tracing is disabled, - * all early events are dropped. - */ -@JNINamespace("base::android") -@MainDex -public class EarlyTraceEvent { - // Must be kept in sync with the native kAndroidTraceConfigFile. - private static final String TRACE_CONFIG_FILENAME = "/data/local/chrome-trace-config.json"; - - /** Single trace event. */ - @VisibleForTesting - static final class Event { - final String mName; - final int mThreadId; - final long mBeginTimeNanos; - final long mBeginThreadTimeMillis; - long mEndTimeNanos; - long mEndThreadTimeMillis; - - Event(String name) { - mName = name; - mThreadId = Process.myTid(); - mBeginTimeNanos = elapsedRealtimeNanos(); - mBeginThreadTimeMillis = SystemClock.currentThreadTimeMillis(); - } - - void end() { - assert mEndTimeNanos == 0; - assert mEndThreadTimeMillis == 0; - mEndTimeNanos = elapsedRealtimeNanos(); - mEndThreadTimeMillis = SystemClock.currentThreadTimeMillis(); - } - - @VisibleForTesting - @SuppressLint("NewApi") - static long elapsedRealtimeNanos() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - return SystemClock.elapsedRealtimeNanos(); - } else { - return SystemClock.elapsedRealtime() * 1000000; - } - } - } - - @VisibleForTesting - static final class AsyncEvent { - final boolean mIsStart; - final String mName; - final long mId; - final long mTimestampNanos; - - AsyncEvent(String name, long id, boolean isStart) { - mName = name; - mId = id; - mIsStart = isStart; - mTimestampNanos = Event.elapsedRealtimeNanos(); - } - } - - // State transitions are: - // - enable(): DISABLED -> ENABLED - // - disable(): ENABLED -> FINISHING - // - Once there are no pending events: FINISHING -> FINISHED. - @VisibleForTesting static final int STATE_DISABLED = 0; - @VisibleForTesting static final int STATE_ENABLED = 1; - @VisibleForTesting static final int STATE_FINISHING = 2; - @VisibleForTesting static final int STATE_FINISHED = 3; - - // Locks the fields below. - private static final Object sLock = new Object(); - - @VisibleForTesting static volatile int sState = STATE_DISABLED; - // Not final as these object are not likely to be used at all. - @VisibleForTesting static List sCompletedEvents; - @VisibleForTesting - static Map sPendingEventByKey; - @VisibleForTesting static List sAsyncEvents; - @VisibleForTesting static List sPendingAsyncEvents; - - /** @see TraceEvent#MaybeEnableEarlyTracing(). - */ - static void maybeEnable() { - ThreadUtils.assertOnUiThread(); - boolean shouldEnable = false; - // Checking for the trace config filename touches the disk. - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - try { - if (CommandLine.getInstance().hasSwitch("trace-startup")) { - shouldEnable = true; - } else { - try { - shouldEnable = (new File(TRACE_CONFIG_FILENAME)).exists(); - } catch (SecurityException e) { - // Access denied, not enabled. - } - } - } finally { - StrictMode.setThreadPolicy(oldPolicy); - } - if (shouldEnable) enable(); - } - - @VisibleForTesting - static void enable() { - synchronized (sLock) { - if (sState != STATE_DISABLED) return; - sCompletedEvents = new ArrayList(); - sPendingEventByKey = new HashMap(); - sAsyncEvents = new ArrayList(); - sPendingAsyncEvents = new ArrayList(); - sState = STATE_ENABLED; - } - } - - /** - * Disables Early tracing. - * - * Once this is called, no new event will be registered. However, end() calls are still recorded - * as long as there are pending events. Once there are none left, pass the events to the native - * side. - */ - static void disable() { - synchronized (sLock) { - if (!enabled()) return; - sState = STATE_FINISHING; - maybeFinishLocked(); - } - } - - /** - * Returns whether early tracing is currently active. - * - * Active means that Early Tracing is either enabled or waiting to complete pending events. - */ - static boolean isActive() { - int state = sState; - return (state == STATE_ENABLED || state == STATE_FINISHING); - } - - static boolean enabled() { - return sState == STATE_ENABLED; - } - - /** @see {@link TraceEvent#begin()}. */ - public static void begin(String name) { - // begin() and end() are going to be called once per TraceEvent, this avoids entering a - // synchronized block at each and every call. - if (!enabled()) return; - Event event = new Event(name); - Event conflictingEvent; - synchronized (sLock) { - if (!enabled()) return; - conflictingEvent = sPendingEventByKey.put(makeEventKeyForCurrentThread(name), event); - } - if (conflictingEvent != null) { - throw new IllegalArgumentException( - "Multiple pending trace events can't have the same name"); - } - } - - /** @see {@link TraceEvent#end()}. */ - public static void end(String name) { - if (!isActive()) return; - synchronized (sLock) { - if (!isActive()) return; - Event event = sPendingEventByKey.remove(makeEventKeyForCurrentThread(name)); - if (event == null) return; - event.end(); - sCompletedEvents.add(event); - if (sState == STATE_FINISHING) maybeFinishLocked(); - } - } - - /** @see {@link TraceEvent#startAsync()}. */ - public static void startAsync(String name, long id) { - if (!enabled()) return; - AsyncEvent event = new AsyncEvent(name, id, true /*isStart*/); - synchronized (sLock) { - if (!enabled()) return; - sAsyncEvents.add(event); - sPendingAsyncEvents.add(name); - } - } - - /** @see {@link TraceEvent#finishAsync()}. */ - public static void finishAsync(String name, long id) { - if (!isActive()) return; - AsyncEvent event = new AsyncEvent(name, id, false /*isStart*/); - synchronized (sLock) { - if (!isActive()) return; - if (!sPendingAsyncEvents.remove(name)) return; - sAsyncEvents.add(event); - if (sState == STATE_FINISHING) maybeFinishLocked(); - } - } - - @VisibleForTesting - static void resetForTesting() { - sState = EarlyTraceEvent.STATE_DISABLED; - sCompletedEvents = null; - sPendingEventByKey = null; - sAsyncEvents = null; - sPendingAsyncEvents = null; - } - - private static void maybeFinishLocked() { - if (!sCompletedEvents.isEmpty()) { - dumpEvents(sCompletedEvents); - sCompletedEvents.clear(); - } - if (!sAsyncEvents.isEmpty()) { - dumpAsyncEvents(sAsyncEvents); - sAsyncEvents.clear(); - } - if (sPendingEventByKey.isEmpty() && sPendingAsyncEvents.isEmpty()) { - sState = STATE_FINISHED; - sPendingEventByKey = null; - sCompletedEvents = null; - sPendingAsyncEvents = null; - sAsyncEvents = null; - } - } - - private static void dumpEvents(List events) { - long offsetNanos = getOffsetNanos(); - for (Event e : events) { - nativeRecordEarlyEvent(e.mName, e.mBeginTimeNanos + offsetNanos, - e.mEndTimeNanos + offsetNanos, e.mThreadId, - e.mEndThreadTimeMillis - e.mBeginThreadTimeMillis); - } - } - private static void dumpAsyncEvents(List events) { - long offsetNanos = getOffsetNanos(); - for (AsyncEvent e : events) { - if (e.mIsStart) { - nativeRecordEarlyStartAsyncEvent(e.mName, e.mId, e.mTimestampNanos + offsetNanos); - } else { - nativeRecordEarlyFinishAsyncEvent(e.mName, e.mId, e.mTimestampNanos + offsetNanos); - } - } - } - - private static long getOffsetNanos() { - long nativeNowNanos = TimeUtils.nativeGetTimeTicksNowUs() * 1000; - long javaNowNanos = Event.elapsedRealtimeNanos(); - return nativeNowNanos - javaNowNanos; - } - - /** - * Returns a key which consists of |name| and the ID of the current thread. - * The key is used with pending events making them thread-specific, thus avoiding - * an exception when similarly named events are started from multiple threads. - */ - @VisibleForTesting - static String makeEventKeyForCurrentThread(String name) { - return name + "@" + Process.myTid(); - } - - private static native void nativeRecordEarlyEvent(String name, long beginTimNanos, - long endTimeNanos, int threadId, long threadDurationMillis); - private static native void nativeRecordEarlyStartAsyncEvent( - String name, long id, long timestamp); - private static native void nativeRecordEarlyFinishAsyncEvent( - String name, long id, long timestamp); -} diff --git a/android/java/src/org/chromium/base/EventLog.java b/android/java/src/org/chromium/base/EventLog.java deleted file mode 100644 index f889175b7..000000000 --- a/android/java/src/org/chromium/base/EventLog.java +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -/** - * A simple interface to Android's EventLog to be used by native code. - */ -@JNINamespace("base::android") -public class EventLog { - - @CalledByNative - public static void writeEvent(int tag, int value) { - android.util.EventLog.writeEvent(tag, value); - } -} diff --git a/android/java/src/org/chromium/base/FieldTrialList.java b/android/java/src/org/chromium/base/FieldTrialList.java deleted file mode 100644 index c3468a4af..000000000 --- a/android/java/src/org/chromium/base/FieldTrialList.java +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import org.chromium.base.annotations.MainDex; - -/** - * Helper to get field trial information. - */ -@MainDex -public class FieldTrialList { - - private FieldTrialList() {} - - /** - * @param trialName The name of the trial to get the group for. - * @return The group name chosen for the named trial, or the empty string if the trial does - * not exist. - */ - public static String findFullName(String trialName) { - return nativeFindFullName(trialName); - } - - /** - * @param trialName The name of the trial to get the group for. - * @return Whether the trial exists or not. - */ - public static boolean trialExists(String trialName) { - return nativeTrialExists(trialName); - } - - /** - * @param trialName The name of the trial with the parameter. - * @param parameterKey The key of the parameter. - * @return The value of the parameter or an empty string if not found. - */ - public static String getVariationParameter(String trialName, String parameterKey) { - return nativeGetVariationParameter(trialName, parameterKey); - } - - private static native String nativeFindFullName(String trialName); - private static native boolean nativeTrialExists(String trialName); - private static native String nativeGetVariationParameter(String trialName, String parameterKey); -} diff --git a/android/java/src/org/chromium/base/FileUtils.java b/android/java/src/org/chromium/base/FileUtils.java deleted file mode 100644 index e44cd928a..000000000 --- a/android/java/src/org/chromium/base/FileUtils.java +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.content.Context; -import android.net.Uri; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; -import java.util.Locale; - -/** - * Helper methods for dealing with Files. - */ -public class FileUtils { - private static final String TAG = "FileUtils"; - - /** - * Delete the given File and (if it's a directory) everything within it. - */ - public static void recursivelyDeleteFile(File currentFile) { - ThreadUtils.assertOnBackgroundThread(); - if (currentFile.isDirectory()) { - File[] files = currentFile.listFiles(); - if (files != null) { - for (File file : files) { - recursivelyDeleteFile(file); - } - } - } - - if (!currentFile.delete()) Log.e(TAG, "Failed to delete: " + currentFile); - } - - /** - * Delete the given files or directories by calling {@link #recursivelyDeleteFile(File)}. - * @param files The files to delete. - */ - public static void batchDeleteFiles(List files) { - ThreadUtils.assertOnBackgroundThread(); - - for (File file : files) { - if (file.exists()) recursivelyDeleteFile(file); - } - } - - /** - * Extracts an asset from the app's APK to a file. - * @param context - * @param assetName Name of the asset to extract. - * @param dest File to extract the asset to. - * @return true on success. - */ - public static boolean extractAsset(Context context, String assetName, File dest) { - InputStream inputStream = null; - OutputStream outputStream = null; - try { - inputStream = context.getAssets().open(assetName); - outputStream = new BufferedOutputStream(new FileOutputStream(dest)); - byte[] buffer = new byte[8192]; - int c; - while ((c = inputStream.read(buffer)) != -1) { - outputStream.write(buffer, 0, c); - } - inputStream.close(); - outputStream.close(); - return true; - } catch (IOException e) { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException ex) { - } - } - if (outputStream != null) { - try { - outputStream.close(); - } catch (IOException ex) { - } - } - } - return false; - } - - /** - * Atomically copies the data from an input stream into an output file. - * @param is Input file stream to read data from. - * @param outFile Output file path. - * @param buffer Caller-provided buffer. Provided to avoid allocating the same - * buffer on each call when copying several files in sequence. - * @throws IOException in case of I/O error. - */ - public static void copyFileStreamAtomicWithBuffer(InputStream is, File outFile, byte[] buffer) - throws IOException { - File tmpOutputFile = new File(outFile.getPath() + ".tmp"); - try (OutputStream os = new FileOutputStream(tmpOutputFile)) { - Log.i(TAG, "Writing to %s", outFile); - - int count = 0; - while ((count = is.read(buffer, 0, buffer.length)) != -1) { - os.write(buffer, 0, count); - } - } - if (!tmpOutputFile.renameTo(outFile)) { - throw new IOException(); - } - } - - /** - * Returns a URI that points at the file. - * @param file File to get a URI for. - * @return URI that points at that file, either as a content:// URI or a file:// URI. - */ - public static Uri getUriForFile(File file) { - // TODO(crbug/709584): Uncomment this when http://crbug.com/709584 has been fixed. - // assert !ThreadUtils.runningOnUiThread(); - Uri uri = null; - - try { - // Try to obtain a content:// URI, which is preferred to a file:// URI so that - // receiving apps don't attempt to determine the file's mime type (which often fails). - uri = ContentUriUtils.getContentUriFromFile(file); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Could not create content uri: " + e); - } - - if (uri == null) uri = Uri.fromFile(file); - - return uri; - } - - /** - * Returns the file extension, or an empty string if none. - * @param file Name of the file, with or without the full path. - * @return empty string if no extension, extension otherwise. - */ - public static String getExtension(String file) { - int index = file.lastIndexOf('.'); - if (index == -1) return ""; - return file.substring(index + 1).toLowerCase(Locale.US); - } -} diff --git a/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java b/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java deleted file mode 100644 index cbaf7f76a..000000000 --- a/android/java/src/org/chromium/base/ImportantFileWriterAndroid.java +++ /dev/null @@ -1,31 +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. - -package org.chromium.base; - -import org.chromium.base.annotations.JNINamespace; - -/** - * This class provides an interface to the native class for writing - * important data files without risking data loss. - */ -@JNINamespace("base::android") -public class ImportantFileWriterAndroid { - - /** - * Write a binary file atomically. - * - * This either writes all the data or leaves the file unchanged. - * - * @param fileName The complete path of the file to be written - * @param data The data to be written to the file - * @return true if the data was written to the file, false if not. - */ - public static boolean writeFileAtomically(String fileName, byte[] data) { - return nativeWriteFileAtomically(fileName, data); - } - - private static native boolean nativeWriteFileAtomically( - String fileName, byte[] data); -} diff --git a/android/java/src/org/chromium/base/JNIUtils.java b/android/java/src/org/chromium/base/JNIUtils.java deleted file mode 100644 index 3fcec9131..000000000 --- a/android/java/src/org/chromium/base/JNIUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.MainDex; - -/** - * This class provides JNI-related methods to the native library. - */ -@MainDex -public class JNIUtils { - private static Boolean sSelectiveJniRegistrationEnabled; - - /** - * This returns a ClassLoader that is capable of loading Chromium Java code. Such a ClassLoader - * is needed for the few cases where the JNI mechanism is unable to automatically determine the - * appropriate ClassLoader instance. - */ - @CalledByNative - public static Object getClassLoader() { - return JNIUtils.class.getClassLoader(); - } - - /** - * @return whether or not the current process supports selective JNI registration. - */ - @CalledByNative - public static boolean isSelectiveJniRegistrationEnabled() { - if (sSelectiveJniRegistrationEnabled == null) { - sSelectiveJniRegistrationEnabled = false; - } - return sSelectiveJniRegistrationEnabled; - } - - /** - * Allow this process to selectively perform JNI registration. This must be called before - * loading native libraries or it will have no effect. - */ - public static void enableSelectiveJniRegistration() { - assert sSelectiveJniRegistrationEnabled == null; - sSelectiveJniRegistrationEnabled = true; - } -} diff --git a/android/java/src/org/chromium/base/JavaExceptionReporter.java b/android/java/src/org/chromium/base/JavaExceptionReporter.java deleted file mode 100644 index f192f78c1..000000000 --- a/android/java/src/org/chromium/base/JavaExceptionReporter.java +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.support.annotation.UiThread; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.MainDex; - -/** - * This UncaughtExceptionHandler will create a breakpad minidump when there is an uncaught - * exception. - * - * The exception's stack trace will be added to the minidump's data. This allows java-only crashes - * to be reported in the same way as other native crashes. - */ -@JNINamespace("base::android") -@MainDex -public class JavaExceptionReporter implements Thread.UncaughtExceptionHandler { - private final Thread.UncaughtExceptionHandler mParent; - private final boolean mCrashAfterReport; - private boolean mHandlingException; - - private JavaExceptionReporter( - Thread.UncaughtExceptionHandler parent, boolean crashAfterReport) { - mParent = parent; - mCrashAfterReport = crashAfterReport; - } - - @Override - public void uncaughtException(Thread t, Throwable e) { - if (!mHandlingException) { - mHandlingException = true; - nativeReportJavaException(mCrashAfterReport, e); - } - if (mParent != null) { - mParent.uncaughtException(t, e); - } - } - - /** - * Report and upload the stack trace as if it was a crash. This is very expensive and should - * be called rarely and only on the UI thread to avoid corrupting other crash uploads. Ideally - * only called in idle handlers. - * - * @param stackTrace The stack trace to report. - */ - @UiThread - public static void reportStackTrace(String stackTrace) { - assert ThreadUtils.runningOnUiThread(); - nativeReportJavaStackTrace(stackTrace); - } - - @CalledByNative - private static void installHandler(boolean crashAfterReport) { - Thread.setDefaultUncaughtExceptionHandler(new JavaExceptionReporter( - Thread.getDefaultUncaughtExceptionHandler(), crashAfterReport)); - } - - private static native void nativeReportJavaException(boolean crashAfterReport, Throwable e); - private static native void nativeReportJavaStackTrace(String stackTrace); -} diff --git a/android/java/src/org/chromium/base/JavaHandlerThread.java b/android/java/src/org/chromium/base/JavaHandlerThread.java deleted file mode 100644 index 9a1c92439..000000000 --- a/android/java/src/org/chromium/base/JavaHandlerThread.java +++ /dev/null @@ -1,119 +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. - -package org.chromium.base; - -import android.os.Build; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.MainDex; - -import java.lang.Thread.UncaughtExceptionHandler; - -/** - * Thread in Java with an Android Handler. This class is not thread safe. - */ -@JNINamespace("base::android") -@MainDex -public class JavaHandlerThread { - private final HandlerThread mThread; - - private Throwable mUnhandledException; - - /** - * Construct a java-only instance. Can be connected with native side later. - * Useful for cases where a java thread is needed before native library is loaded. - */ - public JavaHandlerThread(String name, int priority) { - mThread = new HandlerThread(name, priority); - } - - @CalledByNative - private static JavaHandlerThread create(String name, int priority) { - return new JavaHandlerThread(name, priority); - } - - public Looper getLooper() { - assert hasStarted(); - return mThread.getLooper(); - } - - public void maybeStart() { - if (hasStarted()) return; - mThread.start(); - } - - @CalledByNative - private void startAndInitialize(final long nativeThread, final long nativeEvent) { - maybeStart(); - new Handler(mThread.getLooper()).post(new Runnable() { - @Override - public void run() { - nativeInitializeThread(nativeThread, nativeEvent); - } - }); - } - - @CalledByNative - private void quitThreadSafely(final long nativeThread) { - // Allow pending java tasks to run, but don't run any delayed or newly queued up tasks. - new Handler(mThread.getLooper()).post(new Runnable() { - @Override - public void run() { - mThread.quit(); - nativeOnLooperStopped(nativeThread); - } - }); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - // When we can, signal that new tasks queued up won't be run. - mThread.getLooper().quitSafely(); - } - } - - @CalledByNative - private void joinThread() { - boolean joined = false; - while (!joined) { - try { - mThread.join(); - joined = true; - } catch (InterruptedException e) { - } - } - } - - private boolean hasStarted() { - return mThread.getState() != Thread.State.NEW; - } - - @CalledByNative - private boolean isAlive() { - return mThread.isAlive(); - } - - // This should *only* be used for tests. In production we always need to call the original - // uncaught exception handler (the framework's) after any uncaught exception handling we do, as - // it generates crash dumps and kills the process. - @CalledByNative - private void listenForUncaughtExceptionsForTesting() { - mThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { - @Override - public void uncaughtException(Thread t, Throwable e) { - mUnhandledException = e; - } - }); - } - - @CalledByNative - private Throwable getUncaughtExceptionIfAny() { - return mUnhandledException; - } - - private native void nativeInitializeThread(long nativeJavaHandlerThread, long nativeEvent); - private native void nativeOnLooperStopped(long nativeJavaHandlerThread); -} diff --git a/android/java/src/org/chromium/base/LocaleUtils.java b/android/java/src/org/chromium/base/LocaleUtils.java deleted file mode 100644 index 05d39029a..000000000 --- a/android/java/src/org/chromium/base/LocaleUtils.java +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.annotation.TargetApi; -import android.os.Build; -import android.os.LocaleList; -import android.text.TextUtils; - -import org.chromium.base.annotations.CalledByNative; - -import java.util.ArrayList; -import java.util.Locale; - -/** - * This class provides the locale related methods. - */ -public class LocaleUtils { - /** - * Guards this class from being instantiated. - */ - private LocaleUtils() { - } - - /** - * Java keeps deprecated language codes for Hebrew, Yiddish and Indonesian but Chromium uses - * updated ones. Similarly, Android uses "tl" while Chromium uses "fil" for Tagalog/Filipino. - * So apply a mapping here. - * See http://developer.android.com/reference/java/util/Locale.html - * @return a updated language code for Chromium with given language string. - */ - public static String getUpdatedLanguageForChromium(String language) { - // IMPORTANT: Keep in sync with the mapping found in: - // build/android/gyp/util/resource_utils.py - switch (language) { - case "iw": - return "he"; // Hebrew - case "ji": - return "yi"; // Yiddish - case "in": - return "id"; // Indonesian - case "tl": - return "fil"; // Filipino - default: - return language; - } - } - - /** - * @return a locale with updated language codes for Chromium, with translated modern language - * codes used by Chromium. - */ - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - @VisibleForTesting - public static Locale getUpdatedLocaleForChromium(Locale locale) { - String language = locale.getLanguage(); - String languageForChrome = getUpdatedLanguageForChromium(language); - if (languageForChrome.equals(language)) { - return locale; - } - return new Locale.Builder().setLocale(locale).setLanguage(languageForChrome).build(); - } - - /** - * Android uses "tl" while Chromium uses "fil" for Tagalog/Filipino. - * So apply a mapping here. - * See http://developer.android.com/reference/java/util/Locale.html - * @return a updated language code for Android with given language string. - */ - public static String getUpdatedLanguageForAndroid(String language) { - // IMPORTANT: Keep in sync with the mapping found in: - // build/android/gyp/util/resource_utils.py - switch (language) { - case "und": - return ""; // Undefined - case "fil": - return "tl"; // Filipino - default: - return language; - } - } - - /** - * @return a locale with updated language codes for Android, from translated modern language - * codes used by Chromium. - */ - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - @VisibleForTesting - public static Locale getUpdatedLocaleForAndroid(Locale locale) { - String language = locale.getLanguage(); - String languageForAndroid = getUpdatedLanguageForAndroid(language); - if (languageForAndroid.equals(language)) { - return locale; - } - return new Locale.Builder().setLocale(locale).setLanguage(languageForAndroid).build(); - } - - /** - * This function creates a Locale object from xx-XX style string where xx is language code - * and XX is a country code. This works for API level lower than 21. - * @return the locale that best represents the language tag. - */ - public static Locale forLanguageTagCompat(String languageTag) { - String[] tag = languageTag.split("-"); - if (tag.length == 0) { - return new Locale(""); - } - String language = getUpdatedLanguageForAndroid(tag[0]); - if ((language.length() != 2 && language.length() != 3)) { - return new Locale(""); - } - if (tag.length == 1) { - return new Locale(language); - } - String country = tag[1]; - if (country.length() != 2 && country.length() != 3) { - return new Locale(language); - } - return new Locale(language, country); - } - - /** - * This function creates a Locale object from xx-XX style string where xx is language code - * and XX is a country code. - * @return the locale that best represents the language tag. - */ - public static Locale forLanguageTag(String languageTag) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - Locale locale = Locale.forLanguageTag(languageTag); - return getUpdatedLocaleForAndroid(locale); - } - return forLanguageTagCompat(languageTag); - } - - /** - * Converts Locale object to the BCP 47 compliant string format. - * This works for API level lower than 24. - * - * Note that for Android M or before, we cannot use Locale.getLanguage() and - * Locale.toLanguageTag() for this purpose. Since Locale.getLanguage() returns deprecated - * language code even if the Locale object is constructed with updated language code. As for - * Locale.toLanguageTag(), it does a special conversion from deprecated language code to updated - * one, but it is only usable for Android N or after. - * @return a well-formed IETF BCP 47 language tag with language and country code that - * represents this locale. - */ - public static String toLanguageTag(Locale locale) { - String language = getUpdatedLanguageForChromium(locale.getLanguage()); - String country = locale.getCountry(); - if (language.equals("no") && country.equals("NO") && locale.getVariant().equals("NY")) { - return "nn-NO"; - } - return country.isEmpty() ? language : language + "-" + country; - } - - /** - * Converts LocaleList object to the comma separated BCP 47 compliant string format. - * - * @return a well-formed IETF BCP 47 language tag with language and country code that - * represents this locale list. - */ - @TargetApi(Build.VERSION_CODES.N) - public static String toLanguageTags(LocaleList localeList) { - ArrayList newLocaleList = new ArrayList<>(); - for (int i = 0; i < localeList.size(); i++) { - Locale locale = getUpdatedLocaleForChromium(localeList.get(i)); - newLocaleList.add(toLanguageTag(locale)); - } - return TextUtils.join(",", newLocaleList); - } - - /** - * @return a comma separated language tags string that represents a default locale. - * Each language tag is well-formed IETF BCP 47 language tag with language and country - * code. - */ - @CalledByNative - public static String getDefaultLocaleString() { - return toLanguageTag(Locale.getDefault()); - } - - /** - * @return a comma separated language tags string that represents a default locale or locales. - * Each language tag is well-formed IETF BCP 47 language tag with language and country - * code. - */ - public static String getDefaultLocaleListString() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - return toLanguageTags(LocaleList.getDefault()); - } - return getDefaultLocaleString(); - } - - /** - * @return The default country code set during install. - */ - @CalledByNative - private static String getDefaultCountryCode() { - CommandLine commandLine = CommandLine.getInstance(); - return commandLine.hasSwitch(BaseSwitches.DEFAULT_COUNTRY_CODE_AT_INSTALL) - ? commandLine.getSwitchValue(BaseSwitches.DEFAULT_COUNTRY_CODE_AT_INSTALL) - : Locale.getDefault().getCountry(); - } - -} diff --git a/android/java/src/org/chromium/base/Log.java b/android/java/src/org/chromium/base/Log.java deleted file mode 100644 index 399f16dfc..000000000 --- a/android/java/src/org/chromium/base/Log.java +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import org.chromium.base.annotations.RemovableInRelease; - -import java.util.Locale; - -/** - * Utility class for Logging. - * - *

- * Defines logging access points for each feature. They format and forward the logs to - * {@link android.util.Log}, allowing to standardize the output, to make it easy to identify - * the origin of logs, and enable or disable logging in different parts of the code. - *

- *

- * Usage documentation: {@code //docs/android_logging.md}. - *

- */ -public class Log { - /** Convenience property, same as {@link android.util.Log#ASSERT}. */ - public static final int ASSERT = android.util.Log.ASSERT; - - /** Convenience property, same as {@link android.util.Log#DEBUG}. */ - public static final int DEBUG = android.util.Log.DEBUG; - - /** Convenience property, same as {@link android.util.Log#ERROR}. */ - public static final int ERROR = android.util.Log.ERROR; - - /** Convenience property, same as {@link android.util.Log#INFO}. */ - public static final int INFO = android.util.Log.INFO; - - /** Convenience property, same as {@link android.util.Log#VERBOSE}. */ - public static final int VERBOSE = android.util.Log.VERBOSE; - - /** Convenience property, same as {@link android.util.Log#WARN}. */ - public static final int WARN = android.util.Log.WARN; - - private static final String sTagPrefix = "cr_"; - private static final String sDeprecatedTagPrefix = "cr."; - - private Log() { - // Static only access - } - - /** Returns a formatted log message, using the supplied format and arguments.*/ - private static String formatLog(String messageTemplate, Object... params) { - if (params != null && params.length != 0) { - messageTemplate = String.format(Locale.US, messageTemplate, params); - } - - return messageTemplate; - } - - /** - * Returns a normalized tag that will be in the form: "cr_foo". This function is called by the - * various Log overrides. If using {@link #isLoggable(String, int)}, you might want to call it - * to get the tag that will actually be used. - * @see #sTagPrefix - */ - public static String normalizeTag(String tag) { - if (tag.startsWith(sTagPrefix)) return tag; - - // TODO(dgn) simplify this once 'cr.' is out of the repo (http://crbug.com/533072) - int unprefixedTagStart = 0; - if (tag.startsWith(sDeprecatedTagPrefix)) { - unprefixedTagStart = sDeprecatedTagPrefix.length(); - } - - return sTagPrefix + tag.substring(unprefixedTagStart, tag.length()); - } - - /** - * Returns a formatted log message, using the supplied format and arguments. - * The message will be prepended with the filename and line number of the call. - */ - private static String formatLogWithStack(String messageTemplate, Object... params) { - return "[" + getCallOrigin() + "] " + formatLog(messageTemplate, params); - } - - /** - * Convenience function, forwards to {@link android.util.Log#isLoggable(String, int)}. - * - * Note: Has no effect on whether logs are sent or not. Use a method with - * {@link RemovableInRelease} to log something in Debug builds only. - */ - public static boolean isLoggable(String tag, int level) { - return android.util.Log.isLoggable(tag, level); - } - - /** - * Sends a {@link android.util.Log#VERBOSE} log message. - * - * For optimization purposes, only the fixed parameters versions are visible. If you need more - * than 7 parameters, consider building your log message using a function annotated with - * {@link RemovableInRelease}. - * - * @param tag Used to identify the source of a log message. Might be modified in the output - * (see {@link #normalizeTag(String)}) - * @param messageTemplate The message you would like logged. It is to be specified as a format - * string. - * @param args Arguments referenced by the format specifiers in the format string. If the last - * one is a {@link Throwable}, its trace will be printed. - */ - private static void verbose(String tag, String messageTemplate, Object... args) { - String message = formatLogWithStack(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.v(normalizeTag(tag), message, tr); - } else { - android.util.Log.v(normalizeTag(tag), message); - } - } - - /** Sends a {@link android.util.Log#VERBOSE} log message. 0 args version. */ - @RemovableInRelease - @VisibleForTesting - public static void v(String tag, String message) { - verbose(tag, message); - } - - /** Sends a {@link android.util.Log#VERBOSE} log message. 1 arg version. */ - @RemovableInRelease - @VisibleForTesting - public static void v(String tag, String messageTemplate, Object arg1) { - verbose(tag, messageTemplate, arg1); - } - - /** Sends a {@link android.util.Log#VERBOSE} log message. 2 args version */ - @RemovableInRelease - @VisibleForTesting - public static void v(String tag, String messageTemplate, Object arg1, Object arg2) { - verbose(tag, messageTemplate, arg1, arg2); - } - - /** Sends a {@link android.util.Log#VERBOSE} log message. 3 args version */ - @RemovableInRelease - @VisibleForTesting - public static void v( - String tag, String messageTemplate, Object arg1, Object arg2, Object arg3) { - verbose(tag, messageTemplate, arg1, arg2, arg3); - } - - /** Sends a {@link android.util.Log#VERBOSE} log message. 4 args version */ - @RemovableInRelease - @VisibleForTesting - public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3, - Object arg4) { - verbose(tag, messageTemplate, arg1, arg2, arg3, arg4); - } - - /** Sends a {@link android.util.Log#VERBOSE} log message. 5 args version */ - @RemovableInRelease - @VisibleForTesting - public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3, - Object arg4, Object arg5) { - verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5); - } - - /** Sends a {@link android.util.Log#VERBOSE} log message. 6 args version */ - @RemovableInRelease - @VisibleForTesting - public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3, - Object arg4, Object arg5, Object arg6) { - verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6); - } - - /** Sends a {@link android.util.Log#VERBOSE} log message. 7 args version */ - @RemovableInRelease - @VisibleForTesting - public static void v(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3, - Object arg4, Object arg5, Object arg6, Object arg7) { - verbose(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - } - - /** - * Sends a {@link android.util.Log#DEBUG} log message. - * - * For optimization purposes, only the fixed parameters versions are visible. If you need more - * than 7 parameters, consider building your log message using a function annotated with - * {@link RemovableInRelease}. - * - * @param tag Used to identify the source of a log message. Might be modified in the output - * (see {@link #normalizeTag(String)}) - * @param messageTemplate The message you would like logged. It is to be specified as a format - * string. - * @param args Arguments referenced by the format specifiers in the format string. If the last - * one is a {@link Throwable}, its trace will be printed. - */ - private static void debug(String tag, String messageTemplate, Object... args) { - String message = formatLogWithStack(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.d(normalizeTag(tag), message, tr); - } else { - android.util.Log.d(normalizeTag(tag), message); - } - } - - /** Sends a {@link android.util.Log#DEBUG} log message. 0 args version. */ - @RemovableInRelease - @VisibleForTesting - public static void d(String tag, String message) { - debug(tag, message); - } - - /** Sends a {@link android.util.Log#DEBUG} log message. 1 arg version. */ - @RemovableInRelease - @VisibleForTesting - public static void d(String tag, String messageTemplate, Object arg1) { - debug(tag, messageTemplate, arg1); - } - /** Sends a {@link android.util.Log#DEBUG} log message. 2 args version */ - @RemovableInRelease - @VisibleForTesting - public static void d(String tag, String messageTemplate, Object arg1, Object arg2) { - debug(tag, messageTemplate, arg1, arg2); - } - /** Sends a {@link android.util.Log#DEBUG} log message. 3 args version */ - @RemovableInRelease - @VisibleForTesting - public static void d( - String tag, String messageTemplate, Object arg1, Object arg2, Object arg3) { - debug(tag, messageTemplate, arg1, arg2, arg3); - } - - /** Sends a {@link android.util.Log#DEBUG} log message. 4 args version */ - @RemovableInRelease - @VisibleForTesting - public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3, - Object arg4) { - debug(tag, messageTemplate, arg1, arg2, arg3, arg4); - } - - /** Sends a {@link android.util.Log#DEBUG} log message. 5 args version */ - @RemovableInRelease - @VisibleForTesting - public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3, - Object arg4, Object arg5) { - debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5); - } - - /** Sends a {@link android.util.Log#DEBUG} log message. 6 args version */ - @RemovableInRelease - @VisibleForTesting - public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3, - Object arg4, Object arg5, Object arg6) { - debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6); - } - - /** Sends a {@link android.util.Log#DEBUG} log message. 7 args version */ - @RemovableInRelease - @VisibleForTesting - public static void d(String tag, String messageTemplate, Object arg1, Object arg2, Object arg3, - Object arg4, Object arg5, Object arg6, Object arg7) { - debug(tag, messageTemplate, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - } - - /** - * Sends an {@link android.util.Log#INFO} log message. - * - * @param tag Used to identify the source of a log message. Might be modified in the output - * (see {@link #normalizeTag(String)}) - * @param messageTemplate The message you would like logged. It is to be specified as a format - * string. - * @param args Arguments referenced by the format specifiers in the format string. If the last - * one is a {@link Throwable}, its trace will be printed. - */ - @VisibleForTesting - public static void i(String tag, String messageTemplate, Object... args) { - String message = formatLog(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.i(normalizeTag(tag), message, tr); - } else { - android.util.Log.i(normalizeTag(tag), message); - } - } - - /** - * Sends a {@link android.util.Log#WARN} log message. - * - * @param tag Used to identify the source of a log message. Might be modified in the output - * (see {@link #normalizeTag(String)}) - * @param messageTemplate The message you would like logged. It is to be specified as a format - * string. - * @param args Arguments referenced by the format specifiers in the format string. If the last - * one is a {@link Throwable}, its trace will be printed. - */ - @VisibleForTesting - public static void w(String tag, String messageTemplate, Object... args) { - String message = formatLog(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.w(normalizeTag(tag), message, tr); - } else { - android.util.Log.w(normalizeTag(tag), message); - } - } - - /** - * Sends an {@link android.util.Log#ERROR} log message. - * - * @param tag Used to identify the source of a log message. Might be modified in the output - * (see {@link #normalizeTag(String)}) - * @param messageTemplate The message you would like logged. It is to be specified as a format - * string. - * @param args Arguments referenced by the format specifiers in the format string. If the last - * one is a {@link Throwable}, its trace will be printed. - */ - @VisibleForTesting - public static void e(String tag, String messageTemplate, Object... args) { - String message = formatLog(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.e(normalizeTag(tag), message, tr); - } else { - android.util.Log.e(normalizeTag(tag), message); - } - } - - /** - * What a Terrible Failure: Used for conditions that should never happen, and logged at - * the {@link android.util.Log#ASSERT} level. Depending on the configuration, it might - * terminate the process. - * - * @see android.util.Log#wtf(String, String, Throwable) - * - * @param tag Used to identify the source of a log message. Might be modified in the output - * (see {@link #normalizeTag(String)}) - * @param messageTemplate The message you would like logged. It is to be specified as a format - * string. - * @param args Arguments referenced by the format specifiers in the format string. If the last - * one is a {@link Throwable}, its trace will be printed. - */ - @VisibleForTesting - public static void wtf(String tag, String messageTemplate, Object... args) { - String message = formatLog(messageTemplate, args); - Throwable tr = getThrowableToLog(args); - if (tr != null) { - android.util.Log.wtf(normalizeTag(tag), message, tr); - } else { - android.util.Log.wtf(normalizeTag(tag), message); - } - } - - /** Handy function to get a loggable stack trace from a Throwable. */ - public static String getStackTraceString(Throwable tr) { - return android.util.Log.getStackTraceString(tr); - } - - private static Throwable getThrowableToLog(Object[] args) { - if (args == null || args.length == 0) return null; - - Object lastArg = args[args.length - 1]; - - if (!(lastArg instanceof Throwable)) return null; - return (Throwable) lastArg; - } - - /** Returns a string form of the origin of the log call, to be used as secondary tag.*/ - private static String getCallOrigin() { - StackTraceElement[] st = Thread.currentThread().getStackTrace(); - - // The call stack should look like: - // n [a variable number of calls depending on the vm used] - // +0 getCallOrigin() - // +1 privateLogFunction: verbose or debug - // +2 formatLogWithStack() - // +3 logFunction: v or d - // +4 caller - - int callerStackIndex; - String logClassName = Log.class.getName(); - for (callerStackIndex = 0; callerStackIndex < st.length; callerStackIndex++) { - if (st[callerStackIndex].getClassName().equals(logClassName)) { - callerStackIndex += 4; - break; - } - } - - return st[callerStackIndex].getFileName() + ":" + st[callerStackIndex].getLineNumber(); - } -} diff --git a/android/java/src/org/chromium/base/MemoryPressureListener.java b/android/java/src/org/chromium/base/MemoryPressureListener.java deleted file mode 100644 index 6c80970f4..000000000 --- a/android/java/src/org/chromium/base/MemoryPressureListener.java +++ /dev/null @@ -1,130 +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. - -package org.chromium.base; - -import android.app.Activity; -import android.content.ComponentCallbacks2; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.MainDex; -import org.chromium.base.memory.MemoryPressureCallback; - -/** - * This class is Java equivalent of base::MemoryPressureListener: it distributes pressure - * signals to callbacks. - * - * The class also serves as an entry point to the native side - once native code is ready, - * it adds native callback. - * - * notifyMemoryPressure() is called exclusively by MemoryPressureMonitor, which - * monitors and throttles pressure signals. - * - * NOTE: this class should only be used on UiThread as defined by ThreadUtils (which is - * Android main thread for Chrome, but can be some other thread for WebView). - */ -@MainDex -public class MemoryPressureListener { - /** - * Sending an intent with this action to Chrome will cause it to issue a call to onLowMemory - * thus simulating a low memory situations. - */ - private static final String ACTION_LOW_MEMORY = "org.chromium.base.ACTION_LOW_MEMORY"; - - /** - * Sending an intent with this action to Chrome will cause it to issue a call to onTrimMemory - * thus simulating a low memory situations. - */ - private static final String ACTION_TRIM_MEMORY = "org.chromium.base.ACTION_TRIM_MEMORY"; - - /** - * Sending an intent with this action to Chrome will cause it to issue a call to onTrimMemory - * with notification level TRIM_MEMORY_RUNNING_CRITICAL thus simulating a low memory situation - */ - private static final String ACTION_TRIM_MEMORY_RUNNING_CRITICAL = - "org.chromium.base.ACTION_TRIM_MEMORY_RUNNING_CRITICAL"; - - /** - * Sending an intent with this action to Chrome will cause it to issue a call to onTrimMemory - * with notification level TRIM_MEMORY_MODERATE thus simulating a low memory situation - */ - private static final String ACTION_TRIM_MEMORY_MODERATE = - "org.chromium.base.ACTION_TRIM_MEMORY_MODERATE"; - - private static final ObserverList sCallbacks = new ObserverList<>(); - - /** - * Called by the native side to add native callback. - */ - @CalledByNative - private static void addNativeCallback() { - addCallback(MemoryPressureListener::nativeOnMemoryPressure); - } - - /** - * Adds a memory pressure callback. - * Callback is only added once, regardless of the number of addCallback() calls. - * This method should be called only on ThreadUtils.UiThread. - */ - public static void addCallback(MemoryPressureCallback callback) { - sCallbacks.addObserver(callback); - } - - /** - * Removes previously added memory pressure callback. - * This method should be called only on ThreadUtils.UiThread. - */ - public static void removeCallback(MemoryPressureCallback callback) { - sCallbacks.removeObserver(callback); - } - - /** - * Distributes |pressure| to all callbacks. - * This method should be called only on ThreadUtils.UiThread. - */ - public static void notifyMemoryPressure(@MemoryPressureLevel int pressure) { - for (MemoryPressureCallback callback : sCallbacks) { - callback.onPressure(pressure); - } - } - - /** - * Used by applications to simulate a memory pressure signal. By throwing certain intent - * actions. - */ - public static boolean handleDebugIntent(Activity activity, String action) { - if (ACTION_LOW_MEMORY.equals(action)) { - simulateLowMemoryPressureSignal(activity); - } else if (ACTION_TRIM_MEMORY.equals(action)) { - simulateTrimMemoryPressureSignal(activity, ComponentCallbacks2.TRIM_MEMORY_COMPLETE); - } else if (ACTION_TRIM_MEMORY_RUNNING_CRITICAL.equals(action)) { - simulateTrimMemoryPressureSignal(activity, - ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL); - } else if (ACTION_TRIM_MEMORY_MODERATE.equals(action)) { - simulateTrimMemoryPressureSignal(activity, ComponentCallbacks2.TRIM_MEMORY_MODERATE); - } else { - return false; - } - - return true; - } - - private static void simulateLowMemoryPressureSignal(Activity activity) { - // The Application and the Activity each have a list of callbacks they notify when this - // method is called. Notifying these will simulate the event at the App/Activity level - // as well as trigger the listener bound from native in this process. - activity.getApplication().onLowMemory(); - activity.onLowMemory(); - } - - private static void simulateTrimMemoryPressureSignal(Activity activity, int level) { - // The Application and the Activity each have a list of callbacks they notify when this - // method is called. Notifying these will simulate the event at the App/Activity level - // as well as trigger the listener bound from native in this process. - activity.getApplication().onTrimMemory(level); - activity.onTrimMemory(level); - } - - private static native void nativeOnMemoryPressure(@MemoryPressureLevel int pressure); -} diff --git a/android/java/src/org/chromium/base/NonThreadSafe.java b/android/java/src/org/chromium/base/NonThreadSafe.java deleted file mode 100644 index 53f38d2c8..000000000 --- a/android/java/src/org/chromium/base/NonThreadSafe.java +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -/** - * NonThreadSafe is a helper class used to help verify that methods of a - * class are called from the same thread. - */ -public class NonThreadSafe { - private Long mThreadId; - - public NonThreadSafe() { - ensureThreadIdAssigned(); - } - - /** - * Changes the thread that is checked for in CalledOnValidThread. This may - * be useful when an object may be created on one thread and then used - * exclusively on another thread. - */ - @VisibleForTesting - public synchronized void detachFromThread() { - mThreadId = null; - } - - /** - * Checks if the method is called on the valid thread. - * Assigns the current thread if no thread was assigned. - */ - @SuppressWarnings("NoSynchronizedMethodCheck") - public synchronized boolean calledOnValidThread() { - ensureThreadIdAssigned(); - return mThreadId.equals(Thread.currentThread().getId()); - } - - private void ensureThreadIdAssigned() { - if (mThreadId == null) mThreadId = Thread.currentThread().getId(); - } -} diff --git a/android/java/src/org/chromium/base/ObserverList.java b/android/java/src/org/chromium/base/ObserverList.java deleted file mode 100644 index 59276c6ea..000000000 --- a/android/java/src/org/chromium/base/ObserverList.java +++ /dev/null @@ -1,249 +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. - -package org.chromium.base; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; - -import javax.annotation.concurrent.NotThreadSafe; - -/** - * A container for a list of observers. - *

- * This container can be modified during iteration without invalidating the iterator. - * So, it safely handles the case of an observer removing itself or other observers from the list - * while observers are being notified. - *

- * The implementation (and the interface) is heavily influenced by the C++ ObserverList. - * Notable differences: - * - The iterator implements NOTIFY_EXISTING_ONLY. - * - The range-based for loop is left to the clients to implement in terms of iterator(). - *

- * This class is not threadsafe. Observers MUST be added, removed and will be notified on the same - * thread this is created. - * - * @param The type of observers that this list should hold. - */ -@NotThreadSafe -public class ObserverList implements Iterable { - /** - * Extended iterator interface that provides rewind functionality. - */ - public interface RewindableIterator extends Iterator { - /** - * Rewind the iterator back to the beginning. - * - * If we need to iterate multiple times, we can avoid iterator object reallocation by using - * this method. - */ - public void rewind(); - } - - public final List mObservers = new ArrayList(); - private int mIterationDepth; - private int mCount; - private boolean mNeedsCompact; - - public ObserverList() {} - - /** - * Add an observer to the list. - *

- * An observer should not be added to the same list more than once. If an iteration is already - * in progress, this observer will be not be visible during that iteration. - * - * @return true if the observer list changed as a result of the call. - */ - public boolean addObserver(E obs) { - // Avoid adding null elements to the list as they may be removed on a compaction. - if (obs == null || mObservers.contains(obs)) { - return false; - } - - // Structurally modifying the underlying list here. This means we - // cannot use the underlying list's iterator to iterate over the list. - boolean result = mObservers.add(obs); - assert result; - - ++mCount; - return true; - } - - /** - * Remove an observer from the list if it is in the list. - * - * @return true if an element was removed as a result of this call. - */ - public boolean removeObserver(E obs) { - if (obs == null) { - return false; - } - - int index = mObservers.indexOf(obs); - if (index == -1) { - return false; - } - - if (mIterationDepth == 0) { - // No one is iterating over the list. - mObservers.remove(index); - } else { - mNeedsCompact = true; - mObservers.set(index, null); - } - --mCount; - assert mCount >= 0; - - return true; - } - - public boolean hasObserver(E obs) { - return mObservers.contains(obs); - } - - public void clear() { - mCount = 0; - - if (mIterationDepth == 0) { - mObservers.clear(); - return; - } - - int size = mObservers.size(); - mNeedsCompact |= size != 0; - for (int i = 0; i < size; i++) { - mObservers.set(i, null); - } - } - - @Override - public Iterator iterator() { - return new ObserverListIterator(); - } - - /** - * It's the same as {@link ObserverList#iterator()} but the return type is - * {@link RewindableIterator}. Use this iterator type if you need to use - * {@link RewindableIterator#rewind()}. - */ - public RewindableIterator rewindableIterator() { - return new ObserverListIterator(); - } - - /** - * Returns the number of observers currently registered in the ObserverList. - * This is equivalent to the number of non-empty spaces in |mObservers|. - */ - public int size() { - return mCount; - } - - /** - * Returns true if the ObserverList contains no observers. - */ - public boolean isEmpty() { - return mCount == 0; - } - - /** - * Compact the underlying list be removing null elements. - *

- * Should only be called when mIterationDepth is zero. - */ - private void compact() { - assert mIterationDepth == 0; - for (int i = mObservers.size() - 1; i >= 0; i--) { - if (mObservers.get(i) == null) { - mObservers.remove(i); - } - } - } - - private void incrementIterationDepth() { - mIterationDepth++; - } - - private void decrementIterationDepthAndCompactIfNeeded() { - mIterationDepth--; - assert mIterationDepth >= 0; - if (mIterationDepth > 0) return; - if (!mNeedsCompact) return; - mNeedsCompact = false; - compact(); - } - - /** - * Returns the size of the underlying storage of the ObserverList. - * It will take into account the empty spaces inside |mObservers|. - */ - private int capacity() { - return mObservers.size(); - } - - private E getObserverAt(int index) { - return mObservers.get(index); - } - - private class ObserverListIterator implements RewindableIterator { - private int mListEndMarker; - private int mIndex; - private boolean mIsExhausted; - - private ObserverListIterator() { - ObserverList.this.incrementIterationDepth(); - mListEndMarker = ObserverList.this.capacity(); - } - - @Override - public void rewind() { - compactListIfNeeded(); - ObserverList.this.incrementIterationDepth(); - mListEndMarker = ObserverList.this.capacity(); - mIsExhausted = false; - mIndex = 0; - } - - @Override - public boolean hasNext() { - int lookupIndex = mIndex; - while (lookupIndex < mListEndMarker - && ObserverList.this.getObserverAt(lookupIndex) == null) { - lookupIndex++; - } - if (lookupIndex < mListEndMarker) return true; - - // We have reached the end of the list, allow for compaction. - compactListIfNeeded(); - return false; - } - - @Override - public E next() { - // Advance if the current element is null. - while (mIndex < mListEndMarker && ObserverList.this.getObserverAt(mIndex) == null) { - mIndex++; - } - if (mIndex < mListEndMarker) return ObserverList.this.getObserverAt(mIndex++); - - // We have reached the end of the list, allow for compaction. - compactListIfNeeded(); - throw new NoSuchElementException(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - private void compactListIfNeeded() { - if (!mIsExhausted) { - mIsExhausted = true; - ObserverList.this.decrementIterationDepthAndCompactIfNeeded(); - } - } - } -} diff --git a/android/java/src/org/chromium/base/PackageUtils.java b/android/java/src/org/chromium/base/PackageUtils.java deleted file mode 100644 index a8e487b35..000000000 --- a/android/java/src/org/chromium/base/PackageUtils.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; - -/** - * This class provides package checking related methods. - */ -public class PackageUtils { - /** - * Retrieves the version of the given package installed on the device. - * - * @param context Any context. - * @param packageName Name of the package to find. - * @return The package's version code if found, -1 otherwise. - */ - public static int getPackageVersion(Context context, String packageName) { - int versionCode = -1; - PackageManager pm = context.getPackageManager(); - try { - PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); - if (packageInfo != null) versionCode = packageInfo.versionCode; - } catch (PackageManager.NameNotFoundException e) { - // Do nothing, versionCode stays -1 - } - return versionCode; - } - - /** - * Decodes into a Bitmap an Image resource stored in another package. - * @param otherPackage The package containing the resource. - * @param resourceId The id of the resource. - * @return A Bitmap containing the resource or null if the package could not be found. - */ - public static Bitmap decodeImageResource(String otherPackage, int resourceId) { - PackageManager packageManager = ContextUtils.getApplicationContext().getPackageManager(); - try { - Resources resources = packageManager.getResourcesForApplication(otherPackage); - return BitmapFactory.decodeResource(resources, resourceId); - } catch (PackageManager.NameNotFoundException e) { - return null; - } - } - - private PackageUtils() { - // Hide constructor - } -} diff --git a/android/java/src/org/chromium/base/PathService.java b/android/java/src/org/chromium/base/PathService.java deleted file mode 100644 index 9807c2e82..000000000 --- a/android/java/src/org/chromium/base/PathService.java +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 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. - -package org.chromium.base; - -import org.chromium.base.annotations.JNINamespace; - -/** - * This class provides java side access to the native PathService. - */ -@JNINamespace("base::android") -public abstract class PathService { - - // Must match the value of DIR_MODULE in base/base_paths.h! - public static final int DIR_MODULE = 3; - - // Prevent instantiation. - private PathService() {} - - public static void override(int what, String path) { - nativeOverride(what, path); - } - - private static native void nativeOverride(int what, String path); -} diff --git a/android/java/src/org/chromium/base/PathUtils.java b/android/java/src/org/chromium/base/PathUtils.java deleted file mode 100644 index e6fc8029b..000000000 --- a/android/java/src/org/chromium/base/PathUtils.java +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 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. - -package org.chromium.base; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.os.Build; -import android.os.Environment; -import android.os.SystemClock; -import android.system.Os; -import android.text.TextUtils; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.MainDex; -import org.chromium.base.metrics.RecordHistogram; - -import java.io.File; -import java.util.ArrayList; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * This class provides the path related methods for the native library. - */ -@MainDex -public abstract class PathUtils { - private static final String TAG = "PathUtils"; - private static final String THUMBNAIL_DIRECTORY_NAME = "textures"; - - private static final int DATA_DIRECTORY = 0; - private static final int THUMBNAIL_DIRECTORY = 1; - private static final int CACHE_DIRECTORY = 2; - private static final int NUM_DIRECTORIES = 3; - private static final AtomicBoolean sInitializationStarted = new AtomicBoolean(); - private static AsyncTask sDirPathFetchTask; - - // If the AsyncTask started in setPrivateDataDirectorySuffix() fails to complete by the time we - // need the values, we will need the suffix so that we can restart the task synchronously on - // the UI thread. - private static String sDataDirectorySuffix; - private static String sCacheSubDirectory; - - // Prevent instantiation. - private PathUtils() {} - - /** - * Initialization-on-demand holder. This exists for thread-safe lazy initialization. It will - * cause getOrComputeDirectoryPaths() to be called (safely) the first time DIRECTORY_PATHS is - * accessed. - * - *

See https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom. - */ - private static class Holder { - private static final String[] DIRECTORY_PATHS = getOrComputeDirectoryPaths(); - } - - /** - * Get the directory paths from sDirPathFetchTask if available, or compute it synchronously - * on the UI thread otherwise. This should only be called as part of Holder's initialization - * above to guarantee thread-safety as part of the initialization-on-demand holder idiom. - */ - private static String[] getOrComputeDirectoryPaths() { - try { - // We need to call sDirPathFetchTask.cancel() here to prevent races. If it returns - // true, that means that the task got canceled successfully (and thus, it did not - // finish running its task). Otherwise, it failed to cancel, meaning that it was - // already finished. - if (sDirPathFetchTask.cancel(false)) { - // Allow disk access here because we have no other choice. - try (StrictModeContext unused = StrictModeContext.allowDiskWrites()) { - // sDirPathFetchTask did not complete. We have to run the code it was supposed - // to be responsible for synchronously on the UI thread. - return PathUtils.setPrivateDataDirectorySuffixInternal(); - } - } else { - // sDirPathFetchTask succeeded, and the values we need should be ready to access - // synchronously in its internal future. - return sDirPathFetchTask.get(); - } - } catch (InterruptedException e) { - } catch (ExecutionException e) { - } - - return null; - } - - @SuppressLint("NewApi") - private static void chmod(String path, int mode) { - // Both Os.chmod and ErrnoException require SDK >= 21. But while Dalvik on < 21 tolerates - // Os.chmod, it throws VerifyError for ErrnoException, so catch Exception instead. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; - try { - Os.chmod(path, mode); - } catch (Exception e) { - Log.e(TAG, "Failed to set permissions for path \"" + path + "\""); - } - } - - /** - * Fetch the path of the directory where private data is to be stored by the application. This - * is meant to be called in an AsyncTask in setPrivateDataDirectorySuffix(), but if we need the - * result before the AsyncTask has had a chance to finish, then it's best to cancel the task - * and run it on the UI thread instead, inside getOrComputeDirectoryPaths(). - * - * @see Context#getDir(String, int) - */ - private static String[] setPrivateDataDirectorySuffixInternal() { - String[] paths = new String[NUM_DIRECTORIES]; - Context appContext = ContextUtils.getApplicationContext(); - paths[DATA_DIRECTORY] = appContext.getDir( - sDataDirectorySuffix, Context.MODE_PRIVATE).getPath(); - // MODE_PRIVATE results in rwxrwx--x, but we want rwx------, as a defence-in-depth measure. - chmod(paths[DATA_DIRECTORY], 0700); - paths[THUMBNAIL_DIRECTORY] = appContext.getDir( - THUMBNAIL_DIRECTORY_NAME, Context.MODE_PRIVATE).getPath(); - if (appContext.getCacheDir() != null) { - if (sCacheSubDirectory == null) { - paths[CACHE_DIRECTORY] = appContext.getCacheDir().getPath(); - } else { - paths[CACHE_DIRECTORY] = - new File(appContext.getCacheDir(), sCacheSubDirectory).getPath(); - } - } - return paths; - } - - /** - * Starts an asynchronous task to fetch the path of the directory where private data is to be - * stored by the application. - * - *

This task can run long (or more likely be delayed in a large task queue), in which case we - * want to cancel it and run on the UI thread instead. Unfortunately, this means keeping a bit - * of extra static state - we need to store the suffix and the application context in case we - * need to try to re-execute later. - * - * @param suffix The private data directory suffix. - * @param cacheSubDir The subdirectory in the cache directory to use, if non-null. - * @see Context#getDir(String, int) - */ - public static void setPrivateDataDirectorySuffix(String suffix, String cacheSubDir) { - // This method should only be called once, but many tests end up calling it multiple times, - // so adding a guard here. - if (!sInitializationStarted.getAndSet(true)) { - assert ContextUtils.getApplicationContext() != null; - sDataDirectorySuffix = suffix; - sCacheSubDirectory = cacheSubDir; - sDirPathFetchTask = new AsyncTask() { - @Override - protected String[] doInBackground(Void... unused) { - return PathUtils.setPrivateDataDirectorySuffixInternal(); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - public static void setPrivateDataDirectorySuffix(String suffix) { - setPrivateDataDirectorySuffix(suffix, null); - } - - /** - * @param index The index of the cached directory path. - * @return The directory path requested. - */ - private static String getDirectoryPath(int index) { - return Holder.DIRECTORY_PATHS[index]; - } - - /** - * @return the private directory that is used to store application data. - */ - @CalledByNative - public static String getDataDirectory() { - assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; - return getDirectoryPath(DATA_DIRECTORY); - } - - /** - * @return the cache directory. - */ - @CalledByNative - public static String getCacheDirectory() { - assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; - return getDirectoryPath(CACHE_DIRECTORY); - } - - @CalledByNative - public static String getThumbnailCacheDirectory() { - assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; - return getDirectoryPath(THUMBNAIL_DIRECTORY); - } - - /** - * @return the public downloads directory. - */ - @SuppressWarnings("unused") - @CalledByNative - private static String getDownloadsDirectory() { - // Temporarily allowing disk access while fixing. TODO: http://crbug.com/508615 - try (StrictModeContext unused = StrictModeContext.allowDiskReads()) { - long time = SystemClock.elapsedRealtime(); - String downloadsPath = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) - .getPath(); - RecordHistogram.recordTimesHistogram("Android.StrictMode.DownloadsDir", - SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS); - return downloadsPath; - } - } - - /** - * @return Download directories including the default storage directory on SD card, and a - * private directory on external SD card. - */ - @SuppressWarnings("unused") - @CalledByNative - public static String[] getAllPrivateDownloadsDirectories() { - File[] files; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - try (StrictModeContext unused = StrictModeContext.allowDiskWrites()) { - files = ContextUtils.getApplicationContext().getExternalFilesDirs( - Environment.DIRECTORY_DOWNLOADS); - } - } else { - files = new File[] {Environment.getExternalStorageDirectory()}; - } - - ArrayList absolutePaths = new ArrayList(); - for (int i = 0; i < files.length; ++i) { - if (files[i] == null || TextUtils.isEmpty(files[i].getAbsolutePath())) continue; - absolutePaths.add(files[i].getAbsolutePath()); - } - - return absolutePaths.toArray(new String[absolutePaths.size()]); - } - - /** - * @return the path to native libraries. - */ - @SuppressWarnings("unused") - @CalledByNative - private static String getNativeLibraryDirectory() { - ApplicationInfo ai = ContextUtils.getApplicationContext().getApplicationInfo(); - if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 - || (ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { - return ai.nativeLibraryDir; - } - - return "/system/lib/"; - } - - /** - * @return the external storage directory. - */ - @SuppressWarnings("unused") - @CalledByNative - public static String getExternalStorageDirectory() { - return Environment.getExternalStorageDirectory().getAbsolutePath(); - } -} diff --git a/android/java/src/org/chromium/base/PowerMonitor.java b/android/java/src/org/chromium/base/PowerMonitor.java deleted file mode 100644 index ae36a75d0..000000000 --- a/android/java/src/org/chromium/base/PowerMonitor.java +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 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. - -package org.chromium.base; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.BatteryManager; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -/** - * Integrates native PowerMonitor with the java side. - */ -@JNINamespace("base::android") -public class PowerMonitor { - private static PowerMonitor sInstance; - - private boolean mIsBatteryPower; - - public static void createForTests() { - // Applications will create this once the JNI side has been fully wired up both sides. For - // tests, we just need native -> java, that is, we don't need to notify java -> native on - // creation. - sInstance = new PowerMonitor(); - } - - /** - * Create a PowerMonitor instance if none exists. - */ - public static void create() { - ThreadUtils.assertOnUiThread(); - - if (sInstance != null) return; - - Context context = ContextUtils.getApplicationContext(); - sInstance = new PowerMonitor(); - IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); - Intent batteryStatusIntent = context.registerReceiver(null, ifilter); - if (batteryStatusIntent != null) onBatteryChargingChanged(batteryStatusIntent); - - IntentFilter powerConnectedFilter = new IntentFilter(); - powerConnectedFilter.addAction(Intent.ACTION_POWER_CONNECTED); - powerConnectedFilter.addAction(Intent.ACTION_POWER_DISCONNECTED); - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - PowerMonitor.onBatteryChargingChanged(intent); - } - }, powerConnectedFilter); - } - - private PowerMonitor() { - } - - private static void onBatteryChargingChanged(Intent intent) { - assert sInstance != null; - int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); - // If we're not plugged, assume we're running on battery power. - sInstance.mIsBatteryPower = chargePlug != BatteryManager.BATTERY_PLUGGED_USB - && chargePlug != BatteryManager.BATTERY_PLUGGED_AC; - nativeOnBatteryChargingChanged(); - } - - @CalledByNative - private static boolean isBatteryPower() { - // Creation of the PowerMonitor can be deferred based on the browser startup path. If the - // battery power is requested prior to the browser triggering the creation, force it to be - // created now. - if (sInstance == null) create(); - - return sInstance.mIsBatteryPower; - } - - private static native void nativeOnBatteryChargingChanged(); -} diff --git a/android/java/src/org/chromium/base/Promise.java b/android/java/src/org/chromium/base/Promise.java deleted file mode 100644 index 4319148d9..000000000 --- a/android/java/src/org/chromium/base/Promise.java +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.os.Handler; -import android.support.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.LinkedList; -import java.util.List; - -/** - * A Promise class to be used as a placeholder for a result that will be provided asynchronously. - * It must only be accessed from a single thread. - * @param The type the Promise will be fulfilled with. - */ -public class Promise { - // TODO(peconn): Implement rejection handlers that can recover from rejection. - - @Retention(RetentionPolicy.SOURCE) - @IntDef({UNFULFILLED, FULFILLED, REJECTED}) - private @interface PromiseState {} - - private static final int UNFULFILLED = 0; - private static final int FULFILLED = 1; - private static final int REJECTED = 2; - - @PromiseState - private int mState = UNFULFILLED; - - private T mResult; - private final List> mFulfillCallbacks = new LinkedList<>(); - - private Exception mRejectReason; - private final List> mRejectCallbacks = new LinkedList<>(); - - private final Thread mThread = Thread.currentThread(); - private final Handler mHandler = new Handler(); - - private boolean mThrowingRejectionHandler; - - /** - * A function class for use when chaining Promises with {@link Promise#then(Function)}. - * @param The type of the function input. - * @param The type of the function output. - */ - public interface Function { - R apply(A argument); - } - - /** - * A function class for use when chaining Promises with {@link Promise#then(AsyncFunction)}. - * @param The type of the function input. - * @param The type of the function output. - */ - public interface AsyncFunction { - Promise apply(A argument); - } - - /** - * An exception class for when a rejected Promise is not handled and cannot pass the rejection - * to a subsequent Promise. - */ - public static class UnhandledRejectionException extends RuntimeException { - public UnhandledRejectionException(String message, Throwable cause) { - super(message, cause); - } - } - - /** - * Convenience method that calls {@link #then(Callback, Callback)} providing a rejection - * {@link Callback} that throws a {@link UnhandledRejectionException}. Only use this on - * Promises that do not have rejection handlers or dependant Promises. - */ - public void then(Callback onFulfill) { - checkThread(); - - // Allow multiple single argument then(Callback)'s, but don't bother adding duplicate - // throwing rejection handlers. - if (mThrowingRejectionHandler) { - thenInner(onFulfill); - return; - } - - assert mRejectCallbacks.size() == 0 : "Do not call the single argument " - + "Promise.then(Callback) on a Promise that already has a rejection handler."; - - Callback onReject = reason -> { - throw new UnhandledRejectionException( - "Promise was rejected without a rejection handler.", reason); - }; - - then(onFulfill, onReject); - mThrowingRejectionHandler = true; - } - - /** - * Queues {@link Callback}s to be run when the Promise is either fulfilled or rejected. If the - * Promise is already fulfilled or rejected, the appropriate callback will be run on the next - * iteration of the message loop. - * - * @param onFulfill The Callback to be called on fulfillment. - * @param onReject The Callback to be called on rejection. The argument to onReject will - * may be null if the Promise was rejected manually. - */ - public void then(Callback onFulfill, Callback onReject) { - checkThread(); - thenInner(onFulfill); - exceptInner(onReject); - } - - /** - * Adds a rejection handler to the Promise. This handler will be called if this Promise or any - * Promises this Promise depends on is rejected or fails. The {@link Callback} will be given - * the exception that caused the rejection, or null if the rejection was manual (caused by a - * call to {@link #reject()}. - */ - public void except(Callback onReject) { - checkThread(); - exceptInner(onReject); - } - - private void thenInner(Callback onFulfill) { - if (mState == FULFILLED) { - postCallbackToLooper(onFulfill, mResult); - } else if (mState == UNFULFILLED) { - mFulfillCallbacks.add(onFulfill); - } - } - - private void exceptInner(Callback onReject) { - assert !mThrowingRejectionHandler : "Do not add an exception handler to a Promise you have " - + "called the single argument Promise.then(Callback) on."; - - if (mState == REJECTED) { - postCallbackToLooper(onReject, mRejectReason); - } else if (mState == UNFULFILLED) { - mRejectCallbacks.add(onReject); - } - } - - /** - * Queues a {@link Promise.Function} to be run when the Promise is fulfilled. When this Promise - * is fulfilled, the function will be run and its result will be place in the returned Promise. - */ - public Promise then(final Function function) { - checkThread(); - - // Create a new Promise to store the result of the function. - final Promise promise = new Promise<>(); - - // Once this Promise is fulfilled: - // - Apply the given function to the result. - // - Fulfill the new Promise. - thenInner(result -> { - try { - promise.fulfill(function.apply(result)); - } catch (Exception e) { - // If function application fails, reject the next Promise. - promise.reject(e); - } - }); - - // If this Promise is rejected, reject the next Promise. - exceptInner(promise::reject); - - return promise; - } - - /** - * Queues a {@link Promise.AsyncFunction} to be run when the Promise is fulfilled. When this - * Promise is fulfilled, the AsyncFunction will be run. When the result of the AsyncFunction is - * available, it will be placed in the returned Promise. - */ - public Promise then(final AsyncFunction function) { - checkThread(); - - // Create a new Promise to be returned. - final Promise promise = new Promise<>(); - - // Once this Promise is fulfilled: - // - Apply the given function to the result (giving us an inner Promise). - // - On fulfillment of this inner Promise, fulfill our return Promise. - thenInner(result -> { - try { - // When the inner Promise is fulfilled, fulfill the return Promise. - // Alternatively, if the inner Promise is rejected, reject the return Promise. - function.apply(result).then(promise::fulfill, promise::reject); - } catch (Exception e) { - // If creating the inner Promise failed, reject the next Promise. - promise.reject(e); - } - }); - - // If this Promise is rejected, reject the next Promise. - exceptInner(promise::reject); - - return promise; - } - - /** - * Fulfills the Promise with the result and passes it to any {@link Callback}s previously queued - * on the next iteration of the message loop. - */ - public void fulfill(final T result) { - checkThread(); - assert mState == UNFULFILLED; - - mState = FULFILLED; - mResult = result; - - for (final Callback callback : mFulfillCallbacks) { - postCallbackToLooper(callback, result); - } - - mFulfillCallbacks.clear(); - } - - /** - * Rejects the Promise, rejecting all those Promises that rely on it. - * - * This may throw an exception if a dependent Promise fails to handle the rejection, so it is - * important to make it explicit when a Promise may be rejected, so that users of that Promise - * know to provide rejection handling. - */ - public void reject(final Exception reason) { - checkThread(); - assert mState == UNFULFILLED; - - mState = REJECTED; - mRejectReason = reason; - - for (final Callback callback : mRejectCallbacks) { - postCallbackToLooper(callback, reason); - } - mRejectCallbacks.clear(); - } - - /** - * Rejects a Promise, see {@link #reject(Exception)}. - */ - public void reject() { - reject(null); - } - - /** - * Returns whether the promise is fulfilled. - */ - public boolean isFulfilled() { - checkThread(); - return mState == FULFILLED; - } - - /** - * Returns whether the promise is rejected. - */ - public boolean isRejected() { - checkThread(); - return mState == REJECTED; - } - - /** - * Must be called after the promise has been fulfilled. - * - * @return The promised result. - */ - public T getResult() { - assert isFulfilled(); - return mResult; - } - - /** - * Convenience method to return a Promise fulfilled with the given result. - */ - public static Promise fulfilled(T result) { - Promise promise = new Promise<>(); - promise.fulfill(result); - return promise; - } - - private void checkThread() { - assert mThread == Thread.currentThread() : "Promise must only be used on a single Thread."; - } - - // We use a different template parameter here so this can be used for both T and Throwables. - private void postCallbackToLooper(final Callback callback, final S result) { - // Post the callbacks to the Thread looper so we don't get a long chain of callbacks - // holding up the thread. - mHandler.post(() -> callback.onResult(result)); - } -} diff --git a/android/java/src/org/chromium/base/SecureRandomInitializer.java b/android/java/src/org/chromium/base/SecureRandomInitializer.java deleted file mode 100644 index bfd7b4943..000000000 --- a/android/java/src/org/chromium/base/SecureRandomInitializer.java +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.annotation.SuppressLint; - -import java.io.FileInputStream; -import java.io.IOException; -import java.security.SecureRandom; - -/** - * This class contains code to initialize a SecureRandom generator securely on Android platforms - * <= 4.3. See - * {@link http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html}. - */ -// TODO(crbug.com/635567): Fix this properly. -@SuppressLint("SecureRandom") -public class SecureRandomInitializer { - private static final int NUM_RANDOM_BYTES = 16; - - /** - * Safely initializes the random number generator, by seeding it with data from /dev/urandom. - */ - public static void initialize(SecureRandom generator) throws IOException { - try (FileInputStream fis = new FileInputStream("/dev/urandom")) { - byte[] seedBytes = new byte[NUM_RANDOM_BYTES]; - if (fis.read(seedBytes) != seedBytes.length) { - throw new IOException("Failed to get enough random data."); - } - generator.setSeed(seedBytes); - } - } -} diff --git a/android/java/src/org/chromium/base/StreamUtil.java b/android/java/src/org/chromium/base/StreamUtil.java deleted file mode 100644 index f8cbfeeb9..000000000 --- a/android/java/src/org/chromium/base/StreamUtil.java +++ /dev/null @@ -1,28 +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. - -package org.chromium.base; - -import java.io.Closeable; -import java.io.IOException; - -/** - * Helper methods to deal with stream related tasks. - */ -public class StreamUtil { - /** - * Handle closing a {@link java.io.Closeable} via {@link java.io.Closeable#close()} and catch - * the potentially thrown {@link java.io.IOException}. - * @param closeable The Closeable to be closed. - */ - public static void closeQuietly(Closeable closeable) { - if (closeable == null) return; - - try { - closeable.close(); - } catch (IOException ex) { - // Ignore the exception on close. - } - } -} diff --git a/android/java/src/org/chromium/base/StrictModeContext.java b/android/java/src/org/chromium/base/StrictModeContext.java deleted file mode 100644 index beaaac0ad..000000000 --- a/android/java/src/org/chromium/base/StrictModeContext.java +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.os.StrictMode; - -import java.io.Closeable; - -/** - * Enables try-with-resources compatible StrictMode violation whitelisting. - * - * Example: - *

- *     try (StrictModeContext unused = StrictModeContext.allowDiskWrites()) {
- *         return Example.doThingThatRequiresDiskWrites();
- *     }
- * 
- * - */ -public final class StrictModeContext implements Closeable { - private final StrictMode.ThreadPolicy mThreadPolicy; - private final StrictMode.VmPolicy mVmPolicy; - - private StrictModeContext(StrictMode.ThreadPolicy threadPolicy, StrictMode.VmPolicy vmPolicy) { - mThreadPolicy = threadPolicy; - mVmPolicy = vmPolicy; - } - - private StrictModeContext(StrictMode.ThreadPolicy threadPolicy) { - this(threadPolicy, null); - } - - private StrictModeContext(StrictMode.VmPolicy vmPolicy) { - this(null, vmPolicy); - } - - /** - * Convenience method for disabling all VM-level StrictMode checks with try-with-resources. - * Includes everything listed here: - * https://developer.android.com/reference/android/os/StrictMode.VmPolicy.Builder.html - */ - public static StrictModeContext allowAllVmPolicies() { - StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); - StrictMode.setVmPolicy(StrictMode.VmPolicy.LAX); - return new StrictModeContext(oldPolicy); - } - - /** - * Convenience method for disabling StrictMode for disk-writes with try-with-resources. - */ - public static StrictModeContext allowDiskWrites() { - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); - return new StrictModeContext(oldPolicy); - } - - /** - * Convenience method for disabling StrictMode for disk-reads with try-with-resources. - */ - public static StrictModeContext allowDiskReads() { - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - return new StrictModeContext(oldPolicy); - } - - /** - * Convenience method for disabling StrictMode for slow calls with try-with-resources. - */ - public static StrictModeContext allowSlowCalls() { - StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); - StrictMode.setThreadPolicy( - new StrictMode.ThreadPolicy.Builder(oldPolicy).permitCustomSlowCalls().build()); - return new StrictModeContext(oldPolicy); - } - - @Override - public void close() { - if (mThreadPolicy != null) { - StrictMode.setThreadPolicy(mThreadPolicy); - } - if (mVmPolicy != null) { - StrictMode.setVmPolicy(mVmPolicy); - } - } -} \ No newline at end of file diff --git a/android/java/src/org/chromium/base/Supplier.java b/android/java/src/org/chromium/base/Supplier.java deleted file mode 100644 index 350da578a..000000000 --- a/android/java/src/org/chromium/base/Supplier.java +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -/** - * Based on Java 8's java.util.function.Supplier. - * Same as Callable, but without a checked Exception. - * - * @param Return type. - */ -public interface Supplier { - /** - * Returns a value. - */ - T get(); -} diff --git a/android/java/src/org/chromium/base/SysUtils.java b/android/java/src/org/chromium/base/SysUtils.java deleted file mode 100644 index d4eb30de5..000000000 --- a/android/java/src/org/chromium/base/SysUtils.java +++ /dev/null @@ -1,199 +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. - -package org.chromium.base; - -import android.annotation.TargetApi; -import android.app.ActivityManager; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.StrictMode; -import android.util.Log; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.metrics.CachedMetrics; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Exposes system related information about the current device. - */ -@JNINamespace("base::android") -public class SysUtils { - // A device reporting strictly more total memory in megabytes cannot be considered 'low-end'. - private static final int ANDROID_LOW_MEMORY_DEVICE_THRESHOLD_MB = 512; - private static final int ANDROID_O_LOW_MEMORY_DEVICE_THRESHOLD_MB = 1024; - - private static final String TAG = "SysUtils"; - - private static Boolean sLowEndDevice; - private static Integer sAmountOfPhysicalMemoryKB; - - private static CachedMetrics.BooleanHistogramSample sLowEndMatches = - new CachedMetrics.BooleanHistogramSample("Android.SysUtilsLowEndMatches"); - - private SysUtils() { } - - /** - * Return the amount of physical memory on this device in kilobytes. - * @return Amount of physical memory in kilobytes, or 0 if there was - * an error trying to access the information. - */ - private static int detectAmountOfPhysicalMemoryKB() { - // Extract total memory RAM size by parsing /proc/meminfo, note that - // this is exactly what the implementation of sysconf(_SC_PHYS_PAGES) - // does. However, it can't be called because this method must be - // usable before any native code is loaded. - - // An alternative is to use ActivityManager.getMemoryInfo(), but this - // requires a valid ActivityManager handle, which can only come from - // a valid Context object, which itself cannot be retrieved - // during early startup, where this method is called. And making it - // an explicit parameter here makes all call paths _much_ more - // complicated. - - Pattern pattern = Pattern.compile("^MemTotal:\\s+([0-9]+) kB$"); - // Synchronously reading files in /proc in the UI thread is safe. - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - try { - FileReader fileReader = new FileReader("/proc/meminfo"); - try { - BufferedReader reader = new BufferedReader(fileReader); - try { - String line; - for (;;) { - line = reader.readLine(); - if (line == null) { - Log.w(TAG, "/proc/meminfo lacks a MemTotal entry?"); - break; - } - Matcher m = pattern.matcher(line); - if (!m.find()) continue; - - int totalMemoryKB = Integer.parseInt(m.group(1)); - // Sanity check. - if (totalMemoryKB <= 1024) { - Log.w(TAG, "Invalid /proc/meminfo total size in kB: " + m.group(1)); - break; - } - - return totalMemoryKB; - } - - } finally { - reader.close(); - } - } finally { - fileReader.close(); - } - } catch (Exception e) { - Log.w(TAG, "Cannot get total physical size from /proc/meminfo", e); - } finally { - StrictMode.setThreadPolicy(oldPolicy); - } - - return 0; - } - - /** - * @return Whether or not this device should be considered a low end device. - */ - @CalledByNative - public static boolean isLowEndDevice() { - if (sLowEndDevice == null) { - sLowEndDevice = detectLowEndDevice(); - } - return sLowEndDevice.booleanValue(); - } - - /** - * @return Whether or not this device should be considered a low end device. - */ - public static int amountOfPhysicalMemoryKB() { - if (sAmountOfPhysicalMemoryKB == null) { - sAmountOfPhysicalMemoryKB = detectAmountOfPhysicalMemoryKB(); - } - return sAmountOfPhysicalMemoryKB.intValue(); - } - - /** - * @return Whether or not the system has low available memory. - */ - @CalledByNative - public static boolean isCurrentlyLowMemory() { - ActivityManager am = - (ActivityManager) ContextUtils.getApplicationContext().getSystemService( - Context.ACTIVITY_SERVICE); - ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo(); - am.getMemoryInfo(info); - return info.lowMemory; - } - - /** - * Resets the cached value, if any. - */ - @VisibleForTesting - public static void resetForTesting() { - sLowEndDevice = null; - sAmountOfPhysicalMemoryKB = null; - } - - public static boolean hasCamera(final Context context) { - final PackageManager pm = context.getPackageManager(); - // JellyBean support. - boolean hasCamera = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - hasCamera |= pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY); - } - return hasCamera; - } - - @TargetApi(Build.VERSION_CODES.KITKAT) - private static boolean detectLowEndDevice() { - assert CommandLine.isInitialized(); - if (CommandLine.getInstance().hasSwitch(BaseSwitches.ENABLE_LOW_END_DEVICE_MODE)) { - return true; - } - if (CommandLine.getInstance().hasSwitch(BaseSwitches.DISABLE_LOW_END_DEVICE_MODE)) { - return false; - } - - sAmountOfPhysicalMemoryKB = detectAmountOfPhysicalMemoryKB(); - boolean isLowEnd = true; - if (sAmountOfPhysicalMemoryKB <= 0) { - isLowEnd = false; - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - isLowEnd = sAmountOfPhysicalMemoryKB / 1024 <= ANDROID_O_LOW_MEMORY_DEVICE_THRESHOLD_MB; - } else { - isLowEnd = sAmountOfPhysicalMemoryKB / 1024 <= ANDROID_LOW_MEMORY_DEVICE_THRESHOLD_MB; - } - - // For evaluation purposes check whether our computation agrees with Android API value. - Context appContext = ContextUtils.getApplicationContext(); - boolean isLowRam = false; - if (appContext != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - isLowRam = ((ActivityManager) ContextUtils.getApplicationContext().getSystemService( - Context.ACTIVITY_SERVICE)) - .isLowRamDevice(); - } - sLowEndMatches.record(isLowEnd == isLowRam); - - return isLowEnd; - } - - /** - * Creates a new trace event to log the number of minor / major page faults, if tracing is - * enabled. - */ - public static void logPageFaultCountToTracing() { - nativeLogPageFaultCountToTracing(); - } - - private static native void nativeLogPageFaultCountToTracing(); -} diff --git a/android/java/src/org/chromium/base/ThreadUtils.java b/android/java/src/org/chromium/base/ThreadUtils.java deleted file mode 100644 index 61872a017..000000000 --- a/android/java/src/org/chromium/base/ThreadUtils.java +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 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. - -package org.chromium.base; - -import android.os.Handler; -import android.os.Looper; -import android.os.Process; - -import org.chromium.base.annotations.CalledByNative; - -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.FutureTask; - -/** - * Helper methods to deal with threading related tasks. - */ -public class ThreadUtils { - - private static final Object sLock = new Object(); - - private static boolean sWillOverride; - - private static Handler sUiThreadHandler; - - private static boolean sThreadAssertsDisabled; - - public static void setWillOverrideUiThread() { - synchronized (sLock) { - sWillOverride = true; - } - } - - public static void setUiThread(Looper looper) { - synchronized (sLock) { - if (looper == null) { - // Used to reset the looper after tests. - sUiThreadHandler = null; - return; - } - if (sUiThreadHandler != null && sUiThreadHandler.getLooper() != looper) { - throw new RuntimeException("UI thread looper is already set to " - + sUiThreadHandler.getLooper() + " (Main thread looper is " - + Looper.getMainLooper() + "), cannot set to new looper " + looper); - } else { - sUiThreadHandler = new Handler(looper); - } - } - } - - public static Handler getUiThreadHandler() { - synchronized (sLock) { - if (sUiThreadHandler == null) { - if (sWillOverride) { - throw new RuntimeException("Did not yet override the UI thread"); - } - sUiThreadHandler = new Handler(Looper.getMainLooper()); - } - return sUiThreadHandler; - } - } - - /** - * Run the supplied Runnable on the main thread. The method will block until the Runnable - * completes. - * - * @param r The Runnable to run. - */ - public static void runOnUiThreadBlocking(final Runnable r) { - if (runningOnUiThread()) { - r.run(); - } else { - FutureTask task = new FutureTask(r, null); - postOnUiThread(task); - try { - task.get(); - } catch (Exception e) { - throw new RuntimeException("Exception occurred while waiting for runnable", e); - } - } - } - - /** - * Run the supplied Callable on the main thread, wrapping any exceptions in a RuntimeException. - * The method will block until the Callable completes. - * - * @param c The Callable to run - * @return The result of the callable - */ - @VisibleForTesting - public static T runOnUiThreadBlockingNoException(Callable c) { - try { - return runOnUiThreadBlocking(c); - } catch (ExecutionException e) { - throw new RuntimeException("Error occurred waiting for callable", e); - } - } - - /** - * Run the supplied Callable on the main thread, The method will block until the Callable - * completes. - * - * @param c The Callable to run - * @return The result of the callable - * @throws ExecutionException c's exception - */ - public static T runOnUiThreadBlocking(Callable c) throws ExecutionException { - FutureTask task = new FutureTask(c); - runOnUiThread(task); - try { - return task.get(); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted waiting for callable", e); - } - } - - /** - * Run the supplied FutureTask on the main thread. The method will block only if the current - * thread is the main thread. - * - * @param task The FutureTask to run - * @return The queried task (to aid inline construction) - */ - public static FutureTask runOnUiThread(FutureTask task) { - if (runningOnUiThread()) { - task.run(); - } else { - postOnUiThread(task); - } - return task; - } - - /** - * Run the supplied Callable on the main thread. The method will block only if the current - * thread is the main thread. - * - * @param c The Callable to run - * @return A FutureTask wrapping the callable to retrieve results - */ - public static FutureTask runOnUiThread(Callable c) { - return runOnUiThread(new FutureTask(c)); - } - - /** - * Run the supplied Runnable on the main thread. The method will block only if the current - * thread is the main thread. - * - * @param r The Runnable to run - */ - public static void runOnUiThread(Runnable r) { - if (runningOnUiThread()) { - r.run(); - } else { - getUiThreadHandler().post(r); - } - } - - /** - * Post the supplied FutureTask to run on the main thread. The method will not block, even if - * called on the UI thread. - * - * @param task The FutureTask to run - * @return The queried task (to aid inline construction) - */ - public static FutureTask postOnUiThread(FutureTask task) { - getUiThreadHandler().post(task); - return task; - } - - /** - * Post the supplied Runnable to run on the main thread. The method will not block, even if - * called on the UI thread. - * - * @param task The Runnable to run - */ - public static void postOnUiThread(Runnable task) { - getUiThreadHandler().post(task); - } - - /** - * Post the supplied Runnable to run on the main thread after the given amount of time. The - * method will not block, even if called on the UI thread. - * - * @param task The Runnable to run - * @param delayMillis The delay in milliseconds until the Runnable will be run - */ - @VisibleForTesting - public static void postOnUiThreadDelayed(Runnable task, long delayMillis) { - getUiThreadHandler().postDelayed(task, delayMillis); - } - - /** - * Throw an exception (when DCHECKs are enabled) if currently not running on the UI thread. - * - * Can be disabled by setThreadAssertsDisabledForTesting(true). - */ - public static void assertOnUiThread() { - if (sThreadAssertsDisabled) return; - - assert runningOnUiThread() : "Must be called on the UI thread."; - } - - /** - * Throw an exception (regardless of build) if currently not running on the UI thread. - * - * Can be disabled by setThreadAssertsEnabledForTesting(false). - * - * @see #assertOnUiThread() - */ - public static void checkUiThread() { - if (!sThreadAssertsDisabled && !runningOnUiThread()) { - throw new IllegalStateException("Must be called on the UI thread."); - } - } - - /** - * Throw an exception (when DCHECKs are enabled) if currently running on the UI thread. - * - * Can be disabled by setThreadAssertsDisabledForTesting(true). - */ - public static void assertOnBackgroundThread() { - if (sThreadAssertsDisabled) return; - - assert !runningOnUiThread() : "Must be called on a thread other than UI."; - } - - /** - * Disables thread asserts. - * - * Can be used by tests where code that normally runs multi-threaded is going to run - * single-threaded for the test (otherwise asserts that are valid in production would fail in - * those tests). - */ - public static void setThreadAssertsDisabledForTesting(boolean disabled) { - sThreadAssertsDisabled = disabled; - } - - /** - * @return true iff the current thread is the main (UI) thread. - */ - public static boolean runningOnUiThread() { - return getUiThreadHandler().getLooper() == Looper.myLooper(); - } - - public static Looper getUiThreadLooper() { - return getUiThreadHandler().getLooper(); - } - - /** - * Set thread priority to audio. - */ - @CalledByNative - public static void setThreadPriorityAudio(int tid) { - Process.setThreadPriority(tid, Process.THREAD_PRIORITY_AUDIO); - } - - /** - * Checks whether Thread priority is THREAD_PRIORITY_AUDIO or not. - * @param tid Thread id. - * @return true for THREAD_PRIORITY_AUDIO and false otherwise. - */ - @CalledByNative - private static boolean isThreadPriorityAudio(int tid) { - return Process.getThreadPriority(tid) == Process.THREAD_PRIORITY_AUDIO; - } -} diff --git a/android/java/src/org/chromium/base/ThrowUncaughtException.java b/android/java/src/org/chromium/base/ThrowUncaughtException.java deleted file mode 100644 index d5f18a278..000000000 --- a/android/java/src/org/chromium/base/ThrowUncaughtException.java +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.MainDex; - -@MainDex -abstract class ThrowUncaughtException { - @CalledByNative - private static void post() { - ThreadUtils.postOnUiThread(new Runnable() { - @Override - public void run() { - throw new RuntimeException("Intentional exception not caught by JNI"); - } - }); - } -} diff --git a/android/java/src/org/chromium/base/TimeUtils.java b/android/java/src/org/chromium/base/TimeUtils.java deleted file mode 100644 index dcacabf20..000000000 --- a/android/java/src/org/chromium/base/TimeUtils.java +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.MainDex; - -/** Time-related utilities. */ -@JNINamespace("base::android") -@MainDex -public class TimeUtils { - private TimeUtils() {} - - /** Returns TimeTicks::Now() in microseconds. */ - public static native long nativeGetTimeTicksNowUs(); -} diff --git a/android/java/src/org/chromium/base/TimezoneUtils.java b/android/java/src/org/chromium/base/TimezoneUtils.java deleted file mode 100644 index cddd3d9d2..000000000 --- a/android/java/src/org/chromium/base/TimezoneUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.os.StrictMode; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.MainDex; - -import java.util.TimeZone; - -@JNINamespace("base::android") -@MainDex -class TimezoneUtils { - /** - * Guards this class from being instantiated. - */ - - private TimezoneUtils() {} - - /** - * @return the Olson timezone ID of the current system time zone. - */ - @CalledByNative - private static String getDefaultTimeZoneId() { - // On Android N or earlier, getting the default timezone requires the disk - // access when a device set up is skipped. - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - String timezoneID = TimeZone.getDefault().getID(); - StrictMode.setThreadPolicy(oldPolicy); - return timezoneID; - } -} diff --git a/android/java/src/org/chromium/base/TraceEvent.java b/android/java/src/org/chromium/base/TraceEvent.java deleted file mode 100644 index 96590900e..000000000 --- a/android/java/src/org/chromium/base/TraceEvent.java +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.os.Looper; -import android.os.MessageQueue; -import android.os.SystemClock; -import android.util.Log; -import android.util.Printer; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.MainDex; -/** - * Java mirror of Chrome trace event API. See base/trace_event/trace_event.h. - * - * To get scoped trace events, use the "try with resource" construct, for instance: - *
{@code
- * try (TraceEvent e = TraceEvent.scoped("MyTraceEvent")) {
- *   // code.
- * }
- * }
- * - * It is OK to use tracing before the native library has loaded, in a slightly restricted fashion. - * @see EarlyTraceEvent for details. - */ -@JNINamespace("base::android") -@MainDex -public class TraceEvent implements AutoCloseable { - private static volatile boolean sEnabled; - private static volatile boolean sATraceEnabled; // True when taking an Android systrace. - - private static class BasicLooperMonitor implements Printer { - private static final String EARLY_TOPLEVEL_TASK_NAME = "Looper.dispatchMessage: "; - - @Override - public void println(final String line) { - if (line.startsWith(">")) { - beginHandling(line); - } else { - assert line.startsWith("<"); - endHandling(line); - } - } - - void beginHandling(final String line) { - // May return an out-of-date value. this is not an issue as EarlyTraceEvent#begin() - // will filter the event in this case. - boolean earlyTracingActive = EarlyTraceEvent.isActive(); - if (sEnabled || earlyTracingActive) { - String target = getTarget(line); - if (sEnabled) { - nativeBeginToplevel(target); - } else if (earlyTracingActive) { - // Synthesize a task name instead of using a parameter, as early tracing doesn't - // support parameters. - EarlyTraceEvent.begin(EARLY_TOPLEVEL_TASK_NAME + target); - } - } - } - - void endHandling(final String line) { - if (EarlyTraceEvent.isActive()) { - EarlyTraceEvent.end(EARLY_TOPLEVEL_TASK_NAME + getTarget(line)); - } - if (sEnabled) nativeEndToplevel(); - } - - /** - * Android Looper formats |line| as ">>>>> Dispatching to (TARGET) [...]" since at least - * 2009 (Donut). Extracts the TARGET part of the message. - */ - private static String getTarget(String logLine) { - int start = logLine.indexOf('(', 21); // strlen(">>>>> Dispatching to ") - int end = start == -1 ? -1 : logLine.indexOf(')', start); - return end != -1 ? logLine.substring(start + 1, end) : ""; - } - } - - /** - * A class that records, traces and logs statistics about the UI thead's Looper. - * The output of this class can be used in a number of interesting ways: - *

- *

  1. - * When using chrometrace, there will be a near-continuous line of - * measurements showing both event dispatches as well as idles; - *
  2. - * Logging messages are output for events that run too long on the - * event dispatcher, making it easy to identify problematic areas; - *
  3. - * Statistics are output whenever there is an idle after a non-trivial - * amount of activity, allowing information to be gathered about task - * density and execution cadence on the Looper; - *
- *

- * The class attaches itself as an idle handler to the main Looper, and - * monitors the execution of events and idle notifications. Task counters - * accumulate between idle notifications and get reset when a new idle - * notification is received. - */ - private static final class IdleTracingLooperMonitor extends BasicLooperMonitor - implements MessageQueue.IdleHandler { - // Tags for dumping to logcat or TraceEvent - private static final String TAG = "TraceEvent.LooperMonitor"; - private static final String IDLE_EVENT_NAME = "Looper.queueIdle"; - - // Calculation constants - private static final long FRAME_DURATION_MILLIS = 1000L / 60L; // 60 FPS - // A reasonable threshold for defining a Looper event as "long running" - private static final long MIN_INTERESTING_DURATION_MILLIS = - FRAME_DURATION_MILLIS; - // A reasonable threshold for a "burst" of tasks on the Looper - private static final long MIN_INTERESTING_BURST_DURATION_MILLIS = - MIN_INTERESTING_DURATION_MILLIS * 3; - - // Stats tracking - private long mLastIdleStartedAt; - private long mLastWorkStartedAt; - private int mNumTasksSeen; - private int mNumIdlesSeen; - private int mNumTasksSinceLastIdle; - - // State - private boolean mIdleMonitorAttached; - - // Called from within the begin/end methods only. - // This method can only execute on the looper thread, because that is - // the only thread that is permitted to call Looper.myqueue(). - private final void syncIdleMonitoring() { - if (sEnabled && !mIdleMonitorAttached) { - // approximate start time for computational purposes - mLastIdleStartedAt = SystemClock.elapsedRealtime(); - Looper.myQueue().addIdleHandler(this); - mIdleMonitorAttached = true; - Log.v(TAG, "attached idle handler"); - } else if (mIdleMonitorAttached && !sEnabled) { - Looper.myQueue().removeIdleHandler(this); - mIdleMonitorAttached = false; - Log.v(TAG, "detached idle handler"); - } - } - - @Override - final void beginHandling(final String line) { - // Close-out any prior 'idle' period before starting new task. - if (mNumTasksSinceLastIdle == 0) { - TraceEvent.end(IDLE_EVENT_NAME); - } - mLastWorkStartedAt = SystemClock.elapsedRealtime(); - syncIdleMonitoring(); - super.beginHandling(line); - } - - @Override - final void endHandling(final String line) { - final long elapsed = SystemClock.elapsedRealtime() - - mLastWorkStartedAt; - if (elapsed > MIN_INTERESTING_DURATION_MILLIS) { - traceAndLog(Log.WARN, "observed a task that took " - + elapsed + "ms: " + line); - } - super.endHandling(line); - syncIdleMonitoring(); - mNumTasksSeen++; - mNumTasksSinceLastIdle++; - } - - private static void traceAndLog(int level, String message) { - TraceEvent.instant("TraceEvent.LooperMonitor:IdleStats", message); - Log.println(level, TAG, message); - } - - @Override - public final boolean queueIdle() { - final long now = SystemClock.elapsedRealtime(); - if (mLastIdleStartedAt == 0) mLastIdleStartedAt = now; - final long elapsed = now - mLastIdleStartedAt; - mNumIdlesSeen++; - TraceEvent.begin(IDLE_EVENT_NAME, mNumTasksSinceLastIdle + " tasks since last idle."); - if (elapsed > MIN_INTERESTING_BURST_DURATION_MILLIS) { - // Dump stats - String statsString = mNumTasksSeen + " tasks and " - + mNumIdlesSeen + " idles processed so far, " - + mNumTasksSinceLastIdle + " tasks bursted and " - + elapsed + "ms elapsed since last idle"; - traceAndLog(Log.DEBUG, statsString); - } - mLastIdleStartedAt = now; - mNumTasksSinceLastIdle = 0; - return true; // stay installed - } - } - - // Holder for monitor avoids unnecessary construction on non-debug runs - private static final class LooperMonitorHolder { - private static final BasicLooperMonitor sInstance = - CommandLine.getInstance().hasSwitch(BaseSwitches.ENABLE_IDLE_TRACING) - ? new IdleTracingLooperMonitor() : new BasicLooperMonitor(); - } - - private final String mName; - - /** - * Constructor used to support the "try with resource" construct. - */ - private TraceEvent(String name, String arg) { - mName = name; - begin(name, arg); - } - - @Override - public void close() { - end(mName); - } - - /** - * Factory used to support the "try with resource" construct. - * - * Note that if tracing is not enabled, this will not result in allocating an object. - * - * @param name Trace event name. - * @param name The arguments of the event. - * @return a TraceEvent, or null if tracing is not enabled. - */ - public static TraceEvent scoped(String name, String arg) { - if (!(EarlyTraceEvent.enabled() || enabled())) return null; - return new TraceEvent(name, arg); - } - - /** - * Similar to {@link #scoped(String, String arg)}, but uses null for |arg|. - */ - public static TraceEvent scoped(String name) { - return scoped(name, null); - } - - /** - * Register an enabled observer, such that java traces are always enabled with native. - */ - public static void registerNativeEnabledObserver() { - nativeRegisterEnabledObserver(); - } - - /** - * Notification from native that tracing is enabled/disabled. - */ - @CalledByNative - public static void setEnabled(boolean enabled) { - if (enabled) EarlyTraceEvent.disable(); - // Only disable logging if Chromium enabled it originally, so as to not disrupt logging done - // by other applications - if (sEnabled != enabled) { - sEnabled = enabled; - // Android M+ systrace logs this on its own. Only log it if not writing to Android - // systrace. - if (sATraceEnabled) return; - ThreadUtils.getUiThreadLooper().setMessageLogging( - enabled ? LooperMonitorHolder.sInstance : null); - } - } - - /** - * May enable early tracing depending on the environment. - * - * Must be called after the command-line has been read. - */ - public static void maybeEnableEarlyTracing() { - EarlyTraceEvent.maybeEnable(); - if (EarlyTraceEvent.isActive()) { - ThreadUtils.getUiThreadLooper().setMessageLogging(LooperMonitorHolder.sInstance); - } - } - - /** - * Enables or disabled Android systrace path of Chrome tracing. If enabled, all Chrome - * traces will be also output to Android systrace. Because of the overhead of Android - * systrace, this is for WebView only. - */ - public static void setATraceEnabled(boolean enabled) { - if (sATraceEnabled == enabled) return; - sATraceEnabled = enabled; - if (enabled) { - // Calls TraceEvent.setEnabled(true) via - // TraceLog::EnabledStateObserver::OnTraceLogEnabled - nativeStartATrace(); - } else { - // Calls TraceEvent.setEnabled(false) via - // TraceLog::EnabledStateObserver::OnTraceLogDisabled - nativeStopATrace(); - } - } - - /** - * @return True if tracing is enabled, false otherwise. - * It is safe to call trace methods without checking if TraceEvent - * is enabled. - */ - public static boolean enabled() { - return sEnabled; - } - - /** - * Triggers the 'instant' native trace event with no arguments. - * @param name The name of the event. - */ - public static void instant(String name) { - if (sEnabled) nativeInstant(name, null); - } - - /** - * Triggers the 'instant' native trace event. - * @param name The name of the event. - * @param arg The arguments of the event. - */ - public static void instant(String name, String arg) { - if (sEnabled) nativeInstant(name, arg); - } - - /** - * Triggers the 'start' native trace event with no arguments. - * @param name The name of the event. - * @param id The id of the asynchronous event. - */ - public static void startAsync(String name, long id) { - EarlyTraceEvent.startAsync(name, id); - if (sEnabled) nativeStartAsync(name, id); - } - - /** - * Triggers the 'finish' native trace event with no arguments. - * @param name The name of the event. - * @param id The id of the asynchronous event. - */ - public static void finishAsync(String name, long id) { - EarlyTraceEvent.finishAsync(name, id); - if (sEnabled) nativeFinishAsync(name, id); - } - - /** - * Triggers the 'begin' native trace event with no arguments. - * @param name The name of the event. - */ - public static void begin(String name) { - begin(name, null); - } - - /** - * Triggers the 'begin' native trace event. - * @param name The name of the event. - * @param arg The arguments of the event. - */ - public static void begin(String name, String arg) { - EarlyTraceEvent.begin(name); - if (sEnabled) nativeBegin(name, arg); - } - - /** - * Triggers the 'end' native trace event with no arguments. - * @param name The name of the event. - */ - public static void end(String name) { - end(name, null); - } - - /** - * Triggers the 'end' native trace event. - * @param name The name of the event. - * @param arg The arguments of the event. - */ - public static void end(String name, String arg) { - EarlyTraceEvent.end(name); - if (sEnabled) nativeEnd(name, arg); - } - - private static native void nativeRegisterEnabledObserver(); - private static native void nativeStartATrace(); - private static native void nativeStopATrace(); - private static native void nativeInstant(String name, String arg); - private static native void nativeBegin(String name, String arg); - private static native void nativeEnd(String name, String arg); - private static native void nativeBeginToplevel(String target); - private static native void nativeEndToplevel(); - private static native void nativeStartAsync(String name, long id); - private static native void nativeFinishAsync(String name, long id); -} diff --git a/android/java/src/org/chromium/base/UnguessableToken.java b/android/java/src/org/chromium/base/UnguessableToken.java deleted file mode 100644 index 4b1619dae..000000000 --- a/android/java/src/org/chromium/base/UnguessableToken.java +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.os.Parcel; -import android.os.Parcelable; - -import org.chromium.base.annotations.CalledByNative; - -/** - * This class mirrors unguessable_token.h . Since tokens are passed by value, - * we don't bother to maintain a native token. This implements Parcelable so - * that it may be sent via binder. - * - * To get one of these from native, one must start with a - * base::UnguessableToken, then create a Java object from it. See - * jni_unguessable_token.h for information. - */ -public class UnguessableToken implements Parcelable { - private final long mHigh; - private final long mLow; - - private UnguessableToken(long high, long low) { - mHigh = high; - mLow = low; - } - - @CalledByNative - private static UnguessableToken create(long high, long low) { - return new UnguessableToken(high, low); - } - - @CalledByNative - public long getHighForSerialization() { - return mHigh; - } - - @CalledByNative - public long getLowForSerialization() { - return mLow; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeLong(mHigh); - dest.writeLong(mLow); - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public UnguessableToken createFromParcel(Parcel source) { - long high = source.readLong(); - long low = source.readLong(); - if (high == 0 || low == 0) { - // Refuse to create an empty UnguessableToken. - return null; - } - return new UnguessableToken(high, low); - } - - @Override - public UnguessableToken[] newArray(int size) { - return new UnguessableToken[size]; - } - }; - - // To avoid unwieldy calls in JNI for tests, parcel and unparcel. - // TODO(liberato): It would be nice if we could include this only with a - // java driver that's linked only with unit tests, but i don't see a way - // to do that. - @CalledByNative - private UnguessableToken parcelAndUnparcelForTesting() { - Parcel parcel = Parcel.obtain(); - writeToParcel(parcel, 0); - - // Rewind the parcel and un-parcel. - parcel.setDataPosition(0); - UnguessableToken token = CREATOR.createFromParcel(parcel); - parcel.recycle(); - - return token; - } -}; diff --git a/android/java/src/org/chromium/base/VisibleForTesting.java b/android/java/src/org/chromium/base/VisibleForTesting.java deleted file mode 100644 index 24cbfadfa..000000000 --- a/android/java/src/org/chromium/base/VisibleForTesting.java +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -/** - * Annotation used to mark code that has wider visibility or present for testing code. - */ -public @interface VisibleForTesting { - -} diff --git a/android/java/src/org/chromium/base/annotations/AccessedByNative.java b/android/java/src/org/chromium/base/annotations/AccessedByNative.java deleted file mode 100644 index 6df7c1102..000000000 --- a/android/java/src/org/chromium/base/annotations/AccessedByNative.java +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 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. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @AccessedByNative is used to ensure proguard will keep this field, since it's - * only accessed by native. - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.CLASS) -public @interface AccessedByNative { - public String value() default ""; -} diff --git a/android/java/src/org/chromium/base/annotations/CalledByNative.java b/android/java/src/org/chromium/base/annotations/CalledByNative.java deleted file mode 100644 index 52f5b7e59..000000000 --- a/android/java/src/org/chromium/base/annotations/CalledByNative.java +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 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. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @CalledByNative is used by the JNI generator to create the necessary JNI - * bindings and expose this method to native code. - */ -@Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) -@Retention(RetentionPolicy.CLASS) -public @interface CalledByNative { - /* - * If present, tells which inner class the method belongs to. - */ - public String value() default ""; -} diff --git a/android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java b/android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java deleted file mode 100644 index c0abcbe64..000000000 --- a/android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 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. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @CalledByNativeUnchecked is used to generate JNI bindings that do not check for exceptions. - * It only makes sense to use this annotation on methods that declare a throws... spec. - * However, note that the exception received native side maybe an 'unchecked' (RuntimeExpception) - * such as NullPointerException, so the native code should differentiate these cases. - * Usage of this should be very rare; where possible handle exceptions in the Java side and use a - * return value to indicate success / failure. - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.CLASS) -public @interface CalledByNativeUnchecked { - /* - * If present, tells which inner class the method belongs to. - */ - public String value() default ""; -} diff --git a/android/java/src/org/chromium/base/annotations/DoNotInline.java b/android/java/src/org/chromium/base/annotations/DoNotInline.java deleted file mode 100644 index 9252f3a79..000000000 --- a/android/java/src/org/chromium/base/annotations/DoNotInline.java +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The annotated method or class should never be inlined. - * - * The annotated method (or methods on the annotated class) are guaranteed not to be inlined by - * Proguard. Other optimizations may still apply. - */ -@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE}) -@Retention(RetentionPolicy.CLASS) -public @interface DoNotInline {} diff --git a/android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java b/android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java deleted file mode 100644 index f1bf85ef4..000000000 --- a/android/java/src/org/chromium/base/annotations/JNIAdditionalImport.java +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * JNIAdditionalImport is used by the JNI generator to qualify inner types used on JNI methods. Must - * be used when an inner class is used from a class within the same package. Example: - * - *

- * @JNIAdditionImport(Foo.class)
- * public class Bar {
- *     @CalledByNative static void doSomethingWithInner(Foo.Inner inner) {
- *     ...
- *     }
- * }
- * 
- * 

- * Notes: - * 1) Foo must be in the same package as Bar - * 2) For classes in different packages, they should be imported as: - * import other.package.Foo; - * and this annotation should not be used. - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.CLASS) -public @interface JNIAdditionalImport { - Class[] value(); -} diff --git a/android/java/src/org/chromium/base/annotations/JNINamespace.java b/android/java/src/org/chromium/base/annotations/JNINamespace.java deleted file mode 100644 index 4cd5531fa..000000000 --- a/android/java/src/org/chromium/base/annotations/JNINamespace.java +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 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. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @JNINamespace is used by the JNI generator to create the necessary JNI - * bindings and expose this method to native code using the specified namespace. - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface JNINamespace { - public String value(); -} diff --git a/android/java/src/org/chromium/base/annotations/MainDex.java b/android/java/src/org/chromium/base/annotations/MainDex.java deleted file mode 100644 index 56aab743f..000000000 --- a/android/java/src/org/chromium/base/annotations/MainDex.java +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * An annotation that signals that a class should be kept in the main dex file. - * - * This generally means it's used by renderer processes, which can't load secondary dexes - * on K and below. - */ -@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE}) -@Retention(RetentionPolicy.CLASS) -public @interface MainDex {} diff --git a/android/java/src/org/chromium/base/annotations/NativeCall.java b/android/java/src/org/chromium/base/annotations/NativeCall.java deleted file mode 100644 index b69cd17e2..000000000 --- a/android/java/src/org/chromium/base/annotations/NativeCall.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @NativeCall is used by the JNI generator to create the necessary JNI bindings - * so a native function can be bound to a Java inner class. The native class for - * which the JNI method will be generated is specified by the first parameter. - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.CLASS) -public @interface NativeCall { - /* - * Value determines which native class the method should map to. - */ - public String value() default ""; -} diff --git a/android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java b/android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java deleted file mode 100644 index afbc36854..000000000 --- a/android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 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. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @NativeClassQualifiedName is used by the JNI generator to create the necessary JNI - * bindings to call into the specified native class name. - */ -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface NativeClassQualifiedName { - /* - * Tells which native class the method is going to be bound to. - * The first parameter of the annotated method must be an int nativePtr pointing to - * an instance of this class. - */ - public String value(); -} diff --git a/android/java/src/org/chromium/base/annotations/RemovableInRelease.java b/android/java/src/org/chromium/base/annotations/RemovableInRelease.java deleted file mode 100644 index 2191334a4..000000000 --- a/android/java/src/org/chromium/base/annotations/RemovableInRelease.java +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - -/** - * The annotated function can be removed in release builds. - * - * Calls to this function will be removed if its return value is not used. If all calls are removed, - * the function definition itself will be candidate for removal. - * It works by indicating to Proguard that the function has no side effects. - */ -@Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) -public @interface RemovableInRelease {} diff --git a/android/java/src/org/chromium/base/annotations/UsedByReflection.java b/android/java/src/org/chromium/base/annotations/UsedByReflection.java deleted file mode 100644 index a2af704e0..000000000 --- a/android/java/src/org/chromium/base/annotations/UsedByReflection.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - -/** - * Annotation used for marking methods and fields that are called by reflection. - * Useful for keeping components that would otherwise be removed by Proguard. - * Use the value parameter to mention a file that calls this method. - * - * Note that adding this annotation to a method is not enough to guarantee that - * it is kept - either its class must be referenced elsewhere in the program, or - * the class must be annotated with this as well. - */ -@Target({ - ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, - ElementType.CONSTRUCTOR }) -public @interface UsedByReflection { - String value(); -} diff --git a/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/android/java/src/org/chromium/base/library_loader/LibraryLoader.java deleted file mode 100644 index 5bc62042d..000000000 --- a/android/java/src/org/chromium/base/library_loader/LibraryLoader.java +++ /dev/null @@ -1,829 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.library_loader; - -import static org.chromium.base.metrics.CachedMetrics.EnumeratedHistogramSample; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Build; -import android.os.Build.VERSION_CODES; -import android.os.Process; -import android.os.StrictMode; -import android.os.SystemClock; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; -import android.system.Os; - -import org.chromium.base.AsyncTask; -import org.chromium.base.BuildConfig; -import org.chromium.base.BuildInfo; -import org.chromium.base.CommandLine; -import org.chromium.base.ContextUtils; -import org.chromium.base.FileUtils; -import org.chromium.base.Log; -import org.chromium.base.SysUtils; -import org.chromium.base.TraceEvent; -import org.chromium.base.VisibleForTesting; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.MainDex; -import org.chromium.base.metrics.RecordHistogram; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.zip.ZipFile; - -import javax.annotation.Nullable; - -/** - * This class provides functionality to load and register the native libraries. - * Callers are allowed to separate loading the libraries from initializing them. - * This may be an advantage for Android Webview, where the libraries can be loaded - * by the zygote process, but then needs per process initialization after the - * application processes are forked from the zygote process. - * - * The libraries may be loaded and initialized from any thread. Synchronization - * primitives are used to ensure that overlapping requests from different - * threads are handled sequentially. - * - * See also base/android/library_loader/library_loader_hooks.cc, which contains - * the native counterpart to this class. - */ -@MainDex -@JNINamespace("base::android") -public class LibraryLoader { - private static final String TAG = "LibraryLoader"; - - // Set to true to enable debug logs. - private static final boolean DEBUG = false; - - // Experience shows that on some devices, the PackageManager fails to properly extract - // native shared libraries to the /data partition at installation or upgrade time, - // which creates all kind of chaos (https://crbug.com/806998). - // - // We implement a fallback when we detect the issue by manually extracting the library - // into Chromium's own data directory, then retrying to load the new library from here. - // - // This will work for any device running K-. Starting with Android L, render processes - // cannot access the file system anymore, and extraction will always fail for them. - // However, the issue doesn't seem to appear in the field for Android L. - // - // Also, starting with M, the issue doesn't exist if shared libraries are stored - // uncompressed in the APK (as Chromium does), because the system linker can access them - // directly, and the PackageManager will thus never extract them in the first place. - static public final boolean PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION = - Build.VERSION.SDK_INT <= VERSION_CODES.KITKAT; - - // Location of extracted native libraries. - private static final String LIBRARY_DIR = "native_libraries"; - - // SharedPreferences key for "don't prefetch libraries" flag - private static final String DONT_PREFETCH_LIBRARIES_KEY = "dont_prefetch_libraries"; - - private static final EnumeratedHistogramSample sRelinkerCountHistogram = - new EnumeratedHistogramSample("ChromiumAndroidLinker.RelinkerFallbackCount", 2); - - // The singleton instance of LibraryLoader. Never null (not final for tests). - private static LibraryLoader sInstance = new LibraryLoader(); - - // One-way switch becomes true when the libraries are initialized ( - // by calling nativeLibraryLoaded, which forwards to LibraryLoaded(...) in - // library_loader_hooks.cc). - // Note that this member should remain a one-way switch, since it accessed from multiple - // threads without a lock. - private volatile boolean mInitialized; - - // One-way switch that becomes true once - // {@link asyncPrefetchLibrariesToMemory} has been called. - private final AtomicBoolean mPrefetchLibraryHasBeenCalled = new AtomicBoolean(); - - // Guards all fields below. - private final Object mLock = new Object(); - - private NativeLibraryPreloader mLibraryPreloader; - private boolean mLibraryPreloaderCalled; - - // One-way switch becomes true when the libraries are loaded. - private boolean mLoaded; - - // One-way switch becomes true when the Java command line is switched to - // native. - private boolean mCommandLineSwitched; - - // One-way switches recording attempts to use Relro sharing in the browser. - // The flags are used to report UMA stats later. - private boolean mIsUsingBrowserSharedRelros; - private boolean mLoadAtFixedAddressFailed; - - // One-way switch becomes true if the Chromium library was loaded from the - // APK file directly. - private boolean mLibraryWasLoadedFromApk; - - // The type of process the shared library is loaded in. - private @LibraryProcessType int mLibraryProcessType; - - // The number of milliseconds it took to load all the native libraries, which - // will be reported via UMA. Set once when the libraries are done loading. - private long mLibraryLoadTimeMs; - - // The return value of NativeLibraryPreloader.loadLibrary(), which will be reported - // via UMA, it is initialized to the invalid value which shouldn't showup in UMA - // report. - private int mLibraryPreloaderStatus = -1; - - /** - * Call this method to determine if this chromium project must - * use this linker. If not, System.loadLibrary() should be used to load - * libraries instead. - */ - public static boolean useCrazyLinker() { - // TODO(digit): Remove this early return GVR is loadable. - // A non-monochrome APK (such as ChromePublic.apk or ChromeModernPublic.apk) on N+ cannot - // use the Linker because the latter is incompatible with the GVR library. Fall back - // to using System.loadLibrary() or System.load() at the cost of no RELRO sharing. - // - // A non-monochrome APK (such as ChromePublic.apk) can be installed on N+ in these - // circumstances: - // * installing APK manually - // * after OTA from M to N - // * side-installing Chrome (possibly from another release channel) - // * Play Store bugs leading to incorrect APK flavor being installed - // - if (Build.VERSION.SDK_INT >= VERSION_CODES.N) return false; - - // The auto-generated NativeLibraries.sUseLinker variable will be true if the - // build has not explicitly disabled Linker features. - return NativeLibraries.sUseLinker; - } - - /** - * Call this method to determine if the chromium project must load the library - * directly from a zip file. - */ - private static boolean isInZipFile() { - // The auto-generated NativeLibraries.sUseLibraryInZipFile variable will be true - // iff the library remains embedded in the APK zip file on the target. - return NativeLibraries.sUseLibraryInZipFile; - } - - /** - * Set native library preloader, if set, the NativeLibraryPreloader.loadLibrary will be invoked - * before calling System.loadLibrary, this only applies when not using the chromium linker. - * - * @param loader the NativeLibraryPreloader, it shall only be set once and before the - * native library loaded. - */ - public void setNativeLibraryPreloader(NativeLibraryPreloader loader) { - synchronized (mLock) { - assert mLibraryPreloader == null && !mLoaded; - mLibraryPreloader = loader; - } - } - - public static LibraryLoader getInstance() { - return sInstance; - } - - private LibraryLoader() {} - - /** - * This method blocks until the library is fully loaded and initialized. - * - * @param processType the process the shared library is loaded in. - */ - public void ensureInitialized(@LibraryProcessType int processType) throws ProcessInitException { - synchronized (mLock) { - if (mInitialized) { - // Already initialized, nothing to do. - return; - } - loadAlreadyLocked(ContextUtils.getApplicationContext()); - initializeAlreadyLocked(processType); - } - } - - /** - * Calls native library preloader (see {@link #setNativeLibraryPreloader}) with the app - * context. If there is no preloader set, this function does nothing. - * Preloader is called only once, so calling it explicitly via this method means - * that it won't be (implicitly) called during library loading. - */ - public void preloadNow() { - preloadNowOverrideApplicationContext(ContextUtils.getApplicationContext()); - } - - /** - * Similar to {@link #preloadNow}, but allows specifying app context to use. - */ - public void preloadNowOverrideApplicationContext(Context appContext) { - synchronized (mLock) { - if (!useCrazyLinker()) { - preloadAlreadyLocked(appContext); - } - } - } - - private void preloadAlreadyLocked(Context appContext) { - try (TraceEvent te = TraceEvent.scoped("LibraryLoader.preloadAlreadyLocked")) { - // Preloader uses system linker, we shouldn't preload if Chromium linker is used. - assert !useCrazyLinker(); - if (mLibraryPreloader != null && !mLibraryPreloaderCalled) { - mLibraryPreloaderStatus = mLibraryPreloader.loadLibrary(appContext); - mLibraryPreloaderCalled = true; - } - } - } - - /** - * Checks if library is fully loaded and initialized. - */ - public boolean isInitialized() { - return mInitialized; - } - - /** - * Loads the library and blocks until the load completes. The caller is responsible - * for subsequently calling ensureInitialized(). - * May be called on any thread, but should only be called once. Note the thread - * this is called on will be the thread that runs the native code's static initializers. - * See the comment in doInBackground() for more considerations on this. - * - * @throws ProcessInitException if the native library failed to load. - */ - public void loadNow() throws ProcessInitException { - loadNowOverrideApplicationContext(ContextUtils.getApplicationContext()); - } - - /** - * Override kept for callers that need to load from a different app context. Do not use unless - * specifically required to load from another context that is not the current process's app - * context. - * - * @param appContext The overriding app context to be used to load libraries. - * @throws ProcessInitException if the native library failed to load with this context. - */ - public void loadNowOverrideApplicationContext(Context appContext) throws ProcessInitException { - synchronized (mLock) { - if (mLoaded && appContext != ContextUtils.getApplicationContext()) { - throw new IllegalStateException("Attempt to load again from alternate context."); - } - loadAlreadyLocked(appContext); - } - } - - /** - * Initializes the library here and now: must be called on the thread that the - * native will call its "main" thread. The library must have previously been - * loaded with loadNow. - * - * @param processType the process the shared library is loaded in. - */ - public void initialize(@LibraryProcessType int processType) throws ProcessInitException { - synchronized (mLock) { - initializeAlreadyLocked(processType); - } - } - - /** - * Disables prefetching for subsequent runs. The value comes from "DontPrefetchLibraries" - * finch experiment, and is pushed on every run. I.e. the effect of the finch experiment - * lags by one run, which is the best we can do considering that prefetching happens way - * before finch is initialized. Note that since LibraryLoader is in //base, it can't depend - * on ChromeFeatureList, and has to rely on external code pushing the value. - * - * @param dontPrefetch whether not to prefetch libraries - */ - public static void setDontPrefetchLibrariesOnNextRuns(boolean dontPrefetch) { - ContextUtils.getAppSharedPreferences() - .edit() - .putBoolean(DONT_PREFETCH_LIBRARIES_KEY, dontPrefetch) - .apply(); - } - - /** - * @return whether not to prefetch libraries (see setDontPrefetchLibrariesOnNextRun()). - */ - private static boolean isNotPrefetchingLibraries() { - // This might be the first time getAppSharedPreferences() is used, so relax strict mode - // to allow disk reads. - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); - try { - return ContextUtils.getAppSharedPreferences().getBoolean( - DONT_PREFETCH_LIBRARIES_KEY, false); - } finally { - StrictMode.setThreadPolicy(oldPolicy); - } - } - - /** Prefetches the native libraries in a background thread. - * - * Launches an AsyncTask that, through a short-lived forked process, reads a - * part of each page of the native library. This is done to warm up the - * page cache, turning hard page faults into soft ones. - * - * This is done this way, as testing shows that fadvise(FADV_WILLNEED) is - * detrimental to the startup time. - */ - public void asyncPrefetchLibrariesToMemory() { - SysUtils.logPageFaultCountToTracing(); - if (isNotPrefetchingLibraries()) return; - - final boolean coldStart = mPrefetchLibraryHasBeenCalled.compareAndSet(false, true); - - // Collection should start close to the native library load, but doesn't have - // to be simultaneous with it. Also, don't prefetch in this case, as this would - // skew the results. - if (coldStart && CommandLine.getInstance().hasSwitch("log-native-library-residency")) { - // nativePeriodicallyCollectResidency() sleeps, run it on another thread, - // and not on the AsyncTask thread pool. - new Thread(LibraryLoader::nativePeriodicallyCollectResidency).start(); - return; - } - - new LibraryPrefetchTask(coldStart).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private static class LibraryPrefetchTask extends AsyncTask { - private final boolean mColdStart; - - public LibraryPrefetchTask(boolean coldStart) { - mColdStart = coldStart; - } - - @Override - protected Void doInBackground(Void... params) { - try (TraceEvent e = TraceEvent.scoped("LibraryLoader.asyncPrefetchLibrariesToMemory")) { - int percentage = nativePercentageOfResidentNativeLibraryCode(); - // Arbitrary percentage threshold. If most of the native library is already - // resident (likely with monochrome), don't bother creating a prefetch process. - boolean prefetch = mColdStart && percentage < 90; - if (prefetch) { - nativeForkAndPrefetchNativeLibrary(); - } - if (percentage != -1) { - String histogram = "LibraryLoader.PercentageOfResidentCodeBeforePrefetch" - + (mColdStart ? ".ColdStartup" : ".WarmStartup"); - RecordHistogram.recordPercentageHistogram(histogram, percentage); - } - } - return null; - } - } - - // Helper for loadAlreadyLocked(). Load a native shared library with the Chromium linker. - // Sets UMA flags depending on the results of loading. - private void loadLibraryWithCustomLinkerAlreadyLocked( - Linker linker, @Nullable String zipFilePath, String libFilePath) { - assert Thread.holdsLock(mLock); - if (linker.isUsingBrowserSharedRelros()) { - // If the browser is set to attempt shared RELROs then we try first with shared - // RELROs enabled, and if that fails then retry without. - mIsUsingBrowserSharedRelros = true; - try { - linker.loadLibrary(libFilePath); - } catch (UnsatisfiedLinkError e) { - Log.w(TAG, "Failed to load native library with shared RELRO, retrying without"); - mLoadAtFixedAddressFailed = true; - linker.loadLibraryNoFixedAddress(libFilePath); - } - } else { - // No attempt to use shared RELROs in the browser, so load as normal. - linker.loadLibrary(libFilePath); - } - - // Loaded successfully, so record if we loaded directly from an APK. - if (zipFilePath != null) { - mLibraryWasLoadedFromApk = true; - } - } - - static void incrementRelinkerCountHitHistogram() { - sRelinkerCountHistogram.record(1); - } - - static void incrementRelinkerCountNotHitHistogram() { - sRelinkerCountHistogram.record(0); - } - - // Experience shows that on some devices, the system sometimes fails to extract native libraries - // at installation or update time from the APK. This function will extract the library and - // return the extracted file path. - static String getExtractedLibraryPath(Context appContext, String libName) { - assert PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION; - Log.w(TAG, "Failed to load libName %s, attempting fallback extraction then trying again", - libName); - String libraryEntry = LibraryLoader.makeLibraryPathInZipFile(libName, false, false); - return extractFileIfStale(appContext, libraryEntry, makeLibraryDirAndSetPermission()); - } - - // Invoke either Linker.loadLibrary(...), System.loadLibrary(...) or System.load(...), - // triggering JNI_OnLoad in native code. - // TODO(crbug.com/635567): Fix this properly. - @SuppressLint({"DefaultLocale", "NewApi", "UnsafeDynamicallyLoadedCode"}) - private void loadAlreadyLocked(Context appContext) throws ProcessInitException { - try (TraceEvent te = TraceEvent.scoped("LibraryLoader.loadAlreadyLocked")) { - if (!mLoaded) { - assert !mInitialized; - - long startTime = SystemClock.uptimeMillis(); - - if (useCrazyLinker()) { - // Load libraries using the Chromium linker. - Linker linker = Linker.getInstance(); - - String apkFilePath = - isInZipFile() ? appContext.getApplicationInfo().sourceDir : null; - linker.prepareLibraryLoad(apkFilePath); - - for (String library : NativeLibraries.LIBRARIES) { - // Don't self-load the linker. This is because the build system is - // not clever enough to understand that all the libraries packaged - // in the final .apk don't need to be explicitly loaded. - if (linker.isChromiumLinkerLibrary(library)) { - if (DEBUG) Log.i(TAG, "ignoring self-linker load"); - continue; - } - - // Determine where the library should be loaded from. - String libFilePath = System.mapLibraryName(library); - if (apkFilePath != null) { - Log.i(TAG, " Loading " + library + " from within " + apkFilePath); - } else { - Log.i(TAG, "Loading " + library); - } - - try { - // Load the library using this Linker. May throw UnsatisfiedLinkError. - loadLibraryWithCustomLinkerAlreadyLocked( - linker, apkFilePath, libFilePath); - incrementRelinkerCountNotHitHistogram(); - } catch (UnsatisfiedLinkError e) { - if (!isInZipFile() - && PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) { - loadLibraryWithCustomLinkerAlreadyLocked( - linker, null, getExtractedLibraryPath(appContext, library)); - incrementRelinkerCountHitHistogram(); - } else { - Log.e(TAG, "Unable to load library: " + library); - throw(e); - } - } - } - - linker.finishLibraryLoad(); - } else { - setEnvForNative(); - preloadAlreadyLocked(appContext); - - // If the libraries are located in the zip file, assert that the device API - // level is M or higher. On devices lower than M, the libraries should - // always be loaded by Linker. - assert !isInZipFile() || Build.VERSION.SDK_INT >= VERSION_CODES.M; - - // Load libraries using the system linker. - for (String library : NativeLibraries.LIBRARIES) { - try { - if (!isInZipFile()) { - // The extract and retry logic isn't needed because this path is - // used only for local development. - System.loadLibrary(library); - } else { - // Load directly from the APK. - boolean is64Bit = Process.is64Bit(); - String zipFilePath = appContext.getApplicationInfo().sourceDir; - // In API level 23 and above, it’s possible to open a .so file - // directly from the APK of the path form - // "my_zip_file.zip!/libs/libstuff.so". See: - // https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md#opening-shared-libraries-directly-from-an-apk - String libraryName = zipFilePath + "!/" - + makeLibraryPathInZipFile(library, true, is64Bit); - Log.i(TAG, "libraryName: " + libraryName); - System.load(libraryName); - } - } catch (UnsatisfiedLinkError e) { - Log.e(TAG, "Unable to load library: " + library); - throw(e); - } - } - } - - long stopTime = SystemClock.uptimeMillis(); - mLibraryLoadTimeMs = stopTime - startTime; - Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)", - mLibraryLoadTimeMs, - startTime % 10000, - stopTime % 10000)); - - mLoaded = true; - } - } catch (UnsatisfiedLinkError e) { - throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e); - } - } - - /** - * @param library The library name that is looking for. - * @param crazyPrefix true iff adding crazy linker prefix to the file name. - * @param is64Bit true if the caller think it's run on a 64 bit device. - * @return the library path name in the zip file. - */ - @NonNull - public static String makeLibraryPathInZipFile( - String library, boolean crazyPrefix, boolean is64Bit) { - // Determine the ABI string that Android uses to find native libraries. Values are described - // in: https://developer.android.com/ndk/guides/abis.html - // The 'armeabi' is omitted here because it is not supported in Chrome/WebView, while Cronet - // and Cast load the native library via other paths. - String cpuAbi; - switch (NativeLibraries.sCpuFamily) { - case NativeLibraries.CPU_FAMILY_ARM: - cpuAbi = is64Bit ? "arm64-v8a" : "armeabi-v7a"; - break; - case NativeLibraries.CPU_FAMILY_X86: - cpuAbi = is64Bit ? "x86_64" : "x86"; - break; - case NativeLibraries.CPU_FAMILY_MIPS: - cpuAbi = is64Bit ? "mips64" : "mips"; - break; - default: - throw new RuntimeException("Unknown CPU ABI for native libraries"); - } - - // When both the Chromium linker and zip-uncompressed native libraries are used, - // the build system renames the native shared libraries with a 'crazy.' prefix - // (e.g. "/lib/armeabi-v7a/libfoo.so" -> "/lib/armeabi-v7a/crazy.libfoo.so"). - // - // This prevents the package manager from extracting them at installation/update time - // to the /data directory. The libraries can still be accessed directly by the Chromium - // linker from the APK. - String crazyPart = crazyPrefix ? "crazy." : ""; - return String.format("lib/%s/%s%s", cpuAbi, crazyPart, System.mapLibraryName(library)); - } - - // The WebView requires the Command Line to be switched over before - // initialization is done. This is okay in the WebView's case since the - // JNI is already loaded by this point. - public void switchCommandLineForWebView() { - synchronized (mLock) { - ensureCommandLineSwitchedAlreadyLocked(); - } - } - - // Switch the CommandLine over from Java to native if it hasn't already been done. - // This must happen after the code is loaded and after JNI is ready (since after the - // switch the Java CommandLine will delegate all calls the native CommandLine). - private void ensureCommandLineSwitchedAlreadyLocked() { - assert mLoaded; - if (mCommandLineSwitched) { - return; - } - CommandLine.enableNativeProxy(); - mCommandLineSwitched = true; - } - - // Invoke base::android::LibraryLoaded in library_loader_hooks.cc - private void initializeAlreadyLocked(@LibraryProcessType int processType) - throws ProcessInitException { - if (mInitialized) { - if (mLibraryProcessType != processType) { - throw new ProcessInitException( - LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED); - } - return; - } - mLibraryProcessType = processType; - - ensureCommandLineSwitchedAlreadyLocked(); - - if (!nativeLibraryLoaded(mLibraryProcessType)) { - Log.e(TAG, "error calling nativeLibraryLoaded"); - throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI); - } - - // Check that the version of the library we have loaded matches the version we expect - Log.i(TAG, String.format("Expected native library version number \"%s\", " - + "actual native library version number \"%s\"", - NativeLibraries.sVersionNumber, nativeGetVersionNumber())); - if (!NativeLibraries.sVersionNumber.equals(nativeGetVersionNumber())) { - throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION); - } - - // From now on, keep tracing in sync with native. - TraceEvent.registerNativeEnabledObserver(); - - if (processType == LibraryProcessType.PROCESS_BROWSER - && PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) { - // Perform the detection and deletion of obsolete native libraries on a background - // background thread. - AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { - @Override - public void run() { - final String suffix = BuildInfo.getInstance().extractedFileSuffix; - final File[] files = getLibraryDir().listFiles(); - if (files == null) return; - - for (File file : files) { - // NOTE: Do not simply look for at the end of the file. - // - // Extracted library files have names like 'libfoo.so', but - // extractFileIfStale() will use FileUtils.copyFileStreamAtomicWithBuffer() - // to create them, and this method actually uses a transient temporary file - // named like 'libfoo.so.tmp' to do that. These temporary files, if - // detected here, should be preserved; hence the reason why contains() is - // used below. - if (!file.getName().contains(suffix)) { - String fileName = file.getName(); - if (!file.delete()) { - Log.w(TAG, "Unable to remove %s", fileName); - } else { - Log.i(TAG, "Removed obsolete file %s", fileName); - } - } - } - } - }); - } - - // From this point on, native code is ready to use and checkIsReady() - // shouldn't complain from now on (and in fact, it's used by the - // following calls). - // Note that this flag can be accessed asynchronously, so any initialization - // must be performed before. - mInitialized = true; - } - - // Called after all native initializations are complete. - public void onNativeInitializationComplete() { - synchronized (mLock) { - recordBrowserProcessHistogramAlreadyLocked(); - } - } - - // Record Chromium linker histogram state for the main browser process. Called from - // onNativeInitializationComplete(). - private void recordBrowserProcessHistogramAlreadyLocked() { - assert Thread.holdsLock(mLock); - if (useCrazyLinker()) { - nativeRecordChromiumAndroidLinkerBrowserHistogram(mIsUsingBrowserSharedRelros, - mLoadAtFixedAddressFailed, - mLibraryWasLoadedFromApk ? LibraryLoadFromApkStatusCodes.SUCCESSFUL - : LibraryLoadFromApkStatusCodes.UNKNOWN, - mLibraryLoadTimeMs); - } - if (mLibraryPreloader != null) { - nativeRecordLibraryPreloaderBrowserHistogram(mLibraryPreloaderStatus); - } - } - - // Register pending Chromium linker histogram state for renderer processes. This cannot be - // recorded as a histogram immediately because histograms and IPC are not ready at the - // time it are captured. This function stores a pending value, so that a later call to - // RecordChromiumAndroidLinkerRendererHistogram() will record it correctly. - public void registerRendererProcessHistogram(boolean requestedSharedRelro, - boolean loadAtFixedAddressFailed) { - synchronized (mLock) { - if (useCrazyLinker()) { - nativeRegisterChromiumAndroidLinkerRendererHistogram( - requestedSharedRelro, loadAtFixedAddressFailed, mLibraryLoadTimeMs); - } - if (mLibraryPreloader != null) { - nativeRegisterLibraryPreloaderRendererHistogram(mLibraryPreloaderStatus); - } - } - } - - /** - * Override the library loader (normally with a mock) for testing. - * @param loader the mock library loader. - */ - @VisibleForTesting - public static void setLibraryLoaderForTesting(LibraryLoader loader) { - sInstance = loader; - } - - /** - * Configure ubsan using $UBSAN_OPTIONS. This function needs to be called before any native - * libraries are loaded because ubsan reads its configuration from $UBSAN_OPTIONS when the - * native library is loaded. - */ - public static void setEnvForNative() { - // The setenv API was added in L. On older versions of Android, we should still see ubsan - // reports, but they will not have stack traces. - if (BuildConfig.IS_UBSAN && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - try { - // This value is duplicated in build/android/pylib/constants/__init__.py. - Os.setenv("UBSAN_OPTIONS", - "print_stacktrace=1 stack_trace_format='#%n pc %o %m' " - + "handle_segv=0 handle_sigbus=0 handle_sigfpe=0", - true); - } catch (Exception e) { - Log.w(TAG, "failed to set UBSAN_OPTIONS", e); - } - } - } - - // Android system sometimes fails to extract libraries from APK (https://crbug.com/806998). - // This function manually extract libraries as a fallback. - @SuppressLint({"SetWorldReadable"}) - private static String extractFileIfStale( - Context appContext, String pathWithinApk, File destDir) { - assert PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION; - - String apkPath = appContext.getApplicationInfo().sourceDir; - String fileName = - (new File(pathWithinApk)).getName() + BuildInfo.getInstance().extractedFileSuffix; - File libraryFile = new File(destDir, fileName); - - if (!libraryFile.exists()) { - try (ZipFile zipFile = new ZipFile(apkPath); - InputStream inputStream = - zipFile.getInputStream(zipFile.getEntry(pathWithinApk))) { - if (zipFile.getEntry(pathWithinApk) == null) - throw new RuntimeException("Cannot find ZipEntry" + pathWithinApk); - - FileUtils.copyFileStreamAtomicWithBuffer( - inputStream, libraryFile, new byte[16 * 1024]); - libraryFile.setReadable(true, false); - libraryFile.setExecutable(true, false); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - return libraryFile.getAbsolutePath(); - } - - // Ensure the extracted native libraries is created with the right permissions. - private static File makeLibraryDirAndSetPermission() { - if (!ContextUtils.isIsolatedProcess()) { - File cacheDir = ContextCompat.getCodeCacheDir(ContextUtils.getApplicationContext()); - File libDir = new File(cacheDir, LIBRARY_DIR); - cacheDir.mkdir(); - cacheDir.setExecutable(true, false); - libDir.mkdir(); - libDir.setExecutable(true, false); - } - return getLibraryDir(); - } - - // Return File object for the directory containing extracted native libraries. - private static File getLibraryDir() { - return new File( - ContextCompat.getCodeCacheDir(ContextUtils.getApplicationContext()), LIBRARY_DIR); - } - - // Only methods needed before or during normal JNI registration are during System.OnLoad. - // nativeLibraryLoaded is then called to register everything else. This process is called - // "initialization". This method will be mapped (by generated code) to the LibraryLoaded - // definition in base/android/library_loader/library_loader_hooks.cc. - // - // Return true on success and false on failure. - private native boolean nativeLibraryLoaded(@LibraryProcessType int processType); - - // Method called to record statistics about the Chromium linker operation for the main - // browser process. Indicates whether the linker attempted relro sharing for the browser, - // and if it did, whether the library failed to load at a fixed address. Also records - // support for loading a library directly from the APK file, and the number of milliseconds - // it took to load the libraries. - private native void nativeRecordChromiumAndroidLinkerBrowserHistogram( - boolean isUsingBrowserSharedRelros, - boolean loadAtFixedAddressFailed, - int libraryLoadFromApkStatus, - long libraryLoadTime); - - // Method called to record the return value of NativeLibraryPreloader.loadLibrary for the main - // browser process. - private native void nativeRecordLibraryPreloaderBrowserHistogram(int status); - - // Method called to register (for later recording) statistics about the Chromium linker - // operation for a renderer process. Indicates whether the linker attempted relro sharing, - // and if it did, whether the library failed to load at a fixed address. Also records the - // number of milliseconds it took to load the libraries. - private native void nativeRegisterChromiumAndroidLinkerRendererHistogram( - boolean requestedSharedRelro, - boolean loadAtFixedAddressFailed, - long libraryLoadTime); - - // Method called to register (for later recording) the return value of - // NativeLibraryPreloader.loadLibrary for a renderer process. - private native void nativeRegisterLibraryPreloaderRendererHistogram(int status); - - // Get the version of the native library. This is needed so that we can check we - // have the right version before initializing the (rest of the) JNI. - private native String nativeGetVersionNumber(); - - // Finds the ranges corresponding to the native library pages, forks a new - // process to prefetch these pages and waits for it. The new process then - // terminates. This is blocking. - private static native void nativeForkAndPrefetchNativeLibrary(); - - // Returns the percentage of the native library code page that are currently reseident in - // memory. - private static native int nativePercentageOfResidentNativeLibraryCode(); - - // Periodically logs native library residency from this thread. - private static native void nativePeriodicallyCollectResidency(); -} diff --git a/android/java/src/org/chromium/base/library_loader/Linker.java b/android/java/src/org/chromium/base/library_loader/Linker.java deleted file mode 100644 index 5e30cfa49..000000000 --- a/android/java/src/org/chromium/base/library_loader/Linker.java +++ /dev/null @@ -1,1160 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.library_loader; - -import android.annotation.SuppressLint; -import android.os.Bundle; -import android.os.Parcel; -import android.os.ParcelFileDescriptor; -import android.os.Parcelable; - -import org.chromium.base.ContextUtils; -import org.chromium.base.Log; -import org.chromium.base.StreamUtil; -import org.chromium.base.SysUtils; -import org.chromium.base.ThreadUtils; -import org.chromium.base.annotations.AccessedByNative; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.MainDex; - -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import javax.annotation.Nullable; - -/* - * Technical note: - * - * The point of this class is to provide an alternative to System.loadLibrary() - * to load native shared libraries. One specific feature that it supports is the - * ability to save RAM by sharing the ELF RELRO sections between renderer - * processes. - * - * When two processes load the same native library at the _same_ memory address, - * the content of their RELRO section (which includes C++ vtables or any - * constants that contain pointers) will be largely identical [1]. - * - * By default, the RELRO section is backed by private RAM in each process, - * which is still significant on mobile (e.g. 1.28 MB / process on Chrome 30 for - * Android). - * - * However, it is possible to save RAM by creating a shared memory region, - * copy the RELRO content into it, then have each process swap its private, - * regular RELRO, with a shared, read-only, mapping of the shared one. - * - * This trick saves 98% of the RELRO section size per extra process, after the - * first one. On the other hand, this requires careful communication between - * the process where the shared RELRO is created and the one(s) where it is used. - * - * Note that swapping the regular RELRO with the shared one is not an atomic - * operation. Care must be taken that no other thread tries to run native code - * that accesses it during it. In practice, this means the swap must happen - * before library native code is executed. - * - * [1] The exceptions are pointers to external, randomized, symbols, like - * those from some system libraries, but these are very few in practice. - */ - -/* - * Security considerations: - * - * - Whether the browser process loads its native libraries at the same - * addresses as the service ones (to save RAM by sharing the RELRO too) - * depends on the configuration variable BROWSER_SHARED_RELRO_CONFIG. - * - * Not using fixed library addresses in the browser process is preferred - * for regular devices since it maintains the efficacy of ASLR as an - * exploit mitigation across the render <-> browser privilege boundary. - * - * - The shared RELRO memory region is always forced read-only after creation, - * which means it is impossible for a compromised service process to map - * it read-write (e.g. by calling mmap() or mprotect()) and modify its - * content, altering values seen in other service processes. - * - * - Once the RELRO ashmem region or file is mapped into a service process's - * address space, the corresponding file descriptor is immediately closed. The - * file descriptor is kept opened in the browser process, because a copy needs - * to be sent to each new potential service process. - * - * - The common library load addresses are randomized for each instance of - * the program on the device. See getRandomBaseLoadAddress() for more - * details on how this is obtained. - * - * - When loading several libraries in service processes, a simple incremental - * approach from the original random base load address is used. This is - * sufficient to deal correctly with component builds (which can use dozens - * of shared libraries), while regular builds always embed a single shared - * library per APK. - */ - -/** - * Here's an explanation of how this class is supposed to be used: - * - * - Native shared libraries should be loaded with Linker.loadLibrary(), - * instead of System.loadLibrary(). The two functions should behave the same - * (at a high level). - * - * - Before loading any library, prepareLibraryLoad() should be called. - * - * - After loading all libraries, finishLibraryLoad() should be called, before - * running any native code from any of the libraries (except their static - * constructors, which can't be avoided). - * - * - A service process shall call either initServiceProcess() or - * disableSharedRelros() early (i.e. before any loadLibrary() call). - * Otherwise, the linker considers that it is running inside the browser - * process. This is because various Chromium projects have vastly - * different initialization paths. - * - * disableSharedRelros() completely disables shared RELROs, and loadLibrary() - * will behave exactly like System.loadLibrary(). - * - * initServiceProcess(baseLoadAddress) indicates that shared RELROs are to be - * used in this process. - * - * - The browser is in charge of deciding where in memory each library should - * be loaded. This address must be passed to each service process (see - * ChromiumLinkerParams.java in content for a helper class to do so). - * - * - The browser will also generate shared RELROs for each library it loads. - * More specifically, by default when in the browser process, the linker - * will: - * - * - Load libraries randomly (just like System.loadLibrary()). - * - Compute the fixed address to be used to load the same library - * in service processes. - * - Create a shared memory region populated with the RELRO region - * content pre-relocated for the specific fixed address above. - * - * Note that these shared RELRO regions cannot be used inside the browser - * process. They are also never mapped into it. - * - * This behaviour is altered by the BROWSER_SHARED_RELRO_CONFIG configuration - * variable below, which may force the browser to load the libraries at - * fixed addresses too. - * - * - Once all libraries are loaded in the browser process, one can call - * getSharedRelros() which returns a Bundle instance containing a map that - * links each loaded library to its shared RELRO region. - * - * This Bundle must be passed to each service process, for example through - * a Binder call (note that the Bundle includes file descriptors and cannot - * be added as an Intent extra). - * - * - In a service process, finishLibraryLoad() and/or loadLibrary() may - * block until the RELRO section Bundle is received. This is typically - * done by calling useSharedRelros() from another thread. - * - * This method also ensures the process uses the shared RELROs. - */ -public class Linker { - // Log tag for this class. - private static final String TAG = "LibraryLoader"; - - // Name of the library that contains our JNI code. - private static final String LINKER_JNI_LIBRARY = "chromium_android_linker"; - - // Constants used to control the behaviour of the browser process with - // regards to the shared RELRO section. - // NEVER -> The browser never uses it itself. - // LOW_RAM_ONLY -> It is only used on devices with low RAM. - // ALWAYS -> It is always used. - // NOTE: These names are known and expected by the Linker test scripts. - public static final int BROWSER_SHARED_RELRO_CONFIG_NEVER = 0; - public static final int BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY = 1; - public static final int BROWSER_SHARED_RELRO_CONFIG_ALWAYS = 2; - - // Configuration variable used to control how the browser process uses the - // shared RELRO. Only change this while debugging linker-related issues. - // NOTE: This variable's name is known and expected by the Linker test scripts. - public static final int BROWSER_SHARED_RELRO_CONFIG = - BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY; - - // Constants used to control the memory device config. Can be set explicitly - // by setMemoryDeviceConfigForTesting(). - // INIT -> Value is undetermined (will check at runtime). - // LOW -> This is a low-memory device. - // NORMAL -> This is not a low-memory device. - public static final int MEMORY_DEVICE_CONFIG_INIT = 0; - public static final int MEMORY_DEVICE_CONFIG_LOW = 1; - public static final int MEMORY_DEVICE_CONFIG_NORMAL = 2; - - // Indicates if this is a low-memory device or not. The default is to - // determine this by probing the system at runtime, but this can be forced - // for testing by calling setMemoryDeviceConfigForTesting(). - private int mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_INIT; - - // Set to true to enable debug logs. - protected static final boolean DEBUG = false; - - // Used to pass the shared RELRO Bundle through Binder. - public static final String EXTRA_LINKER_SHARED_RELROS = - "org.chromium.base.android.linker.shared_relros"; - - // Guards all access to the linker. - protected final Object mLock = new Object(); - - // The name of a class that implements TestRunner. - private String mTestRunnerClassName; - - // Size of reserved Breakpad guard region. Should match the value of - // kBreakpadGuardRegionBytes on the JNI side. Used when computing the load - // addresses of multiple loaded libraries. Set to 0 to disable the guard. - private static final int BREAKPAD_GUARD_REGION_BYTES = 16 * 1024 * 1024; - - // Size of the area requested when using ASLR to obtain a random load address. - // Should match the value of kAddressSpaceReservationSize on the JNI side. - // Used when computing the load addresses of multiple loaded libraries to - // ensure that we don't try to load outside the area originally requested. - private static final int ADDRESS_SPACE_RESERVATION = 192 * 1024 * 1024; - - // Becomes true after linker initialization. - private boolean mInitialized; - - // Set to true if this runs in the browser process. Disabled by initServiceProcess(). - private boolean mInBrowserProcess = true; - - // Becomes true to indicate this process needs to wait for a shared RELRO in - // finishLibraryLoad(). - private boolean mWaitForSharedRelros; - - // Becomes true when initialization determines that the browser process can use the - // shared RELRO. - private boolean mBrowserUsesSharedRelro; - - // The map of all RELRO sections either created or used in this process. - private Bundle mSharedRelros; - - // Current common random base load address. A value of -1 indicates not yet initialized. - private long mBaseLoadAddress = -1; - - // Current fixed-location load address for the next library called by loadLibrary(). - // A value of -1 indicates not yet initialized. - private long mCurrentLoadAddress = -1; - - // Becomes true once prepareLibraryLoad() has been called. - private boolean mPrepareLibraryLoadCalled; - - // The map of libraries that are currently loaded in this process. - private HashMap mLoadedLibraries; - - // Singleton. - private static final Linker sSingleton = new Linker(); - - // Private singleton constructor. - private Linker() { - // Ensure this class is not referenced unless it's used. - assert LibraryLoader.useCrazyLinker(); - } - - /** - * Get singleton instance. Returns a Linker. - * - * On N+ Monochrome is selected by Play Store. With Monochrome this code is not used, instead - * Chrome asks the WebView to provide the library (and the shared RELRO). If the WebView fails - * to provide the library, the system linker is used as a fallback. - * - * Linker runs on all Android releases, but is incompatible with GVR library on N+. - * Linker is preferred on M- because it does not write the shared RELRO to disk at - * almost every cold startup. - * - * @return the Linker implementation instance. - */ - public static Linker getInstance() { - return sSingleton; - } - - /** - * Check that native library linker tests are enabled. - * If not enabled, calls to testing functions will fail with an assertion - * error. - * - * @return true if native library linker tests are enabled. - */ - public static boolean areTestsEnabled() { - return NativeLibraries.sEnableLinkerTests; - } - - /** - * Assert NativeLibraries.sEnableLinkerTests is true. - * Hard assertion that we are in a testing context. Cannot be disabled. The - * test methods in this module permit injection of runnable code by class - * name. To protect against both malicious and accidental use of these - * methods, we ensure that NativeLibraries.sEnableLinkerTests is true when - * any is called. - */ - private static void assertLinkerTestsAreEnabled() { - assert NativeLibraries.sEnableLinkerTests : "Testing method called in non-testing context"; - } - - /** - * A public interface used to run runtime linker tests after loading - * libraries. Should only be used to implement the linker unit tests, - * which is controlled by the value of NativeLibraries.sEnableLinkerTests - * configured at build time. - */ - public interface TestRunner { - /** - * Run runtime checks and return true if they all pass. - * - * @param memoryDeviceConfig The current memory device configuration. - * @param inBrowserProcess true iff this is the browser process. - * @return true if all checks pass. - */ - public boolean runChecks(int memoryDeviceConfig, boolean inBrowserProcess); - } - - /** - * Call this to retrieve the name of the current TestRunner class name - * if any. This can be useful to pass it from the browser process to - * child ones. - * - * @return null or a String holding the name of the class implementing - * the TestRunner set by calling setTestRunnerClassNameForTesting() previously. - */ - public final String getTestRunnerClassNameForTesting() { - // Sanity check. This method may only be called during tests. - assertLinkerTestsAreEnabled(); - - synchronized (mLock) { - return mTestRunnerClassName; - } - } - - /** - * Sets the test class name. - * - * On the first call, instantiates a Linker and sets its test runner class name. On subsequent - * calls, checks that the singleton produced by the first call matches the test runner class - * name. - */ - public static final void setupForTesting(String testRunnerClassName) { - if (DEBUG) { - Log.i(TAG, "setupForTesting(" + testRunnerClassName + ") called"); - } - // Sanity check. This method may only be called during tests. - assertLinkerTestsAreEnabled(); - - synchronized (sSingleton) { - sSingleton.mTestRunnerClassName = testRunnerClassName; - } - } - - /** - * Instantiate and run the current TestRunner, if any. The TestRunner implementation - * must be instantiated _after_ all libraries are loaded to ensure that its - * native methods are properly registered. - * - * @param memoryDeviceConfig Linker memory config, or 0 if unused - * @param inBrowserProcess true if in the browser process - */ - private final void runTestRunnerClassForTesting( - int memoryDeviceConfig, boolean inBrowserProcess) { - if (DEBUG) { - Log.i(TAG, "runTestRunnerClassForTesting called"); - } - // Sanity check. This method may only be called during tests. - assertLinkerTestsAreEnabled(); - - synchronized (mLock) { - if (mTestRunnerClassName == null) { - Log.wtf(TAG, "Linker runtime tests not set up for this process"); - assert false; - } - if (DEBUG) { - Log.i(TAG, "Instantiating " + mTestRunnerClassName); - } - TestRunner testRunner = null; - try { - testRunner = (TestRunner) Class.forName(mTestRunnerClassName) - .getDeclaredConstructor() - .newInstance(); - } catch (Exception e) { - Log.wtf(TAG, "Could not instantiate test runner class by name", e); - assert false; - } - - if (!testRunner.runChecks(memoryDeviceConfig, inBrowserProcess)) { - Log.wtf(TAG, "Linker runtime tests failed in this process"); - assert false; - } - - Log.i(TAG, "All linker tests passed"); - } - } - - /** - * Call this method before any other Linker method to force a specific - * memory device configuration. Should only be used for testing. - * - * @param memoryDeviceConfig MEMORY_DEVICE_CONFIG_LOW or MEMORY_DEVICE_CONFIG_NORMAL. - */ - public final void setMemoryDeviceConfigForTesting(int memoryDeviceConfig) { - if (DEBUG) { - Log.i(TAG, "setMemoryDeviceConfigForTesting(" + memoryDeviceConfig + ") called"); - } - // Sanity check. This method may only be called during tests. - assertLinkerTestsAreEnabled(); - assert memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW - || memoryDeviceConfig == MEMORY_DEVICE_CONFIG_NORMAL; - - synchronized (mLock) { - assert mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT; - - mMemoryDeviceConfig = memoryDeviceConfig; - if (DEBUG) { - if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) { - Log.i(TAG, "Simulating a low-memory device"); - } else { - Log.i(TAG, "Simulating a regular-memory device"); - } - } - } - } - - /** - * Determine whether a library is the linker library. - * - * @param library the name of the library. - * @return true is the library is the Linker's own JNI library. - */ - boolean isChromiumLinkerLibrary(String library) { - return library.equals(LINKER_JNI_LIBRARY); - } - - /** - * Load the Linker JNI library. Throws UnsatisfiedLinkError on error. - */ - @SuppressLint({"UnsafeDynamicallyLoadedCode"}) - private static void loadLinkerJniLibrary() { - LibraryLoader.setEnvForNative(); - if (DEBUG) { - String libName = "lib" + LINKER_JNI_LIBRARY + ".so"; - Log.i(TAG, "Loading " + libName); - } - try { - System.loadLibrary(LINKER_JNI_LIBRARY); - LibraryLoader.incrementRelinkerCountNotHitHistogram(); - } catch (UnsatisfiedLinkError e) { - if (LibraryLoader.PLATFORM_REQUIRES_NATIVE_FALLBACK_EXTRACTION) { - System.load(LibraryLoader.getExtractedLibraryPath( - ContextUtils.getApplicationContext(), LINKER_JNI_LIBRARY)); - LibraryLoader.incrementRelinkerCountHitHistogram(); - } - } - } - - /** - * Obtain a random base load address at which to place loaded libraries. - * - * @return new base load address - */ - private long getRandomBaseLoadAddress() { - // nativeGetRandomBaseLoadAddress() returns an address at which it has previously - // successfully mapped an area larger than the largest library we expect to load, - // on the basis that we will be able, with high probability, to map our library - // into it. - // - // One issue with this is that we do not yet know the size of the library that - // we will load is. If it is smaller than the size we used to obtain a random - // address the library mapping may still succeed. The other issue is that - // although highly unlikely, there is no guarantee that something else does not - // map into the area we are going to use between here and when we try to map into it. - // - // The above notes mean that all of this is probablistic. It is however okay to do - // because if, worst case and unlikely, we get unlucky in our choice of address, - // the back-out and retry without the shared RELRO in the ChildProcessService will - // keep things running. - final long address = nativeGetRandomBaseLoadAddress(); - if (DEBUG) { - Log.i(TAG, String.format(Locale.US, "Random native base load address: 0x%x", address)); - } - return address; - } - - /** - * Load a native shared library with the Chromium linker. Note the crazy linker treats - * libraries and files as equivalent, so you can only open one library in a given zip - * file. The library must not be the Chromium linker library. - * - * @param libFilePath The path of the library (possibly in the zip file). - */ - void loadLibrary(String libFilePath) { - if (DEBUG) { - Log.i(TAG, "loadLibrary: " + libFilePath); - } - final boolean isFixedAddressPermitted = true; - loadLibraryImpl(libFilePath, isFixedAddressPermitted); - } - - /** - * Load a native shared library with the Chromium linker, ignoring any - * requested fixed address for RELRO sharing. Note the crazy linker treats libraries and - * files as equivalent, so you can only open one library in a given zip file. The - * library must not be the Chromium linker library. - * - * @param libFilePath The path of the library (possibly in the zip file). - */ - void loadLibraryNoFixedAddress(String libFilePath) { - if (DEBUG) { - Log.i(TAG, "loadLibraryAtAnyAddress: " + libFilePath); - } - final boolean isFixedAddressPermitted = false; - loadLibraryImpl(libFilePath, isFixedAddressPermitted); - } - - // Used internally to initialize the linker's data. Assumes lock is held. - // Loads JNI, and sets mMemoryDeviceConfig and mBrowserUsesSharedRelro. - private void ensureInitializedLocked() { - assert Thread.holdsLock(mLock); - - if (mInitialized) { - return; - } - - // On first call, load libchromium_android_linker.so. Cannot be done in the - // constructor because instantiation occurs on the UI thread. - loadLinkerJniLibrary(); - - if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) { - if (SysUtils.isLowEndDevice()) { - mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_LOW; - } else { - mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_NORMAL; - } - } - - // Cannot run in the constructor because SysUtils.isLowEndDevice() relies - // on CommandLine, which may not be available at instantiation. - switch (BROWSER_SHARED_RELRO_CONFIG) { - case BROWSER_SHARED_RELRO_CONFIG_NEVER: - mBrowserUsesSharedRelro = false; - break; - case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY: - if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) { - mBrowserUsesSharedRelro = true; - Log.w(TAG, "Low-memory device: shared RELROs used in all processes"); - } else { - mBrowserUsesSharedRelro = false; - } - break; - case BROWSER_SHARED_RELRO_CONFIG_ALWAYS: - Log.w(TAG, "Beware: shared RELROs used in all processes!"); - mBrowserUsesSharedRelro = true; - break; - default: - Log.wtf(TAG, "FATAL: illegal shared RELRO config"); - throw new AssertionError(); - } - - mInitialized = true; - } - - /** - * Call this method to determine if the linker will try to use shared RELROs - * for the browser process. - */ - public boolean isUsingBrowserSharedRelros() { - synchronized (mLock) { - ensureInitializedLocked(); - return mInBrowserProcess && mBrowserUsesSharedRelro; - } - } - - /** - * Call this method just before loading any native shared libraries in this process. - * - * @param apkFilePath Optional current APK file path. If provided, the linker - * will try to load libraries directly from it. - */ - public void prepareLibraryLoad(@Nullable String apkFilePath) { - if (DEBUG) { - Log.i(TAG, "prepareLibraryLoad() called"); - } - synchronized (mLock) { - ensureInitializedLocked(); - if (apkFilePath != null) { - nativeAddZipArchivePath(apkFilePath); - } - mPrepareLibraryLoadCalled = true; - - if (mInBrowserProcess) { - // Force generation of random base load address, as well - // as creation of shared RELRO sections in this process. - setupBaseLoadAddressLocked(); - } - } - } - - /** - * Call this method just after loading all native shared libraries in this process. - * Note that when in a service process, this will block until the RELRO bundle is - * received, i.e. when another thread calls useSharedRelros(). - */ - void finishLibraryLoad() { - if (DEBUG) { - Log.i(TAG, "finishLibraryLoad() called"); - } - synchronized (mLock) { - ensureInitializedLocked(); - if (DEBUG) { - Log.i(TAG, - String.format(Locale.US, - "mInBrowserProcess=%b mBrowserUsesSharedRelro=%b mWaitForSharedRelros=%b", - mInBrowserProcess, mBrowserUsesSharedRelro, mWaitForSharedRelros)); - } - - if (mLoadedLibraries == null) { - if (DEBUG) { - Log.i(TAG, "No libraries loaded"); - } - } else { - if (mInBrowserProcess) { - // Create new Bundle containing RELRO section information - // for all loaded libraries. Make it available to getSharedRelros(). - mSharedRelros = createBundleFromLibInfoMap(mLoadedLibraries); - if (DEBUG) { - Log.i(TAG, "Shared RELRO created"); - dumpBundle(mSharedRelros); - } - - if (mBrowserUsesSharedRelro) { - useSharedRelrosLocked(mSharedRelros); - } - } - - if (mWaitForSharedRelros) { - assert !mInBrowserProcess; - - // Wait until the shared relro bundle is received from useSharedRelros(). - while (mSharedRelros == null) { - try { - mLock.wait(); - } catch (InterruptedException ie) { - // Restore the thread's interrupt status. - Thread.currentThread().interrupt(); - } - } - useSharedRelrosLocked(mSharedRelros); - // Clear the Bundle to ensure its file descriptor references can't be reused. - mSharedRelros.clear(); - mSharedRelros = null; - } - } - - // If testing, run tests now that all libraries are loaded and initialized. - if (NativeLibraries.sEnableLinkerTests) { - runTestRunnerClassForTesting(mMemoryDeviceConfig, mInBrowserProcess); - } - } - if (DEBUG) { - Log.i(TAG, "finishLibraryLoad() exiting"); - } - } - - /** - * Call this to send a Bundle containing the shared RELRO sections to be - * used in this process. If initServiceProcess() was previously called, - * finishLibraryLoad() will not exit until this method is called in another - * thread with a non-null value. - * - * @param bundle The Bundle instance containing a map of shared RELRO sections - * to use in this process. - */ - public void useSharedRelros(Bundle bundle) { - // Ensure the bundle uses the application's class loader, not the framework - // one which doesn't know anything about LibInfo. - // Also, hold a fresh copy of it so the caller can't recycle it. - Bundle clonedBundle = null; - if (bundle != null) { - bundle.setClassLoader(LibInfo.class.getClassLoader()); - clonedBundle = new Bundle(LibInfo.class.getClassLoader()); - Parcel parcel = Parcel.obtain(); - bundle.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - clonedBundle.readFromParcel(parcel); - parcel.recycle(); - } - if (DEBUG) { - Log.i(TAG, "useSharedRelros() called with " + bundle + ", cloned " + clonedBundle); - } - synchronized (mLock) { - // Note that in certain cases, this can be called before - // initServiceProcess() in service processes. - mSharedRelros = clonedBundle; - // Tell any listener blocked in finishLibraryLoad() about it. - mLock.notifyAll(); - } - } - - /** - * Call this to retrieve the shared RELRO sections created in this process, - * after loading all libraries. - * - * @return a new Bundle instance, or null if RELRO sharing is disabled on - * this system, or if initServiceProcess() was called previously. - */ - public Bundle getSharedRelros() { - if (DEBUG) { - Log.i(TAG, "getSharedRelros() called"); - } - synchronized (mLock) { - if (!mInBrowserProcess) { - if (DEBUG) { - Log.i(TAG, "... returning null Bundle"); - } - return null; - } - - // Return the Bundle created in finishLibraryLoad(). - if (DEBUG) { - Log.i(TAG, "... returning " + mSharedRelros); - } - return mSharedRelros; - } - } - - /** - * Call this method before loading any libraries to indicate that this - * process shall neither create or reuse shared RELRO sections. - */ - public void disableSharedRelros() { - if (DEBUG) { - Log.i(TAG, "disableSharedRelros() called"); - } - synchronized (mLock) { - ensureInitializedLocked(); - mInBrowserProcess = false; - mWaitForSharedRelros = false; - mBrowserUsesSharedRelro = false; - } - } - - /** - * Call this method before loading any libraries to indicate that this - * process is ready to reuse shared RELRO sections from another one. - * Typically used when starting service processes. - * - * @param baseLoadAddress the base library load address to use. - */ - public void initServiceProcess(long baseLoadAddress) { - if (DEBUG) { - Log.i(TAG, - String.format(Locale.US, "initServiceProcess(0x%x) called", baseLoadAddress)); - } - synchronized (mLock) { - ensureInitializedLocked(); - mInBrowserProcess = false; - mBrowserUsesSharedRelro = false; - mWaitForSharedRelros = true; - mBaseLoadAddress = baseLoadAddress; - mCurrentLoadAddress = baseLoadAddress; - } - } - - /** - * Retrieve the base load address of all shared RELRO sections. - * This also enforces the creation of shared RELRO sections in - * prepareLibraryLoad(), which can later be retrieved with getSharedRelros(). - * - * @return a common, random base load address, or 0 if RELRO sharing is - * disabled. - */ - public long getBaseLoadAddress() { - synchronized (mLock) { - ensureInitializedLocked(); - if (!mInBrowserProcess) { - Log.w(TAG, "Shared RELRO sections are disabled in this process!"); - return 0; - } - - setupBaseLoadAddressLocked(); - if (DEBUG) { - Log.i(TAG, - String.format( - Locale.US, "getBaseLoadAddress() returns 0x%x", mBaseLoadAddress)); - } - return mBaseLoadAddress; - } - } - - // Used internally to lazily setup the common random base load address. - private void setupBaseLoadAddressLocked() { - assert Thread.holdsLock(mLock); - if (mBaseLoadAddress == -1) { - mBaseLoadAddress = getRandomBaseLoadAddress(); - mCurrentLoadAddress = mBaseLoadAddress; - if (mBaseLoadAddress == 0) { - // If the random address is 0 there are issues with finding enough - // free address space, so disable RELRO shared / fixed load addresses. - Log.w(TAG, "Disabling shared RELROs due address space pressure"); - mBrowserUsesSharedRelro = false; - mWaitForSharedRelros = false; - } - } - } - - // Used for debugging only. - private void dumpBundle(Bundle bundle) { - if (DEBUG) { - Log.i(TAG, "Bundle has " + bundle.size() + " items: " + bundle); - } - } - - /** - * Use the shared RELRO section from a Bundle received form another process. - * Call this after calling setBaseLoadAddress() then loading all libraries - * with loadLibrary(). - * - * @param bundle Bundle instance generated with createSharedRelroBundle() in - * another process. - */ - private void useSharedRelrosLocked(Bundle bundle) { - assert Thread.holdsLock(mLock); - - if (DEBUG) { - Log.i(TAG, "Linker.useSharedRelrosLocked() called"); - } - - if (bundle == null) { - if (DEBUG) { - Log.i(TAG, "null bundle!"); - } - return; - } - - if (mLoadedLibraries == null) { - if (DEBUG) { - Log.i(TAG, "No libraries loaded!"); - } - return; - } - - if (DEBUG) { - dumpBundle(bundle); - } - HashMap relroMap = createLibInfoMapFromBundle(bundle); - - // Apply the RELRO section to all libraries that were already loaded. - for (Map.Entry entry : relroMap.entrySet()) { - String libName = entry.getKey(); - LibInfo libInfo = entry.getValue(); - if (!nativeUseSharedRelro(libName, libInfo)) { - Log.w(TAG, "Could not use shared RELRO section for " + libName); - } else { - if (DEBUG) { - Log.i(TAG, "Using shared RELRO section for " + libName); - } - } - } - - // In service processes, close all file descriptors from the map now. - if (!mInBrowserProcess) { - closeLibInfoMap(relroMap); - } - - if (DEBUG) { - Log.i(TAG, "Linker.useSharedRelrosLocked() exiting"); - } - } - - /** - * Implements loading a native shared library with the Chromium linker. - * - * Load a native shared library with the Chromium linker. If the zip file - * is not null, the shared library must be uncompressed and page aligned - * inside the zipfile. Note the crazy linker treats libraries and files as - * equivalent, so you can only open one library in a given zip file. The - * library must not be the Chromium linker library. - * - * @param libFilePath The path of the library (possibly in the zip file). - * @param isFixedAddressPermitted If true, uses a fixed load address if one was - * supplied, otherwise ignores the fixed address and loads wherever available. - */ - void loadLibraryImpl(String libFilePath, boolean isFixedAddressPermitted) { - if (DEBUG) { - Log.i(TAG, "loadLibraryImpl: " + libFilePath + ", " + isFixedAddressPermitted); - } - synchronized (mLock) { - ensureInitializedLocked(); - - // Security: Ensure prepareLibraryLoad() was called before. - // In theory, this can be done lazily here, but it's more consistent - // to use a pair of functions (i.e. prepareLibraryLoad() + finishLibraryLoad()) - // that wrap all calls to loadLibrary() in the library loader. - assert mPrepareLibraryLoadCalled; - - if (mLoadedLibraries == null) { - mLoadedLibraries = new HashMap(); - } - - if (mLoadedLibraries.containsKey(libFilePath)) { - if (DEBUG) { - Log.i(TAG, "Not loading " + libFilePath + " twice"); - } - return; - } - - LibInfo libInfo = new LibInfo(); - long loadAddress = 0; - if (isFixedAddressPermitted) { - if ((mInBrowserProcess && mBrowserUsesSharedRelro) || mWaitForSharedRelros) { - // Load the library at a fixed address. - loadAddress = mCurrentLoadAddress; - - // For multiple libraries, ensure we stay within reservation range. - if (loadAddress > mBaseLoadAddress + ADDRESS_SPACE_RESERVATION) { - String errorMessage = - "Load address outside reservation, for: " + libFilePath; - Log.e(TAG, errorMessage); - throw new UnsatisfiedLinkError(errorMessage); - } - } - } - - final String sharedRelRoName = libFilePath; - if (!nativeLoadLibrary(libFilePath, loadAddress, libInfo)) { - String errorMessage = "Unable to load library: " + libFilePath; - Log.e(TAG, errorMessage); - throw new UnsatisfiedLinkError(errorMessage); - } - - // Print the load address to the logcat when testing the linker. The format - // of the string is expected by the Python test_runner script as one of: - // BROWSER_LIBRARY_ADDRESS:

- // RENDERER_LIBRARY_ADDRESS:
- // Where is the library name, and
is the hexadecimal load - // address. - if (NativeLibraries.sEnableLinkerTests) { - String tag = - mInBrowserProcess ? "BROWSER_LIBRARY_ADDRESS" : "RENDERER_LIBRARY_ADDRESS"; - Log.i(TAG, - String.format( - Locale.US, "%s: %s %x", tag, libFilePath, libInfo.mLoadAddress)); - } - - if (mInBrowserProcess) { - // Create a new shared RELRO section at the 'current' fixed load address. - if (!nativeCreateSharedRelro(sharedRelRoName, mCurrentLoadAddress, libInfo)) { - Log.w(TAG, - String.format(Locale.US, "Could not create shared RELRO for %s at %x", - libFilePath, mCurrentLoadAddress)); - } else { - if (DEBUG) { - Log.i(TAG, - String.format(Locale.US, "Created shared RELRO for %s at %x: %s", - sharedRelRoName, mCurrentLoadAddress, libInfo.toString())); - } - } - } - - if (loadAddress != 0 && mCurrentLoadAddress != 0) { - // Compute the next current load address. If mCurrentLoadAddress - // is not 0, this is an explicit library load address. Otherwise, - // this is an explicit load address for relocated RELRO sections - // only. - mCurrentLoadAddress = - libInfo.mLoadAddress + libInfo.mLoadSize + BREAKPAD_GUARD_REGION_BYTES; - } - - mLoadedLibraries.put(sharedRelRoName, libInfo); - if (DEBUG) { - Log.i(TAG, "Library details " + libInfo.toString()); - } - } - } - - /** - * Record information for a given library. - * IMPORTANT: Native code knows about this class's fields, so - * don't change them without modifying the corresponding C++ sources. - * Also, the LibInfo instance owns the shared RELRO file descriptor. - */ - private static class LibInfo implements Parcelable { - LibInfo() {} - - // from Parcelable - LibInfo(Parcel in) { - mLoadAddress = in.readLong(); - mLoadSize = in.readLong(); - mRelroStart = in.readLong(); - mRelroSize = in.readLong(); - ParcelFileDescriptor fd = ParcelFileDescriptor.CREATOR.createFromParcel(in); - // If CreateSharedRelro fails, the OS file descriptor will be -1 and |fd| will be null. - if (fd != null) { - mRelroFd = fd.detachFd(); - } - } - - public void close() { - if (mRelroFd >= 0) { - StreamUtil.closeQuietly(ParcelFileDescriptor.adoptFd(mRelroFd)); - mRelroFd = -1; - } - } - - // from Parcelable - @Override - public void writeToParcel(Parcel out, int flags) { - if (mRelroFd >= 0) { - out.writeLong(mLoadAddress); - out.writeLong(mLoadSize); - out.writeLong(mRelroStart); - out.writeLong(mRelroSize); - try { - ParcelFileDescriptor fd = ParcelFileDescriptor.fromFd(mRelroFd); - fd.writeToParcel(out, 0); - fd.close(); - } catch (java.io.IOException e) { - Log.e(TAG, "Can't write LibInfo file descriptor to parcel", e); - } - } - } - - // from Parcelable - @Override - public int describeContents() { - return Parcelable.CONTENTS_FILE_DESCRIPTOR; - } - - // from Parcelable - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public LibInfo createFromParcel(Parcel in) { - return new LibInfo(in); - } - - @Override - public LibInfo[] newArray(int size) { - return new LibInfo[size]; - } - }; - - // IMPORTANT: Don't change these fields without modifying the - // native code that accesses them directly! - @AccessedByNative - public long mLoadAddress; // page-aligned library load address. - @AccessedByNative - public long mLoadSize; // page-aligned library load size. - @AccessedByNative - public long mRelroStart; // page-aligned address in memory, or 0 if none. - @AccessedByNative - public long mRelroSize; // page-aligned size in memory, or 0. - @AccessedByNative - public int mRelroFd = -1; // shared RELRO file descriptor, or -1 - } - - // Create a Bundle from a map of LibInfo objects. - private Bundle createBundleFromLibInfoMap(HashMap map) { - Bundle bundle = new Bundle(map.size()); - for (Map.Entry entry : map.entrySet()) { - bundle.putParcelable(entry.getKey(), entry.getValue()); - } - return bundle; - } - - // Create a new LibInfo map from a Bundle. - private HashMap createLibInfoMapFromBundle(Bundle bundle) { - HashMap map = new HashMap(); - for (String library : bundle.keySet()) { - LibInfo libInfo = bundle.getParcelable(library); - map.put(library, libInfo); - } - return map; - } - - // Call the close() method on all values of a LibInfo map. - private void closeLibInfoMap(HashMap map) { - for (Map.Entry entry : map.entrySet()) { - entry.getValue().close(); - } - } - - /** - * Move activity from the native thread to the main UI thread. - * Called from native code on its own thread. Posts a callback from - * the UI thread back to native code. - * - * @param opaque Opaque argument. - */ - @CalledByNative - @MainDex - private static void postCallbackOnMainThread(final long opaque) { - ThreadUtils.postOnUiThread(new Runnable() { - @Override - public void run() { - nativeRunCallbackOnUiThread(opaque); - } - }); - } - - /** - * Native method to run callbacks on the main UI thread. - * Supplied by the crazy linker and called by postCallbackOnMainThread. - * - * @param opaque Opaque crazy linker arguments. - */ - private static native void nativeRunCallbackOnUiThread(long opaque); - - /** - * Native method used to load a library. - * - * @param library Platform specific library name (e.g. libfoo.so) - * @param loadAddress Explicit load address, or 0 for randomized one. - * @param libInfo If not null, the mLoadAddress and mLoadSize fields - * of this LibInfo instance will set on success. - * @return true for success, false otherwise. - */ - private static native boolean nativeLoadLibrary( - String library, long loadAddress, LibInfo libInfo); - - /** - * Native method used to add a zip archive or APK to the search path - * for native libraries. Allows loading directly from it. - * - * @param zipfilePath Path of the zip file containing the libraries. - * @return true for success, false otherwise. - */ - private static native boolean nativeAddZipArchivePath(String zipFilePath); - - /** - * Native method used to create a shared RELRO section. - * If the library was already loaded at the same address using - * nativeLoadLibrary(), this creates the RELRO for it. Otherwise, - * this loads a new temporary library at the specified address, - * creates and extracts the RELRO section from it, then unloads it. - * - * @param library Library name. - * @param loadAddress load address, which can be different from the one - * used to load the library in the current process! - * @param libInfo libInfo instance. On success, the mRelroStart, mRelroSize - * and mRelroFd will be set. - * @return true on success, false otherwise. - */ - private static native boolean nativeCreateSharedRelro( - String library, long loadAddress, LibInfo libInfo); - - /** - * Native method used to use a shared RELRO section. - * - * @param library Library name. - * @param libInfo A LibInfo instance containing valid RELRO information - * @return true on success. - */ - private static native boolean nativeUseSharedRelro(String library, LibInfo libInfo); - - /** - * Return a random address that should be free to be mapped with the given size. - * Maps an area large enough for the largest library we might attempt to load, - * and if successful then unmaps it and returns the address of the area allocated - * by the system (with ASLR). The idea is that this area should remain free of - * other mappings until we map our library into it. - * - * @return address to pass to future mmap, or 0 on error. - */ - private static native long nativeGetRandomBaseLoadAddress(); -} diff --git a/android/java/src/org/chromium/base/library_loader/LoaderErrors.java b/android/java/src/org/chromium/base/library_loader/LoaderErrors.java deleted file mode 100644 index 2b94370bd..000000000 --- a/android/java/src/org/chromium/base/library_loader/LoaderErrors.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.library_loader; - -/** - * These are the possible failures from the LibraryLoader - */ -public class LoaderErrors { - public static final int LOADER_ERROR_NORMAL_COMPLETION = 0; - public static final int LOADER_ERROR_FAILED_TO_REGISTER_JNI = 1; - public static final int LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED = 2; - public static final int LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION = 3; - public static final int LOADER_ERROR_NATIVE_STARTUP_FAILED = 4; -} diff --git a/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java b/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java deleted file mode 100644 index 6f8008d64..000000000 --- a/android/java/src/org/chromium/base/library_loader/NativeLibraryPreloader.java +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.library_loader; - -import android.content.Context; - -/** - * This is interface to preload the native library before calling System.loadLibrary. - * - * Preloading shouldn't call System.loadLibrary() or otherwise cause any Chromium - * code to be run, because it can be called before Chromium command line is known. - * It can however open the library via dlopen() or android_dlopen_ext() so that - * dlopen() later called by System.loadLibrary() becomes a noop. This is what the - * only subclass (MonochromeLibraryPreloader) is doing. - */ -public abstract class NativeLibraryPreloader { - public abstract int loadLibrary(Context context); -} diff --git a/android/java/src/org/chromium/base/library_loader/ProcessInitException.java b/android/java/src/org/chromium/base/library_loader/ProcessInitException.java deleted file mode 100644 index 106667536..000000000 --- a/android/java/src/org/chromium/base/library_loader/ProcessInitException.java +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.library_loader; - -/** - * The exception that is thrown when the intialization of a process was failed. - */ -public class ProcessInitException extends Exception { - private int mErrorCode = LoaderErrors.LOADER_ERROR_NORMAL_COMPLETION; - - /** - * @param errorCode This will be one of the LoaderErrors error codes. - */ - public ProcessInitException(int errorCode) { - mErrorCode = errorCode; - } - - /** - * @param errorCode This will be one of the LoaderErrors error codes. - * @param throwable The wrapped throwable obj. - */ - public ProcessInitException(int errorCode, Throwable throwable) { - super(null, throwable); - mErrorCode = errorCode; - } - - /** - * Return the error code. - */ - public int getErrorCode() { - return mErrorCode; - } -} diff --git a/android/java/src/org/chromium/base/memory/MemoryPressureCallback.java b/android/java/src/org/chromium/base/memory/MemoryPressureCallback.java deleted file mode 100644 index 258aa0bbd..000000000 --- a/android/java/src/org/chromium/base/memory/MemoryPressureCallback.java +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.memory; - -import org.chromium.base.MemoryPressureLevel; - -/** - * Memory pressure callback interface. - */ -@FunctionalInterface -public interface MemoryPressureCallback { - public void onPressure(@MemoryPressureLevel int pressure); -} diff --git a/android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java b/android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java deleted file mode 100644 index c8af48468..000000000 --- a/android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.memory; - -import android.app.ActivityManager; -import android.content.ComponentCallbacks2; -import android.content.res.Configuration; -import android.os.Build; -import android.os.SystemClock; - -import org.chromium.base.ContextUtils; -import org.chromium.base.MemoryPressureLevel; -import org.chromium.base.MemoryPressureListener; -import org.chromium.base.Supplier; -import org.chromium.base.ThreadUtils; -import org.chromium.base.VisibleForTesting; -import org.chromium.base.annotations.MainDex; -import org.chromium.base.metrics.CachedMetrics; - -import java.util.concurrent.TimeUnit; - -/** - * This class monitors memory pressure and reports it to the native side. - * Even though there can be other callbacks besides MemoryPressureListener (which reports - * pressure to the native side, and is added implicitly), the class is designed to suite - * needs of native MemoryPressureListeners. - * - * There are two groups of MemoryPressureListeners: - * - * 1. Stateless, i.e. ones that simply free memory (caches, etc.) in response to memory - * pressure. These listeners need to be called periodically (to have effect), but not - * too frequently (to avoid regressing performance too much). - * - * 2. Stateful, i.e. ones that change their behavior based on the last received memory - * pressure (in addition to freeing memory). These listeners need to know when the - * pressure subsides, i.e. they need to be notified about CRITICAL->MODERATE changes. - * - * Android notifies about memory pressure through onTrimMemory() / onLowMemory() callbacks - * from ComponentCallbacks2, but these are unreliable (e.g. called too early, called just - * once, not called when memory pressure subsides, etc., see https://crbug.com/813909 for - * more examples). - * - * There is also ActivityManager.getMyMemoryState() API which returns current pressure for - * the calling process. It has its caveats, for example it can't be called from isolated - * processes (renderers). Plus we don't want to poll getMyMemoryState() unnecessarily, for - * example there is no reason to poll it when Chrome is in the background. - * - * This class implements the following principles: - * - * 1. Throttle pressure signals sent to callbacks. - * Callbacks are called at most once during throttling interval. If same pressure is - * reported several times during the interval, all reports except the first one are - * ignored. - * - * 2. Always report changes in pressure. - * If pressure changes during the interval, the change is not ignored, but delayed - * until the end of the interval. - * - * 3. Poll on CRITICAL memory pressure. - * Once CRITICAL pressure is reported, getMyMemoryState API is used to periodically - * query pressure until it subsides (becomes non-CRITICAL). - * - * Zooming out, the class is used as follows: - * - * 1. Only the browser process / WebView process poll, and it only polls when it makes - * sense to do so (when Chrome is in the foreground / there are WebView instances - * around). - * - * 2. Services (GPU, renderers) don't poll, instead they get additional pressure signals - * from the main process. - * - * NOTE: This class should only be used on UiThread as defined by ThreadUtils (which is - * Android main thread for Chrome, but can be some other thread for WebView). - */ -@MainDex -public class MemoryPressureMonitor { - private static final int DEFAULT_THROTTLING_INTERVAL_MS = 60 * 1000; - - private final int mThrottlingIntervalMs; - - // Pressure reported to callbacks in the current throttling interval. - private @MemoryPressureLevel int mLastReportedPressure = MemoryPressureLevel.NONE; - - // Pressure received (but not reported) during the current throttling interval, - // or null if no pressure was received. - private @MemoryPressureLevel Integer mThrottledPressure; - - // Whether we need to throttle pressure signals. - private boolean mIsInsideThrottlingInterval; - - private boolean mPollingEnabled; - - // Changed by tests. - private Supplier mCurrentPressureSupplier = - MemoryPressureMonitor::getCurrentMemoryPressure; - - // Changed by tests. - private MemoryPressureCallback mReportingCallback = - MemoryPressureListener::notifyMemoryPressure; - - private final Runnable mThrottlingIntervalTask = this ::onThrottlingIntervalFinished; - - // ActivityManager.getMyMemoryState() time histograms, recorded by getCurrentMemoryPressure(). - // Using Count1MHistogramSample because TimesHistogramSample doesn't support microsecond - // precision. - private static final CachedMetrics.Count1MHistogramSample sGetMyMemoryStateSucceededTime = - new CachedMetrics.Count1MHistogramSample( - "Android.MemoryPressureMonitor.GetMyMemoryState.Succeeded.Time"); - private static final CachedMetrics.Count1MHistogramSample sGetMyMemoryStateFailedTime = - new CachedMetrics.Count1MHistogramSample( - "Android.MemoryPressureMonitor.GetMyMemoryState.Failed.Time"); - - // The only instance. - public static final MemoryPressureMonitor INSTANCE = - new MemoryPressureMonitor(DEFAULT_THROTTLING_INTERVAL_MS); - - @VisibleForTesting - protected MemoryPressureMonitor(int throttlingIntervalMs) { - mThrottlingIntervalMs = throttlingIntervalMs; - } - - /** - * Starts listening to ComponentCallbacks2. - */ - public void registerComponentCallbacks() { - ThreadUtils.assertOnUiThread(); - - ContextUtils.getApplicationContext().registerComponentCallbacks(new ComponentCallbacks2() { - @Override - public void onTrimMemory(int level) { - Integer pressure = memoryPressureFromTrimLevel(level); - if (pressure != null) { - notifyPressure(pressure); - } - } - - @Override - public void onLowMemory() { - notifyPressure(MemoryPressureLevel.CRITICAL); - } - - @Override - public void onConfigurationChanged(Configuration configuration) {} - }); - } - - /** - * Enables memory pressure polling. - * See class comment for specifics. This method also does a single pressure check to get - * the current pressure. - */ - public void enablePolling() { - ThreadUtils.assertOnUiThread(); - if (mPollingEnabled) return; - - mPollingEnabled = true; - if (!mIsInsideThrottlingInterval) { - reportCurrentPressure(); - } - } - - /** - * Disables memory pressure polling. - */ - public void disablePolling() { - ThreadUtils.assertOnUiThread(); - if (!mPollingEnabled) return; - - mPollingEnabled = false; - } - - /** - * Notifies the class about change in memory pressure. - * Note that |pressure| might get throttled or delayed, i.e. calling this method doesn't - * necessarily call the callbacks. See the class comment. - */ - public void notifyPressure(@MemoryPressureLevel int pressure) { - ThreadUtils.assertOnUiThread(); - - if (mIsInsideThrottlingInterval) { - // We've already reported during this interval. Save |pressure| and act on - // it later, when the interval finishes. - mThrottledPressure = pressure; - return; - } - - reportPressure(pressure); - } - - /** - * Last pressure that was reported to MemoryPressureListener. - * Returns MemoryPressureLevel.NONE if nothing was reported yet. - */ - public @MemoryPressureLevel int getLastReportedPressure() { - ThreadUtils.assertOnUiThread(); - return mLastReportedPressure; - } - - private void reportPressure(@MemoryPressureLevel int pressure) { - assert !mIsInsideThrottlingInterval : "Can't report pressure when throttling."; - - startThrottlingInterval(); - - mLastReportedPressure = pressure; - mReportingCallback.onPressure(pressure); - } - - private void onThrottlingIntervalFinished() { - mIsInsideThrottlingInterval = false; - - // If there was a pressure change during the interval, report it. - if (mThrottledPressure != null && mLastReportedPressure != mThrottledPressure) { - int throttledPressure = mThrottledPressure; - mThrottledPressure = null; - reportPressure(throttledPressure); - return; - } - - // The pressure didn't change during the interval. Report current pressure - // (starting a new interval) if we need to. - if (mPollingEnabled && mLastReportedPressure == MemoryPressureLevel.CRITICAL) { - reportCurrentPressure(); - } - } - - private void reportCurrentPressure() { - Integer pressure = mCurrentPressureSupplier.get(); - if (pressure != null) { - reportPressure(pressure); - } - } - - private void startThrottlingInterval() { - ThreadUtils.postOnUiThreadDelayed(mThrottlingIntervalTask, mThrottlingIntervalMs); - mIsInsideThrottlingInterval = true; - } - - @VisibleForTesting - public void setCurrentPressureSupplierForTesting(Supplier supplier) { - mCurrentPressureSupplier = supplier; - } - - @VisibleForTesting - public void setReportingCallbackForTesting(MemoryPressureCallback callback) { - mReportingCallback = callback; - } - - /** - * Queries current memory pressure. - * Returns null if the pressure couldn't be determined. - */ - private static @MemoryPressureLevel Integer getCurrentMemoryPressure() { - long startNanos = elapsedRealtimeNanos(); - try { - ActivityManager.RunningAppProcessInfo processInfo = - new ActivityManager.RunningAppProcessInfo(); - ActivityManager.getMyMemoryState(processInfo); - recordRealtimeNanosDuration(sGetMyMemoryStateSucceededTime, startNanos); - return memoryPressureFromTrimLevel(processInfo.lastTrimLevel); - } catch (Exception e) { - // Defensively catch all exceptions, just in case. - recordRealtimeNanosDuration(sGetMyMemoryStateFailedTime, startNanos); - return null; - } - } - - private static void recordRealtimeNanosDuration( - CachedMetrics.Count1MHistogramSample histogram, long startNanos) { - // We're using Count1MHistogram, so we need to calculate duration in microseconds - long durationUs = TimeUnit.NANOSECONDS.toMicros(elapsedRealtimeNanos() - startNanos); - // record() takes int, so we need to clamp. - histogram.record((int) Math.min(durationUs, Integer.MAX_VALUE)); - } - - private static long elapsedRealtimeNanos() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - return SystemClock.elapsedRealtimeNanos(); - } else { - return SystemClock.elapsedRealtime() * 1000000; - } - } - - /** - * Maps ComponentCallbacks2.TRIM_* value to MemoryPressureLevel. - * Returns null if |level| couldn't be mapped and should be ignored. - */ - @VisibleForTesting - public static @MemoryPressureLevel Integer memoryPressureFromTrimLevel(int level) { - if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE - || level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) { - return MemoryPressureLevel.CRITICAL; - } else if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { - // Don't notify on TRIM_MEMORY_UI_HIDDEN, since this class only - // dispatches actionable memory pressure signals to native. - return MemoryPressureLevel.MODERATE; - } - return null; - } -} diff --git a/android/java/src/org/chromium/base/memory/MemoryPressureUma.java b/android/java/src/org/chromium/base/memory/MemoryPressureUma.java deleted file mode 100644 index dc90f5706..000000000 --- a/android/java/src/org/chromium/base/memory/MemoryPressureUma.java +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.memory; - -import android.content.ComponentCallbacks2; -import android.content.res.Configuration; -import android.support.annotation.IntDef; - -import org.chromium.base.ContextUtils; -import org.chromium.base.ThreadUtils; -import org.chromium.base.metrics.RecordHistogram; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Centralizes UMA data collection for Android-specific memory conditions. - */ -public class MemoryPressureUma implements ComponentCallbacks2 { - @IntDef({ - Notification.UNKNOWN_TRIM_LEVEL, Notification.TRIM_MEMORY_COMPLETE, - Notification.TRIM_MEMORY_MODERATE, Notification.TRIM_MEMORY_BACKGROUND, - Notification.TRIM_MEMORY_UI_HIDDEN, Notification.TRIM_MEMORY_RUNNING_CRITICAL, - Notification.TRIM_MEMORY_RUNNING_LOW, Notification.TRIM_MEMORY_RUNNING_MODERATE, - Notification.ON_LOW_MEMORY, Notification.NOTIFICATION_MAX, - }) - @Retention(RetentionPolicy.SOURCE) - private @interface Notification { - // WARNING: These values are persisted to logs. Entries should not be - // renumbered and numeric values should never be reused. - // Keep in sync with "Android.MemoryPressureNotification" UMA enum. - int UNKNOWN_TRIM_LEVEL = 0; - int TRIM_MEMORY_COMPLETE = 1; - int TRIM_MEMORY_MODERATE = 2; - int TRIM_MEMORY_BACKGROUND = 3; - int TRIM_MEMORY_UI_HIDDEN = 4; - int TRIM_MEMORY_RUNNING_CRITICAL = 5; - int TRIM_MEMORY_RUNNING_LOW = 6; - int TRIM_MEMORY_RUNNING_MODERATE = 7; - int ON_LOW_MEMORY = 8; - - // Must be the last one. - int NOTIFICATION_MAX = 9; - } - - private final String mHistogramName; - - private static MemoryPressureUma sInstance; - - public static void initializeForBrowser() { - initializeInstance("Browser"); - } - - public static void initializeForChildService() { - initializeInstance("ChildService"); - } - - private static void initializeInstance(String processType) { - ThreadUtils.assertOnUiThread(); - assert sInstance == null; - sInstance = new MemoryPressureUma(processType); - ContextUtils.getApplicationContext().registerComponentCallbacks(sInstance); - } - - private MemoryPressureUma(String processType) { - mHistogramName = "Android.MemoryPressureNotification." + processType; - } - - @Override - public void onLowMemory() { - record(Notification.ON_LOW_MEMORY); - } - - @Override - public void onTrimMemory(int level) { - switch (level) { - case TRIM_MEMORY_COMPLETE: - record(Notification.TRIM_MEMORY_COMPLETE); - break; - case TRIM_MEMORY_MODERATE: - record(Notification.TRIM_MEMORY_MODERATE); - break; - case TRIM_MEMORY_BACKGROUND: - record(Notification.TRIM_MEMORY_BACKGROUND); - break; - case TRIM_MEMORY_UI_HIDDEN: - record(Notification.TRIM_MEMORY_UI_HIDDEN); - break; - case TRIM_MEMORY_RUNNING_CRITICAL: - record(Notification.TRIM_MEMORY_RUNNING_CRITICAL); - break; - case TRIM_MEMORY_RUNNING_LOW: - record(Notification.TRIM_MEMORY_RUNNING_LOW); - break; - case TRIM_MEMORY_RUNNING_MODERATE: - record(Notification.TRIM_MEMORY_RUNNING_MODERATE); - break; - default: - record(Notification.UNKNOWN_TRIM_LEVEL); - break; - } - } - - @Override - public void onConfigurationChanged(Configuration configuration) {} - - private void record(@Notification int notification) { - RecordHistogram.recordEnumeratedHistogram( - mHistogramName, notification, Notification.NOTIFICATION_MAX); - } -} diff --git a/android/java/src/org/chromium/base/metrics/CachedMetrics.java b/android/java/src/org/chromium/base/metrics/CachedMetrics.java deleted file mode 100644 index ba03e5127..000000000 --- a/android/java/src/org/chromium/base/metrics/CachedMetrics.java +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.metrics; - -import org.chromium.base.library_loader.LibraryLoader; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -/** - * Utility classes for recording UMA metrics before the native library - * may have been loaded. Metrics are cached until the library is known - * to be loaded, then committed to the MetricsService all at once. - */ -public class CachedMetrics { - /** - * Base class for cached metric objects. Subclasses are expected to call - * addToCache() when some metric state gets recorded that requires a later - * commit operation when the native library is loaded. - */ - private abstract static class CachedMetric { - private static final List sMetrics = new ArrayList(); - - protected final String mName; - protected boolean mCached; - - /** - * @param name Name of the metric to record. - */ - protected CachedMetric(String name) { - mName = name; - } - - /** - * Adds this object to the sMetrics cache, if it hasn't been added already. - * Must be called while holding the synchronized(sMetrics) lock. - * Note: The synchronization is not done inside this function because subclasses - * need to increment their held values under lock to ensure thread-safety. - */ - protected final void addToCache() { - assert Thread.holdsLock(sMetrics); - - if (mCached) return; - sMetrics.add(this); - mCached = true; - } - - /** - * Commits the metric. Expects the native library to be loaded. - * Must be called while holding the synchronized(sMetrics) lock. - */ - protected abstract void commitAndClear(); - } - - /** - * Caches an action that will be recorded after native side is loaded. - */ - public static class ActionEvent extends CachedMetric { - private int mCount; - - public ActionEvent(String actionName) { - super(actionName); - } - - public void record() { - synchronized (CachedMetric.sMetrics) { - if (LibraryLoader.getInstance().isInitialized()) { - recordWithNative(); - } else { - mCount++; - addToCache(); - } - } - } - - private void recordWithNative() { - RecordUserAction.record(mName); - } - - @Override - protected void commitAndClear() { - while (mCount > 0) { - recordWithNative(); - mCount--; - } - } - } - - /** Caches a set of integer histogram samples. */ - public static class SparseHistogramSample extends CachedMetric { - private final List mSamples = new ArrayList(); - - public SparseHistogramSample(String histogramName) { - super(histogramName); - } - - public void record(int sample) { - synchronized (CachedMetric.sMetrics) { - if (LibraryLoader.getInstance().isInitialized()) { - recordWithNative(sample); - } else { - mSamples.add(sample); - addToCache(); - } - } - } - - private void recordWithNative(int sample) { - RecordHistogram.recordSparseSlowlyHistogram(mName, sample); - } - - @Override - protected void commitAndClear() { - for (Integer sample : mSamples) { - recordWithNative(sample); - } - mSamples.clear(); - } - } - - /** Caches a set of enumerated histogram samples. */ - public static class EnumeratedHistogramSample extends CachedMetric { - private final List mSamples = new ArrayList(); - private final int mMaxValue; - - public EnumeratedHistogramSample(String histogramName, int maxValue) { - super(histogramName); - mMaxValue = maxValue; - } - - public void record(int sample) { - synchronized (CachedMetric.sMetrics) { - if (LibraryLoader.getInstance().isInitialized()) { - recordWithNative(sample); - } else { - mSamples.add(sample); - addToCache(); - } - } - } - - private void recordWithNative(int sample) { - RecordHistogram.recordEnumeratedHistogram(mName, sample, mMaxValue); - } - - @Override - protected void commitAndClear() { - for (Integer sample : mSamples) { - recordWithNative(sample); - } - mSamples.clear(); - } - } - - /** Caches a set of times histogram samples. */ - public static class TimesHistogramSample extends CachedMetric { - private final List mSamples = new ArrayList(); - private final TimeUnit mTimeUnit; - - public TimesHistogramSample(String histogramName, TimeUnit timeUnit) { - super(histogramName); - RecordHistogram.assertTimesHistogramSupportsUnit(timeUnit); - mTimeUnit = timeUnit; - } - - public void record(long sample) { - synchronized (CachedMetric.sMetrics) { - if (LibraryLoader.getInstance().isInitialized()) { - recordWithNative(sample); - } else { - mSamples.add(sample); - addToCache(); - } - } - } - - private void recordWithNative(long sample) { - RecordHistogram.recordTimesHistogram(mName, sample, mTimeUnit); - } - - @Override - protected void commitAndClear() { - for (Long sample : mSamples) { - recordWithNative(sample); - } - mSamples.clear(); - } - } - - /** Caches a set of boolean histogram samples. */ - public static class BooleanHistogramSample extends CachedMetric { - private final List mSamples = new ArrayList(); - - public BooleanHistogramSample(String histogramName) { - super(histogramName); - } - - public void record(boolean sample) { - synchronized (CachedMetric.sMetrics) { - if (LibraryLoader.getInstance().isInitialized()) { - recordWithNative(sample); - } else { - mSamples.add(sample); - addToCache(); - } - } - } - - private void recordWithNative(boolean sample) { - RecordHistogram.recordBooleanHistogram(mName, sample); - } - - @Override - protected void commitAndClear() { - for (Boolean sample : mSamples) { - recordWithNative(sample); - } - mSamples.clear(); - } - } - - /** - * Caches a set of custom count histogram samples. - * Corresponds to UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro. - */ - public static class CustomCountHistogramSample extends CachedMetric { - private final List mSamples = new ArrayList(); - private final int mMin; - private final int mMax; - private final int mNumBuckets; - - public CustomCountHistogramSample(String histogramName, int min, int max, int numBuckets) { - super(histogramName); - mMin = min; - mMax = max; - mNumBuckets = numBuckets; - } - - public void record(int sample) { - synchronized (CachedMetric.sMetrics) { - if (LibraryLoader.getInstance().isInitialized()) { - recordWithNative(sample); - } else { - mSamples.add(sample); - addToCache(); - } - } - } - - private void recordWithNative(int sample) { - RecordHistogram.recordCustomCountHistogram(mName, sample, mMin, mMax, mNumBuckets); - } - - @Override - protected void commitAndClear() { - for (Integer sample : mSamples) { - recordWithNative(sample); - } - mSamples.clear(); - } - } - - /** - * Caches a set of count histogram samples in range [1, 100). - * Corresponds to UMA_HISTOGRAM_COUNTS_100 C++ macro. - */ - public static class Count100HistogramSample extends CustomCountHistogramSample { - public Count100HistogramSample(String histogramName) { - super(histogramName, 1, 100, 50); - } - } - - /** - * Caches a set of count histogram samples in range [1, 1000). - * Corresponds to UMA_HISTOGRAM_COUNTS_1000 C++ macro. - */ - public static class Count1000HistogramSample extends CustomCountHistogramSample { - public Count1000HistogramSample(String histogramName) { - super(histogramName, 1, 1000, 50); - } - } - - /** - * Caches a set of count histogram samples in range [1, 1000000). - * Corresponds to UMA_HISTOGRAM_COUNTS_1M C++ macro. - */ - public static class Count1MHistogramSample extends CustomCountHistogramSample { - public Count1MHistogramSample(String histogramName) { - super(histogramName, 1, 1000000, 50); - } - } - - /** - * Calls out to native code to commit any cached histograms and events. - * Should be called once the native library has been loaded. - */ - public static void commitCachedMetrics() { - synchronized (CachedMetric.sMetrics) { - for (CachedMetric metric : CachedMetric.sMetrics) { - metric.commitAndClear(); - } - } - } -} diff --git a/android/java/src/org/chromium/base/metrics/RecordHistogram.java b/android/java/src/org/chromium/base/metrics/RecordHistogram.java deleted file mode 100644 index 898f0094a..000000000 --- a/android/java/src/org/chromium/base/metrics/RecordHistogram.java +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.metrics; - -import org.chromium.base.VisibleForTesting; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.MainDex; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * Java API for recording UMA histograms. - * - * Internally, histograms objects are cached on the Java side by their pointer - * values (converted to long). This is safe to do because C++ Histogram objects - * are never freed. Caching them on the Java side prevents needing to do costly - * Java String to C++ string conversions on the C++ side during lookup. - * - * Note: the JNI calls are relatively costly - avoid calling these methods in performance-critical - * code. - */ -@JNINamespace("base::android") -@MainDex -public class RecordHistogram { - private static Throwable sDisabledBy; - private static Map sCache = - Collections.synchronizedMap(new HashMap()); - - /** - * Tests may not have native initialized, so they may need to disable metrics. The value should - * be reset after the test done, to avoid carrying over state to unrelated tests. - * - * In JUnit tests this can be done automatically using - * {@link org.chromium.chrome.browser.DisableHistogramsRule} - */ - @VisibleForTesting - public static void setDisabledForTests(boolean disabled) { - if (disabled && sDisabledBy != null) { - throw new IllegalStateException("Histograms are already disabled.", sDisabledBy); - } - sDisabledBy = disabled ? new Throwable() : null; - } - - private static long getCachedHistogramKey(String name) { - Long key = sCache.get(name); - // Note: If key is null, we don't have it cached. In that case, pass 0 - // to the native code, which gets converted to a null histogram pointer - // which will cause the native code to look up the object on the native - // side. - return (key == null ? 0 : key); - } - - /** - * Records a sample in a boolean UMA histogram of the given name. Boolean histogram has two - * buckets, corresponding to success (true) and failure (false). This is the Java equivalent of - * the UMA_HISTOGRAM_BOOLEAN C++ macro. - * @param name name of the histogram - * @param sample sample to be recorded, either true or false - */ - public static void recordBooleanHistogram(String name, boolean sample) { - if (sDisabledBy != null) return; - long key = getCachedHistogramKey(name); - long result = nativeRecordBooleanHistogram(name, key, sample); - if (result != key) sCache.put(name, result); - } - - /** - * Records a sample in an enumerated histogram of the given name and boundary. Note that - * |boundary| identifies the histogram - it should be the same at every invocation. This is the - * Java equivalent of the UMA_HISTOGRAM_ENUMERATION C++ macro. - * @param name name of the histogram - * @param sample sample to be recorded, at least 0 and at most |boundary| - 1 - * @param boundary upper bound for legal sample values - all sample values have to be strictly - * lower than |boundary| - */ - public static void recordEnumeratedHistogram(String name, int sample, int boundary) { - if (sDisabledBy != null) return; - long key = getCachedHistogramKey(name); - long result = nativeRecordEnumeratedHistogram(name, key, sample, boundary); - if (result != key) sCache.put(name, result); - } - - /** - * Records a sample in a count histogram. This is the Java equivalent of the - * UMA_HISTOGRAM_COUNTS C++ macro. - * @param name name of the histogram - * @param sample sample to be recorded, at least 1 and at most 999999 - */ - public static void recordCountHistogram(String name, int sample) { - recordCustomCountHistogram(name, sample, 1, 1000000, 50); - } - - /** - * Records a sample in a count histogram. This is the Java equivalent of the - * UMA_HISTOGRAM_COUNTS_100 C++ macro. - * @param name name of the histogram - * @param sample sample to be recorded, at least 1 and at most 99 - */ - public static void recordCount100Histogram(String name, int sample) { - recordCustomCountHistogram(name, sample, 1, 100, 50); - } - - /** - * Records a sample in a count histogram. This is the Java equivalent of the - * UMA_HISTOGRAM_COUNTS_1000 C++ macro. - * @param name name of the histogram - * @param sample sample to be recorded, at least 1 and at most 999 - */ - public static void recordCount1000Histogram(String name, int sample) { - recordCustomCountHistogram(name, sample, 1, 1000, 50); - } - - /** - * Records a sample in a count histogram. This is the Java equivalent of the - * UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro. - * @param name name of the histogram - * @param sample sample to be recorded, at least |min| and at most |max| - 1 - * @param min lower bound for expected sample values. It must be >= 1 - * @param max upper bounds for expected sample values - * @param numBuckets the number of buckets - */ - public static void recordCustomCountHistogram( - String name, int sample, int min, int max, int numBuckets) { - if (sDisabledBy != null) return; - long key = getCachedHistogramKey(name); - long result = nativeRecordCustomCountHistogram(name, key, sample, min, max, numBuckets); - if (result != key) sCache.put(name, result); - } - - /** - * Records a sample in a linear histogram. This is the Java equivalent for using - * base::LinearHistogram. - * @param name name of the histogram - * @param sample sample to be recorded, at least |min| and at most |max| - 1. - * @param min lower bound for expected sample values, should be at least 1. - * @param max upper bounds for expected sample values - * @param numBuckets the number of buckets - */ - public static void recordLinearCountHistogram( - String name, int sample, int min, int max, int numBuckets) { - if (sDisabledBy != null) return; - long key = getCachedHistogramKey(name); - long result = nativeRecordLinearCountHistogram(name, key, sample, min, max, numBuckets); - if (result != key) sCache.put(name, result); - } - - /** - * Records a sample in a percentage histogram. This is the Java equivalent of the - * UMA_HISTOGRAM_PERCENTAGE C++ macro. - * @param name name of the histogram - * @param sample sample to be recorded, at least 0 and at most 100. - */ - public static void recordPercentageHistogram(String name, int sample) { - if (sDisabledBy != null) return; - long key = getCachedHistogramKey(name); - long result = nativeRecordEnumeratedHistogram(name, key, sample, 101); - if (result != key) sCache.put(name, result); - } - - /** - * Records a sparse histogram. This is the Java equivalent of UmaHistogramSparse. - * @param name name of the histogram - * @param sample sample to be recorded. All values of |sample| are valid, including negative - * values. - */ - public static void recordSparseSlowlyHistogram(String name, int sample) { - if (sDisabledBy != null) return; - long key = getCachedHistogramKey(name); - long result = nativeRecordSparseHistogram(name, key, sample); - if (result != key) sCache.put(name, result); - } - - /** - * Records a sample in a histogram of times. Useful for recording short durations. This is the - * Java equivalent of the UMA_HISTOGRAM_TIMES C++ macro. - * Note that histogram samples will always be converted to milliseconds when logged. - * @param name name of the histogram - * @param duration duration to be recorded - * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS) - */ - public static void recordTimesHistogram(String name, long duration, TimeUnit timeUnit) { - assertTimesHistogramSupportsUnit(timeUnit); - recordCustomTimesHistogramMilliseconds( - name, timeUnit.toMillis(duration), 1, TimeUnit.SECONDS.toMillis(10), 50); - } - - /** - * Records a sample in a histogram of times. Useful for recording medium durations. This is the - * Java equivalent of the UMA_HISTOGRAM_MEDIUM_TIMES C++ macro. - * Note that histogram samples will always be converted to milliseconds when logged. - * @param name name of the histogram - * @param duration duration to be recorded - * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS) - */ - public static void recordMediumTimesHistogram(String name, long duration, TimeUnit timeUnit) { - assertTimesHistogramSupportsUnit(timeUnit); - recordCustomTimesHistogramMilliseconds( - name, timeUnit.toMillis(duration), 10, TimeUnit.MINUTES.toMillis(3), 50); - } - - /** - * Records a sample in a histogram of times. Useful for recording long durations. This is the - * Java equivalent of the UMA_HISTOGRAM_LONG_TIMES C++ macro. - * Note that histogram samples will always be converted to milliseconds when logged. - * @param name name of the histogram - * @param duration duration to be recorded - * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS) - */ - public static void recordLongTimesHistogram(String name, long duration, TimeUnit timeUnit) { - assertTimesHistogramSupportsUnit(timeUnit); - recordCustomTimesHistogramMilliseconds( - name, timeUnit.toMillis(duration), 1, TimeUnit.HOURS.toMillis(1), 50); - } - - /** - * Records a sample in a histogram of times. Useful for recording long durations. This is the - * Java equivalent of the UMA_HISTOGRAM_LONG_TIMES_100 C++ macro. - * Note that histogram samples will always be converted to milliseconds when logged. - * @param name name of the histogram - * @param duration duration to be recorded - * @param timeUnit the unit of the duration argument (must be >= MILLISECONDS) - */ - public static void recordLongTimesHistogram100(String name, long duration, TimeUnit timeUnit) { - assertTimesHistogramSupportsUnit(timeUnit); - recordCustomTimesHistogramMilliseconds( - name, timeUnit.toMillis(duration), 1, TimeUnit.HOURS.toMillis(1), 100); - } - - /** - * Records a sample in a histogram of times with custom buckets. This is the Java equivalent of - * the UMA_HISTOGRAM_CUSTOM_TIMES C++ macro. - * Note that histogram samples will always be converted to milliseconds when logged. - * @param name name of the histogram - * @param duration duration to be recorded - * @param min the minimum bucket value - * @param max the maximum bucket value - * @param timeUnit the unit of the duration, min, and max arguments (must be >= MILLISECONDS) - * @param numBuckets the number of buckets - */ - public static void recordCustomTimesHistogram( - String name, long duration, long min, long max, TimeUnit timeUnit, int numBuckets) { - assertTimesHistogramSupportsUnit(timeUnit); - recordCustomTimesHistogramMilliseconds(name, timeUnit.toMillis(duration), - timeUnit.toMillis(min), timeUnit.toMillis(max), numBuckets); - } - - /** - * Records a sample in a histogram of sizes in KB. This is the Java equivalent of the - * UMA_HISTOGRAM_MEMORY_KB C++ macro. - * - * Good for sizes up to about 500MB. - * - * @param name name of the histogram. - * @param sizeInkB Sample to record in KB. - */ - public static void recordMemoryKBHistogram(String name, int sizeInKB) { - recordCustomCountHistogram(name, sizeInKB, 1000, 500000, 50); - } - - /** - * Asserts that the time unit is supported by TimesHistogram. - * @param timeUnit the unit, must be >= MILLISECONDS - */ - /* package */ static void assertTimesHistogramSupportsUnit(TimeUnit timeUnit) { - // Use extra variable, or else 'git cl format' produces weird results. - boolean supported = timeUnit != TimeUnit.NANOSECONDS && timeUnit != TimeUnit.MICROSECONDS; - assert supported : "TimesHistogram doesn't support MICROSECOND and NANOSECONDS time units. " - + "Consider using CountHistogram instead."; - } - - private static int clampToInt(long value) { - if (value > Integer.MAX_VALUE) return Integer.MAX_VALUE; - // Note: Clamping to MIN_VALUE rather than 0, to let base/ histograms code - // do its own handling of negative values in the future. - if (value < Integer.MIN_VALUE) return Integer.MIN_VALUE; - return (int) value; - } - - private static void recordCustomTimesHistogramMilliseconds( - String name, long duration, long min, long max, int numBuckets) { - if (sDisabledBy != null) return; - long key = getCachedHistogramKey(name); - // Note: Duration, min and max are clamped to int here because that's what's expected by - // the native histograms API. Callers of these functions still pass longs because that's - // the types returned by TimeUnit and System.currentTimeMillis() APIs, from which these - // values come. - assert max == clampToInt(max); - long result = nativeRecordCustomTimesHistogramMilliseconds( - name, key, clampToInt(duration), clampToInt(min), clampToInt(max), numBuckets); - if (result != key) sCache.put(name, result); - } - - /** - * Returns the number of samples recorded in the given bucket of the given histogram. - * @param name name of the histogram to look up - * @param sample the bucket containing this sample value will be looked up - */ - @VisibleForTesting - public static int getHistogramValueCountForTesting(String name, int sample) { - return nativeGetHistogramValueCountForTesting(name, sample); - } - - /** - * Returns the number of samples recorded for the given histogram. - * @param name name of the histogram to look up. - */ - @VisibleForTesting - public static int getHistogramTotalCountForTesting(String name) { - return nativeGetHistogramTotalCountForTesting(name); - } - - private static native long nativeRecordCustomTimesHistogramMilliseconds( - String name, long key, int duration, int min, int max, int numBuckets); - - private static native long nativeRecordBooleanHistogram(String name, long key, boolean sample); - private static native long nativeRecordEnumeratedHistogram( - String name, long key, int sample, int boundary); - private static native long nativeRecordCustomCountHistogram( - String name, long key, int sample, int min, int max, int numBuckets); - private static native long nativeRecordLinearCountHistogram( - String name, long key, int sample, int min, int max, int numBuckets); - private static native long nativeRecordSparseHistogram(String name, long key, int sample); - - private static native int nativeGetHistogramValueCountForTesting(String name, int sample); - private static native int nativeGetHistogramTotalCountForTesting(String name); -} diff --git a/android/java/src/org/chromium/base/metrics/RecordUserAction.java b/android/java/src/org/chromium/base/metrics/RecordUserAction.java deleted file mode 100644 index 0d2ba548d..000000000 --- a/android/java/src/org/chromium/base/metrics/RecordUserAction.java +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.metrics; - -import org.chromium.base.ThreadUtils; -import org.chromium.base.VisibleForTesting; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -/** - * Java API for recording UMA actions. - * - * WARNINGS: - * JNI calls are relatively costly - avoid using in performance-critical code. - * - * We use a script (extract_actions.py) to scan the source code and extract actions. A string - * literal (not a variable) must be passed to record(). - */ -@JNINamespace("base::android") -public class RecordUserAction { - private static Throwable sDisabledBy; - - /** - * Tests may not have native initialized, so they may need to disable metrics. The value should - * be reset after the test done, to avoid carrying over state to unrelated tests. - */ - @VisibleForTesting - public static void setDisabledForTests(boolean disabled) { - if (disabled && sDisabledBy != null) { - throw new IllegalStateException("UserActions are already disabled.", sDisabledBy); - } - sDisabledBy = disabled ? new Throwable() : null; - } - - public static void record(final String action) { - if (sDisabledBy != null) return; - - if (ThreadUtils.runningOnUiThread()) { - nativeRecordUserAction(action); - return; - } - - ThreadUtils.runOnUiThread(new Runnable() { - @Override - public void run() { - nativeRecordUserAction(action); - } - }); - } - - /** - * Interface to a class that receives a callback for each UserAction that is recorded. - */ - public interface UserActionCallback { - @CalledByNative("UserActionCallback") - void onActionRecorded(String action); - } - - private static long sNativeActionCallback; - - /** - * Register a callback that is executed for each recorded UserAction. - * Only one callback can be registered at a time. - * The callback has to be unregistered using removeActionCallbackForTesting(). - */ - public static void setActionCallbackForTesting(UserActionCallback callback) { - assert sNativeActionCallback == 0; - sNativeActionCallback = nativeAddActionCallbackForTesting(callback); - } - - /** - * Unregister the UserActionCallback. - */ - public static void removeActionCallbackForTesting() { - assert sNativeActionCallback != 0; - nativeRemoveActionCallbackForTesting(sNativeActionCallback); - sNativeActionCallback = 0; - } - - private static native void nativeRecordUserAction(String action); - private static native long nativeAddActionCallbackForTesting(UserActionCallback callback); - private static native void nativeRemoveActionCallbackForTesting(long callbackId); -} diff --git a/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java b/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java deleted file mode 100644 index bff3fae76..000000000 --- a/android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.metrics; - -import org.chromium.base.annotations.JNINamespace; - -/** - * Java API which exposes the registered histograms on the native side as - * JSON test. - */ -@JNINamespace("base::android") -public final class StatisticsRecorderAndroid { - private StatisticsRecorderAndroid() {} - - /** - * @param verbosityLevel controls the information that should be included when dumping each of - * the histogram. - * @return All the registered histograms as JSON text. - */ - public static String toJson(@JSONVerbosityLevel int verbosityLevel) { - return nativeToJson(verbosityLevel); - } - - private static native String nativeToJson(@JSONVerbosityLevel int verbosityLevel); -} \ No newline at end of file diff --git a/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java b/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java deleted file mode 100644 index 5588ec5bd..000000000 --- a/android/java/src/org/chromium/base/multidex/ChromiumMultiDexInstaller.java +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.multidex; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Build; -import android.support.multidex.MultiDex; - -import org.chromium.base.ContextUtils; -import org.chromium.base.Log; -import org.chromium.base.VisibleForTesting; -import org.chromium.base.annotations.MainDex; - -/** - * Performs multidex installation for non-isolated processes. - */ -@MainDex -public class ChromiumMultiDexInstaller { - private static final String TAG = "base_multidex"; - - /** - * Suffix for the meta-data tag in the AndroidManifext.xml that determines whether loading - * secondary dexes should be skipped for a given process name. - */ - private static final String IGNORE_MULTIDEX_KEY = ".ignore_multidex"; - - /** - * Installs secondary dexes if possible/necessary. - * - * Isolated processes (e.g. renderer processes) can't load secondary dex files on - * K and below, so we don't even try in that case. - * - * In release builds of app apks (as opposed to test apks), this is a no-op because: - * - multidex isn't necessary in release builds because we run proguard there and - * thus aren't threatening to hit the dex limit; and - * - calling MultiDex.install, even in the absence of secondary dexes, causes a - * significant regression in start-up time (crbug.com/525695). - * - * @param context The application context. - */ - @VisibleForTesting - public static void install(Context context) { - // TODO(jbudorick): Back out this version check once support for K & below works. - // http://crbug.com/512357 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP - && !shouldInstallMultiDex(context)) { - Log.i(TAG, "Skipping multidex installation: not needed for process."); - } else { - MultiDex.install(context); - Log.i(TAG, "Completed multidex installation."); - } - } - - // Determines whether MultiDex should be installed for the current process. Isolated - // Processes should skip MultiDex as they can not actually access the files on disk. - // Privileged processes need ot have all of their dependencies in the MainDex for - // performance reasons. - private static boolean shouldInstallMultiDex(Context context) { - if (ContextUtils.isIsolatedProcess()) { - return false; - } - String currentProcessName = ContextUtils.getProcessName(); - PackageManager packageManager = context.getPackageManager(); - try { - ApplicationInfo appInfo = packageManager.getApplicationInfo(context.getPackageName(), - PackageManager.GET_META_DATA); - if (appInfo == null || appInfo.metaData == null) return true; - return !appInfo.metaData.getBoolean(currentProcessName + IGNORE_MULTIDEX_KEY, false); - } catch (PackageManager.NameNotFoundException e) { - return true; - } - } - -} diff --git a/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java b/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java deleted file mode 100644 index 43ae2591d..000000000 --- a/android/java/src/org/chromium/base/process_launcher/ChildConnectionAllocator.java +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.process_launcher; - -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; - -import org.chromium.base.Log; -import org.chromium.base.VisibleForTesting; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Queue; - -/** - * This class is responsible for allocating and managing connections to child - * process services. These connections are in a pool (the services are defined - * in the AndroidManifest.xml). - */ -public class ChildConnectionAllocator { - private static final String TAG = "ChildConnAllocator"; - - /** Factory interface. Used by tests to specialize created connections. */ - @VisibleForTesting - public interface ConnectionFactory { - ChildProcessConnection createConnection(Context context, ComponentName serviceName, - boolean bindToCaller, boolean bindAsExternalService, Bundle serviceBundle); - } - - /** Default implementation of the ConnectionFactory that creates actual connections. */ - private static class ConnectionFactoryImpl implements ConnectionFactory { - @Override - public ChildProcessConnection createConnection(Context context, ComponentName serviceName, - boolean bindToCaller, boolean bindAsExternalService, Bundle serviceBundle) { - return new ChildProcessConnection( - context, serviceName, bindToCaller, bindAsExternalService, serviceBundle); - } - } - - // Delay between the call to freeConnection and the connection actually beeing freed. - private static final long FREE_CONNECTION_DELAY_MILLIS = 1; - - // The handler of the thread on which all interations should happen. - private final Handler mLauncherHandler; - - // Connections to services. Indices of the array correspond to the service numbers. - private final ChildProcessConnection[] mChildProcessConnections; - - // Runnable which will be called when allocator wants to allocate a new connection, but does - // not have any more free slots. May be null. - private final Runnable mFreeSlotCallback; - private final String mPackageName; - private final String mServiceClassName; - private final boolean mBindToCaller; - private final boolean mBindAsExternalService; - private final boolean mUseStrongBinding; - - // The list of free (not bound) service indices. - private final ArrayList mFreeConnectionIndices; - - private final Queue mPendingAllocations = new ArrayDeque<>(); - - private ConnectionFactory mConnectionFactory = new ConnectionFactoryImpl(); - - /** - * Factory method that retrieves the service name and number of service from the - * AndroidManifest.xml. - */ - public static ChildConnectionAllocator create(Context context, Handler launcherHandler, - Runnable freeSlotCallback, String packageName, String serviceClassName, - String numChildServicesManifestKey, boolean bindToCaller, boolean bindAsExternalService, - boolean useStrongBinding) { - int numServices = -1; - PackageManager packageManager = context.getPackageManager(); - try { - ApplicationInfo appInfo = - packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA); - if (appInfo.metaData != null) { - numServices = appInfo.metaData.getInt(numChildServicesManifestKey, -1); - } - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException("Could not get application info."); - } - - if (numServices < 0) { - throw new RuntimeException("Illegal meta data value for number of child services"); - } - - // Check that the service exists. - try { - // PackageManager#getServiceInfo() throws an exception if the service does not exist. - packageManager.getServiceInfo( - new ComponentName(packageName, serviceClassName + "0"), 0); - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException("Illegal meta data value: the child service doesn't exist"); - } - - return new ChildConnectionAllocator(launcherHandler, freeSlotCallback, packageName, - serviceClassName, bindToCaller, bindAsExternalService, useStrongBinding, - numServices); - } - - /** - * Factory method used with some tests to create an allocator with values passed in directly - * instead of being retrieved from the AndroidManifest.xml. - */ - @VisibleForTesting - public static ChildConnectionAllocator createForTest(Runnable freeSlotCallback, - String packageName, String serviceClassName, int serviceCount, boolean bindToCaller, - boolean bindAsExternalService, boolean useStrongBinding) { - return new ChildConnectionAllocator(new Handler(), freeSlotCallback, packageName, - serviceClassName, bindToCaller, bindAsExternalService, useStrongBinding, - serviceCount); - } - - private ChildConnectionAllocator(Handler launcherHandler, Runnable freeSlotCallback, - String packageName, String serviceClassName, boolean bindToCaller, - boolean bindAsExternalService, boolean useStrongBinding, int numChildServices) { - mFreeSlotCallback = freeSlotCallback; - mLauncherHandler = launcherHandler; - assert isRunningOnLauncherThread(); - mPackageName = packageName; - mServiceClassName = serviceClassName; - mBindToCaller = bindToCaller; - mBindAsExternalService = bindAsExternalService; - mUseStrongBinding = useStrongBinding; - mChildProcessConnections = new ChildProcessConnection[numChildServices]; - mFreeConnectionIndices = new ArrayList(numChildServices); - for (int i = 0; i < numChildServices; i++) { - mFreeConnectionIndices.add(i); - } - } - - /** @return a bound connection, or null if there are no free slots. */ - public ChildProcessConnection allocate(Context context, Bundle serviceBundle, - final ChildProcessConnection.ServiceCallback serviceCallback) { - assert isRunningOnLauncherThread(); - if (mFreeConnectionIndices.isEmpty()) { - Log.d(TAG, "Ran out of services to allocate."); - return null; - } - int slot = mFreeConnectionIndices.remove(0); - assert mChildProcessConnections[slot] == null; - ComponentName serviceName = new ComponentName(mPackageName, mServiceClassName + slot); - - // Wrap the service callbacks so that: - // - we can intercept onChildProcessDied and clean-up connections - // - the callbacks are actually posted so that this method will return before the callbacks - // are called (so that the caller may set any reference to the returned connection before - // any callback logic potentially tries to access that connection). - ChildProcessConnection.ServiceCallback serviceCallbackWrapper = - new ChildProcessConnection.ServiceCallback() { - @Override - public void onChildStarted() { - assert isRunningOnLauncherThread(); - if (serviceCallback != null) { - mLauncherHandler.post(new Runnable() { - @Override - public void run() { - serviceCallback.onChildStarted(); - } - }); - } - } - - @Override - public void onChildStartFailed(final ChildProcessConnection connection) { - assert isRunningOnLauncherThread(); - if (serviceCallback != null) { - mLauncherHandler.post(new Runnable() { - @Override - public void run() { - serviceCallback.onChildStartFailed(connection); - } - }); - } - freeConnectionWithDelay(connection); - } - - @Override - public void onChildProcessDied(final ChildProcessConnection connection) { - assert isRunningOnLauncherThread(); - if (serviceCallback != null) { - mLauncherHandler.post(new Runnable() { - @Override - public void run() { - serviceCallback.onChildProcessDied(connection); - } - }); - } - freeConnectionWithDelay(connection); - } - - private void freeConnectionWithDelay(final ChildProcessConnection connection) { - // Freeing a service should be delayed. This is so that we avoid immediately - // reusing the freed service (see http://crbug.com/164069): the framework - // might keep a service process alive when it's been unbound for a short - // time. If a new connection to the same service is bound at that point, the - // process is reused and bad things happen (mostly static variables are set - // when we don't expect them to). - mLauncherHandler.postDelayed(new Runnable() { - @Override - public void run() { - free(connection); - } - }, FREE_CONNECTION_DELAY_MILLIS); - } - }; - - ChildProcessConnection connection = mConnectionFactory.createConnection( - context, serviceName, mBindToCaller, mBindAsExternalService, serviceBundle); - mChildProcessConnections[slot] = connection; - - connection.start(mUseStrongBinding, serviceCallbackWrapper); - Log.d(TAG, "Allocator allocated and bound a connection, name: %s, slot: %d", - mServiceClassName, slot); - return connection; - } - - /** Frees a connection and notifies listeners. */ - private void free(ChildProcessConnection connection) { - assert isRunningOnLauncherThread(); - - // mChildProcessConnections is relatively short (20 items at max at this point). - // We are better of iterating than caching in a map. - int slot = Arrays.asList(mChildProcessConnections).indexOf(connection); - if (slot == -1) { - Log.e(TAG, "Unable to find connection to free."); - assert false; - } else { - mChildProcessConnections[slot] = null; - assert !mFreeConnectionIndices.contains(slot); - mFreeConnectionIndices.add(slot); - Log.d(TAG, "Allocator freed a connection, name: %s, slot: %d", mServiceClassName, slot); - } - - if (mPendingAllocations.isEmpty()) return; - mPendingAllocations.remove().run(); - assert mFreeConnectionIndices.isEmpty(); - if (!mPendingAllocations.isEmpty() && mFreeSlotCallback != null) mFreeSlotCallback.run(); - } - - // Can only be called once all slots are full, ie when allocate returns null. - // The callback will be called when a slot becomes free, and should synchronous call - // allocate to take the slot. - public void queueAllocation(Runnable runnable) { - assert mFreeConnectionIndices.isEmpty(); - boolean wasEmpty = mPendingAllocations.isEmpty(); - mPendingAllocations.add(runnable); - if (wasEmpty && mFreeSlotCallback != null) mFreeSlotCallback.run(); - } - - public String getPackageName() { - return mPackageName; - } - - public boolean anyConnectionAllocated() { - return mFreeConnectionIndices.size() < mChildProcessConnections.length; - } - - public boolean isFreeConnectionAvailable() { - assert isRunningOnLauncherThread(); - return !mFreeConnectionIndices.isEmpty(); - } - - public int getNumberOfServices() { - return mChildProcessConnections.length; - } - - public boolean isConnectionFromAllocator(ChildProcessConnection connection) { - for (ChildProcessConnection existingConnection : mChildProcessConnections) { - if (existingConnection == connection) return true; - } - return false; - } - - @VisibleForTesting - public void setConnectionFactoryForTesting(ConnectionFactory connectionFactory) { - mConnectionFactory = connectionFactory; - } - - /** @return the count of connections managed by the allocator */ - @VisibleForTesting - public int allocatedConnectionsCountForTesting() { - assert isRunningOnLauncherThread(); - return mChildProcessConnections.length - mFreeConnectionIndices.size(); - } - - @VisibleForTesting - public ChildProcessConnection getChildProcessConnectionAtSlotForTesting(int slotNumber) { - return mChildProcessConnections[slotNumber]; - } - - private boolean isRunningOnLauncherThread() { - return mLauncherHandler.getLooper() == Looper.myLooper(); - } -} diff --git a/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java b/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java deleted file mode 100644 index bfa5d5cc6..000000000 --- a/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java +++ /dev/null @@ -1,766 +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. - -package org.chromium.base.process_launcher; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.RemoteException; - -import org.chromium.base.ChildBindingState; -import org.chromium.base.Log; -import org.chromium.base.MemoryPressureLevel; -import org.chromium.base.MemoryPressureListener; -import org.chromium.base.ThreadUtils; -import org.chromium.base.TraceEvent; -import org.chromium.base.VisibleForTesting; -import org.chromium.base.memory.MemoryPressureCallback; - -import java.util.Arrays; -import java.util.List; - -import javax.annotation.Nullable; -import javax.annotation.concurrent.GuardedBy; - -/** - * Manages a connection between the browser activity and a child service. - */ -public class ChildProcessConnection { - private static final String TAG = "ChildProcessConn"; - private static final int NUM_BINDING_STATES = ChildBindingState.MAX_VALUE + 1; - - /** - * Used to notify the consumer about the process start. These callbacks will be invoked before - * the ConnectionCallbacks. - */ - public interface ServiceCallback { - /** - * Called when the child process has successfully started and is ready for connection - * setup. - */ - void onChildStarted(); - - /** - * Called when the child process failed to start. This can happen if the process is already - * in use by another client. The client will not receive any other callbacks after this one. - */ - void onChildStartFailed(ChildProcessConnection connection); - - /** - * Called when the service has been disconnected. whether it was stopped by the client or - * if it stopped unexpectedly (process crash). - * This is the last callback from this interface that a client will receive for a specific - * connection. - */ - void onChildProcessDied(ChildProcessConnection connection); - } - - /** - * Used to notify the consumer about the connection being established. - */ - public interface ConnectionCallback { - /** - * Called when the connection to the service is established. - * @param connection the connection object to the child process - */ - void onConnected(ChildProcessConnection connection); - } - - /** - * Delegate that ChildServiceConnection should call when the service connects/disconnects. - * These callbacks are expected to happen on a background thread. - */ - @VisibleForTesting - protected interface ChildServiceConnectionDelegate { - void onServiceConnected(IBinder service); - void onServiceDisconnected(); - } - - @VisibleForTesting - protected interface ChildServiceConnectionFactory { - ChildServiceConnection createConnection( - Intent bindIntent, int bindFlags, ChildServiceConnectionDelegate delegate); - } - - /** Interface representing a connection to the Android service. Can be mocked in unit-tests. */ - @VisibleForTesting - protected interface ChildServiceConnection { - boolean bind(); - void unbind(); - boolean isBound(); - } - - /** Implementation of ChildServiceConnection that does connect to a service. */ - private static class ChildServiceConnectionImpl - implements ChildServiceConnection, ServiceConnection { - private final Context mContext; - private final Intent mBindIntent; - private final int mBindFlags; - private final ChildServiceConnectionDelegate mDelegate; - private boolean mBound; - - private ChildServiceConnectionImpl(Context context, Intent bindIntent, int bindFlags, - ChildServiceConnectionDelegate delegate) { - mContext = context; - mBindIntent = bindIntent; - mBindFlags = bindFlags; - mDelegate = delegate; - } - - @Override - public boolean bind() { - if (!mBound) { - try { - TraceEvent.begin("ChildProcessConnection.ChildServiceConnectionImpl.bind"); - mBound = mContext.bindService(mBindIntent, this, mBindFlags); - } finally { - TraceEvent.end("ChildProcessConnection.ChildServiceConnectionImpl.bind"); - } - } - return mBound; - } - - @Override - public void unbind() { - if (mBound) { - mContext.unbindService(this); - mBound = false; - } - } - - @Override - public boolean isBound() { - return mBound; - } - - @Override - public void onServiceConnected(ComponentName className, final IBinder service) { - mDelegate.onServiceConnected(service); - } - - // Called on the main thread to notify that the child service did not disconnect gracefully. - @Override - public void onServiceDisconnected(ComponentName className) { - mDelegate.onServiceDisconnected(); - } - } - - // Synchronize on this for access. - @GuardedBy("sAllBindingStateCounts") - private static final int[] sAllBindingStateCounts = new int[NUM_BINDING_STATES]; - - @VisibleForTesting - static void resetBindingStateCountsForTesting() { - synchronized (sAllBindingStateCounts) { - for (int i = 0; i < NUM_BINDING_STATES; ++i) { - sAllBindingStateCounts[i] = 0; - } - } - } - - private final Handler mLauncherHandler; - private final ComponentName mServiceName; - - // Parameters passed to the child process through the service binding intent. - // If the service gets recreated by the framework the intent will be reused, so these parameters - // should be common to all processes of that type. - private final Bundle mServiceBundle; - - // Whether bindToCaller should be called on the service after setup to check that only one - // process is bound to the service. - private final boolean mBindToCaller; - - private static class ConnectionParams { - final Bundle mConnectionBundle; - final List mClientInterfaces; - - ConnectionParams(Bundle connectionBundle, List clientInterfaces) { - mConnectionBundle = connectionBundle; - mClientInterfaces = clientInterfaces; - } - } - - // This is set in start() and is used in onServiceConnected(). - private ServiceCallback mServiceCallback; - - // This is set in setupConnection() and is later used in doConnectionSetup(), after which the - // variable is cleared. Therefore this is only valid while the connection is being set up. - private ConnectionParams mConnectionParams; - - // Callback provided in setupConnection() that will communicate the result to the caller. This - // has to be called exactly once after setupConnection(), even if setup fails, so that the - // caller can free up resources associated with the setup attempt. This is set to null after the - // call. - private ConnectionCallback mConnectionCallback; - - private IChildProcessService mService; - - // Set to true when the service connection callback runs. This differs from - // mServiceConnectComplete, which tracks that the connection completed successfully. - private boolean mDidOnServiceConnected; - - // Set to true when the service connected successfully. - private boolean mServiceConnectComplete; - - // Set to true when the service disconnects, as opposed to being properly closed. This happens - // when the process crashes or gets killed by the system out-of-memory killer. - private boolean mServiceDisconnected; - - // Process ID of the corresponding child process. - private int mPid; - - // Strong binding will make the service priority equal to the priority of the activity. - private final ChildServiceConnection mStrongBinding; - - // Moderate binding will make the service priority equal to the priority of a visible process - // while the app is in the foreground. - // This is also used as the initial binding before any priorities are set. - private final ChildServiceConnection mModerateBinding; - - // Low priority binding maintained in the entire lifetime of the connection, i.e. between calls - // to start() and stop(). - private final ChildServiceConnection mWaivedBinding; - - // Refcount of bindings. - private int mStrongBindingCount; - private int mModerateBindingCount; - - // Set to true once unbind() was called. - private boolean mUnbound; - - // Binding state of this connection. - private @ChildBindingState int mBindingState; - - // Protects access to instance variables that are also accessed on the client thread. - private final Object mClientThreadLock = new Object(); - - // Same as above except it no longer updates after |unbind()|. - @GuardedBy("mClientThreadLock") - private @ChildBindingState int mBindingStateCurrentOrWhenDied; - - // Indicate |kill()| was called to intentionally kill this process. - @GuardedBy("mClientThreadLock") - private boolean mKilledByUs; - - // Copy of |sAllBindingStateCounts| at the time this is unbound. - @GuardedBy("mClientThreadLock") - private int[] mAllBindingStateCountsWhenDied; - - private MemoryPressureCallback mMemoryPressureCallback; - - public ChildProcessConnection(Context context, ComponentName serviceName, boolean bindToCaller, - boolean bindAsExternalService, Bundle serviceBundle) { - this(context, serviceName, bindToCaller, bindAsExternalService, serviceBundle, - null /* connectionFactory */); - } - - @VisibleForTesting - public ChildProcessConnection(final Context context, ComponentName serviceName, - boolean bindToCaller, boolean bindAsExternalService, Bundle serviceBundle, - ChildServiceConnectionFactory connectionFactory) { - mLauncherHandler = new Handler(); - assert isRunningOnLauncherThread(); - mServiceName = serviceName; - mServiceBundle = serviceBundle != null ? serviceBundle : new Bundle(); - mServiceBundle.putBoolean(ChildProcessConstants.EXTRA_BIND_TO_CALLER, bindToCaller); - mBindToCaller = bindToCaller; - - if (connectionFactory == null) { - connectionFactory = new ChildServiceConnectionFactory() { - @Override - public ChildServiceConnection createConnection( - Intent bindIntent, int bindFlags, ChildServiceConnectionDelegate delegate) { - return new ChildServiceConnectionImpl(context, bindIntent, bindFlags, delegate); - } - }; - } - - ChildServiceConnectionDelegate delegate = new ChildServiceConnectionDelegate() { - @Override - public void onServiceConnected(final IBinder service) { - mLauncherHandler.post(new Runnable() { - @Override - public void run() { - onServiceConnectedOnLauncherThread(service); - } - }); - } - - @Override - public void onServiceDisconnected() { - mLauncherHandler.post(new Runnable() { - @Override - public void run() { - onServiceDisconnectedOnLauncherThread(); - } - }); - } - }; - - Intent intent = new Intent(); - intent.setComponent(serviceName); - if (serviceBundle != null) { - intent.putExtras(serviceBundle); - } - - int defaultFlags = Context.BIND_AUTO_CREATE - | (bindAsExternalService ? Context.BIND_EXTERNAL_SERVICE : 0); - - mModerateBinding = connectionFactory.createConnection(intent, defaultFlags, delegate); - mStrongBinding = connectionFactory.createConnection( - intent, defaultFlags | Context.BIND_IMPORTANT, delegate); - mWaivedBinding = connectionFactory.createConnection( - intent, defaultFlags | Context.BIND_WAIVE_PRIORITY, delegate); - } - - public final IChildProcessService getService() { - assert isRunningOnLauncherThread(); - return mService; - } - - public final ComponentName getServiceName() { - assert isRunningOnLauncherThread(); - return mServiceName; - } - - public boolean isConnected() { - return mService != null; - } - - /** - * @return the connection pid, or 0 if not yet connected - */ - public int getPid() { - assert isRunningOnLauncherThread(); - return mPid; - } - - /** - * Starts a connection to an IChildProcessService. This must be followed by a call to - * setupConnection() to setup the connection parameters. start() and setupConnection() are - * separate to allow to pass whatever parameters are available in start(), and complete the - * remainder addStrongBinding while reducing the connection setup latency. - * @param useStrongBinding whether a strong binding should be bound by default. If false, an - * initial moderate binding is used. - * @param serviceCallback (optional) callbacks invoked when the child process starts or fails to - * start and when the service stops. - */ - public void start(boolean useStrongBinding, ServiceCallback serviceCallback) { - try { - TraceEvent.begin("ChildProcessConnection.start"); - assert isRunningOnLauncherThread(); - assert mConnectionParams - == null : "setupConnection() called before start() in ChildProcessConnection."; - - mServiceCallback = serviceCallback; - - if (!bind(useStrongBinding)) { - Log.e(TAG, "Failed to establish the service connection."); - // We have to notify the caller so that they can free-up associated resources. - // TODO(ppi): Can we hard-fail here? - notifyChildProcessDied(); - } - } finally { - TraceEvent.end("ChildProcessConnection.start"); - } - } - - /** - * Sets-up the connection after it was started with start(). - * @param connectionBundle a bundle passed to the service that can be used to pass various - * parameters to the service - * @param clientInterfaces optional client specified interfaces that the child can use to - * communicate with the parent process - * @param connectionCallback will be called exactly once after the connection is set up or the - * setup fails - */ - public void setupConnection(Bundle connectionBundle, @Nullable List clientInterfaces, - ConnectionCallback connectionCallback) { - assert isRunningOnLauncherThread(); - assert mConnectionParams == null; - if (mServiceDisconnected) { - Log.w(TAG, "Tried to setup a connection that already disconnected."); - connectionCallback.onConnected(null); - return; - } - try { - TraceEvent.begin("ChildProcessConnection.setupConnection"); - mConnectionCallback = connectionCallback; - mConnectionParams = new ConnectionParams(connectionBundle, clientInterfaces); - // Run the setup if the service is already connected. If not, doConnectionSetup() will - // be called from onServiceConnected(). - if (mServiceConnectComplete) { - doConnectionSetup(); - } - } finally { - TraceEvent.end("ChildProcessConnection.setupConnection"); - } - } - - /** - * Terminates the connection to IChildProcessService, closing all bindings. It is safe to call - * this multiple times. - */ - public void stop() { - assert isRunningOnLauncherThread(); - unbind(); - notifyChildProcessDied(); - } - - public void kill() { - assert isRunningOnLauncherThread(); - IChildProcessService service = mService; - unbind(); - try { - if (service != null) service.forceKill(); - } catch (RemoteException e) { - // Intentionally ignore since we are killing it anyway. - } - synchronized (mClientThreadLock) { - mKilledByUs = true; - } - notifyChildProcessDied(); - } - - private void onServiceConnectedOnLauncherThread(IBinder service) { - assert isRunningOnLauncherThread(); - // A flag from the parent class ensures we run the post-connection logic only once - // (instead of once per each ChildServiceConnection). - if (mDidOnServiceConnected) { - return; - } - try { - TraceEvent.begin("ChildProcessConnection.ChildServiceConnection.onServiceConnected"); - mDidOnServiceConnected = true; - mService = IChildProcessService.Stub.asInterface(service); - - if (mBindToCaller) { - try { - if (!mService.bindToCaller()) { - if (mServiceCallback != null) { - mServiceCallback.onChildStartFailed(this); - } - unbind(); - return; - } - } catch (RemoteException ex) { - // Do not trigger the StartCallback here, since the service is already - // dead and the onChildStopped callback will run from onServiceDisconnected(). - Log.e(TAG, "Failed to bind service to connection.", ex); - return; - } - } - - if (mServiceCallback != null) { - mServiceCallback.onChildStarted(); - } - - mServiceConnectComplete = true; - - if (mMemoryPressureCallback == null) { - final MemoryPressureCallback callback = this ::onMemoryPressure; - ThreadUtils.postOnUiThread(() -> MemoryPressureListener.addCallback(callback)); - mMemoryPressureCallback = callback; - } - - // Run the setup if the connection parameters have already been provided. If - // not, doConnectionSetup() will be called from setupConnection(). - if (mConnectionParams != null) { - doConnectionSetup(); - } - } finally { - TraceEvent.end("ChildProcessConnection.ChildServiceConnection.onServiceConnected"); - } - } - - private void onServiceDisconnectedOnLauncherThread() { - assert isRunningOnLauncherThread(); - // Ensure that the disconnection logic runs only once (instead of once per each - // ChildServiceConnection). - if (mServiceDisconnected) { - return; - } - mServiceDisconnected = true; - Log.w(TAG, "onServiceDisconnected (crash or killed by oom): pid=%d", mPid); - stop(); // We don't want to auto-restart on crash. Let the browser do that. - - // If we have a pending connection callback, we need to communicate the failure to - // the caller. - if (mConnectionCallback != null) { - mConnectionCallback.onConnected(null); - mConnectionCallback = null; - } - } - - private void onSetupConnectionResult(int pid) { - mPid = pid; - assert mPid != 0 : "Child service claims to be run by a process of pid=0."; - - if (mConnectionCallback != null) { - mConnectionCallback.onConnected(this); - } - mConnectionCallback = null; - } - - /** - * Called after the connection parameters have been set (in setupConnection()) *and* a - * connection has been established (as signaled by onServiceConnected()). These two events can - * happen in any order. - */ - private void doConnectionSetup() { - try { - TraceEvent.begin("ChildProcessConnection.doConnectionSetup"); - assert mServiceConnectComplete && mService != null; - assert mConnectionParams != null; - - ICallbackInt pidCallback = new ICallbackInt.Stub() { - @Override - public void call(final int pid) { - mLauncherHandler.post(new Runnable() { - @Override - public void run() { - onSetupConnectionResult(pid); - } - }); - } - }; - try { - mService.setupConnection(mConnectionParams.mConnectionBundle, pidCallback, - mConnectionParams.mClientInterfaces); - } catch (RemoteException re) { - Log.e(TAG, "Failed to setup connection.", re); - } - mConnectionParams = null; - } finally { - TraceEvent.end("ChildProcessConnection.doConnectionSetup"); - } - } - - private boolean bind(boolean useStrongBinding) { - assert isRunningOnLauncherThread(); - assert !mUnbound; - - boolean success; - if (useStrongBinding) { - success = mStrongBinding.bind(); - } else { - mModerateBindingCount++; - success = mModerateBinding.bind(); - } - if (!success) return false; - - mWaivedBinding.bind(); - updateBindingState(); - return true; - } - - @VisibleForTesting - protected void unbind() { - assert isRunningOnLauncherThread(); - mService = null; - mConnectionParams = null; - mUnbound = true; - mStrongBinding.unbind(); - mWaivedBinding.unbind(); - mModerateBinding.unbind(); - updateBindingState(); - - int[] bindingStateCounts; - synchronized (sAllBindingStateCounts) { - bindingStateCounts = Arrays.copyOf(sAllBindingStateCounts, NUM_BINDING_STATES); - } - synchronized (mClientThreadLock) { - mAllBindingStateCountsWhenDied = bindingStateCounts; - } - - if (mMemoryPressureCallback != null) { - final MemoryPressureCallback callback = mMemoryPressureCallback; - ThreadUtils.postOnUiThread(() -> MemoryPressureListener.removeCallback(callback)); - mMemoryPressureCallback = null; - } - } - - public boolean isStrongBindingBound() { - assert isRunningOnLauncherThread(); - return mStrongBinding.isBound(); - } - - public void addStrongBinding() { - assert isRunningOnLauncherThread(); - if (!isConnected()) { - Log.w(TAG, "The connection is not bound for %d", getPid()); - return; - } - if (mStrongBindingCount == 0) { - mStrongBinding.bind(); - updateBindingState(); - } - mStrongBindingCount++; - } - - public void removeStrongBinding() { - assert isRunningOnLauncherThread(); - if (!isConnected()) { - Log.w(TAG, "The connection is not bound for %d", getPid()); - return; - } - assert mStrongBindingCount > 0; - mStrongBindingCount--; - if (mStrongBindingCount == 0) { - mStrongBinding.unbind(); - updateBindingState(); - } - } - - public boolean isModerateBindingBound() { - assert isRunningOnLauncherThread(); - return mModerateBinding.isBound(); - } - - public void addModerateBinding() { - assert isRunningOnLauncherThread(); - if (!isConnected()) { - Log.w(TAG, "The connection is not bound for %d", getPid()); - return; - } - if (mModerateBindingCount == 0) { - mModerateBinding.bind(); - updateBindingState(); - } - mModerateBindingCount++; - } - - public void removeModerateBinding() { - assert isRunningOnLauncherThread(); - if (!isConnected()) { - Log.w(TAG, "The connection is not bound for %d", getPid()); - return; - } - assert mModerateBindingCount > 0; - mModerateBindingCount--; - if (mModerateBindingCount == 0) { - mModerateBinding.unbind(); - updateBindingState(); - } - } - - /** - * @return true if the connection is bound and only bound with the waived binding or if the - * connection is unbound and was only bound with the waived binding when it disconnected. - */ - public @ChildBindingState int bindingStateCurrentOrWhenDied() { - // WARNING: this method can be called from a thread other than the launcher thread. - // Note that it returns the current waived bound only state and is racy. This not really - // preventable without changing the caller's API, short of blocking. - synchronized (mClientThreadLock) { - return mBindingStateCurrentOrWhenDied; - } - } - - /** - * @return true if the connection is intentionally killed by calling kill(). - */ - public boolean isKilledByUs() { - // WARNING: this method can be called from a thread other than the launcher thread. - // Note that it returns the current waived bound only state and is racy. This not really - // preventable without changing the caller's API, short of blocking. - synchronized (mClientThreadLock) { - return mKilledByUs; - } - } - - public int[] bindingStateCountsCurrentOrWhenDied() { - // WARNING: this method can be called from a thread other than the launcher thread. - // Note that it returns the current waived bound only state and is racy. This not really - // preventable without changing the caller's API, short of blocking. - synchronized (mClientThreadLock) { - if (mAllBindingStateCountsWhenDied != null) { - return Arrays.copyOf(mAllBindingStateCountsWhenDied, NUM_BINDING_STATES); - } - } - synchronized (sAllBindingStateCounts) { - return Arrays.copyOf(sAllBindingStateCounts, NUM_BINDING_STATES); - } - } - - // Should be called any binding is bound or unbound. - private void updateBindingState() { - int oldBindingState = mBindingState; - if (mUnbound) { - mBindingState = ChildBindingState.UNBOUND; - } else if (mStrongBinding.isBound()) { - mBindingState = ChildBindingState.STRONG; - } else if (mModerateBinding.isBound()) { - mBindingState = ChildBindingState.MODERATE; - } else { - assert mWaivedBinding.isBound(); - mBindingState = ChildBindingState.WAIVED; - } - - if (mBindingState != oldBindingState) { - synchronized (sAllBindingStateCounts) { - if (oldBindingState != ChildBindingState.UNBOUND) { - assert sAllBindingStateCounts[oldBindingState] > 0; - sAllBindingStateCounts[oldBindingState]--; - } - if (mBindingState != ChildBindingState.UNBOUND) { - sAllBindingStateCounts[mBindingState]++; - } - } - } - - if (!mUnbound) { - synchronized (mClientThreadLock) { - mBindingStateCurrentOrWhenDied = mBindingState; - } - } - } - - private void notifyChildProcessDied() { - if (mServiceCallback != null) { - // Guard against nested calls to this method. - ServiceCallback serviceCallback = mServiceCallback; - mServiceCallback = null; - serviceCallback.onChildProcessDied(this); - } - } - - private boolean isRunningOnLauncherThread() { - return mLauncherHandler.getLooper() == Looper.myLooper(); - } - - @VisibleForTesting - public void crashServiceForTesting() throws RemoteException { - mService.forceKill(); - } - - @VisibleForTesting - public boolean didOnServiceConnectedForTesting() { - return mDidOnServiceConnected; - } - - @VisibleForTesting - protected Handler getLauncherHandler() { - return mLauncherHandler; - } - - private void onMemoryPressure(@MemoryPressureLevel int pressure) { - mLauncherHandler.post(() -> onMemoryPressureOnLauncherThread(pressure)); - } - - private void onMemoryPressureOnLauncherThread(@MemoryPressureLevel int pressure) { - if (mService == null) return; - try { - mService.onMemoryPressure(pressure); - } catch (RemoteException ex) { - // Ignore - } - } -} diff --git a/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java b/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java deleted file mode 100644 index ec232d7c1..000000000 --- a/android/java/src/org/chromium/base/process_launcher/ChildProcessConstants.java +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.process_launcher; - -/** - * Constants to be used by child processes. - */ -public interface ChildProcessConstants { - // Below are the names for the items placed in the bind or start command intent. - // Note that because that intent maybe reused if a service is restarted, none should be process - // specific. - - public static final String EXTRA_BIND_TO_CALLER = - "org.chromium.base.process_launcher.extra.bind_to_caller"; - - // Below are the names for the items placed in the Bundle passed in the - // IChildProcessService.setupConnection call, once the connection has been established. - - // Key for the command line. - public static final String EXTRA_COMMAND_LINE = - "org.chromium.base.process_launcher.extra.command_line"; - - // Key for the file descriptors that should be mapped in the child process. - public static final String EXTRA_FILES = "org.chromium.base.process_launcher.extra.extraFiles"; -} diff --git a/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java b/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java deleted file mode 100644 index 7cdc8528b..000000000 --- a/android/java/src/org/chromium/base/process_launcher/ChildProcessLauncher.java +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.process_launcher; - -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; - -import org.chromium.base.ContextUtils; -import org.chromium.base.Log; -import org.chromium.base.TraceEvent; - -import java.io.IOException; -import java.util.List; - -/** - * This class is used to start a child process by connecting to a ChildProcessService. - */ -public class ChildProcessLauncher { - private static final String TAG = "ChildProcLauncher"; - - /** Delegate that client should use to customize the process launching. */ - public abstract static class Delegate { - /** - * Called when the launcher is about to start. Gives the embedder a chance to provide an - * already bound connection if it has one. (allowing for warm-up connections: connections - * that are already bound in advance to speed up child process start-up time). - * Note that onBeforeConnectionAllocated will not be called if this method returns a - * connection. - * @param connectionAllocator the allocator the returned connection should have been - * allocated of. - * @param serviceCallback the service callback that the connection should use. - * @return a bound connection to use to connect to the child process service, or null if a - * connection should be allocated and bound by the launcher. - */ - public ChildProcessConnection getBoundConnection( - ChildConnectionAllocator connectionAllocator, - ChildProcessConnection.ServiceCallback serviceCallback) { - return null; - } - - /** - * Called before a connection is allocated. - * Note that this is only called if the ChildProcessLauncher is created with - * {@link #createWithConnectionAllocator}. - * @param serviceBundle the bundle passed in the service intent. Clients can add their own - * extras to the bundle. - */ - public void onBeforeConnectionAllocated(Bundle serviceBundle) {} - - /** - * Called before setup is called on the connection. - * @param connectionBundle the bundle passed to the {@link ChildProcessService} in the - * setup call. Clients can add their own extras to the bundle. - */ - public void onBeforeConnectionSetup(Bundle connectionBundle) {} - - /** - * Called when the connection was successfully established, meaning the setup call on the - * service was successful. - * @param connection the connection over which the setup call was made. - */ - public void onConnectionEstablished(ChildProcessConnection connection) {} - - /** - * Called when a connection has been disconnected. Only invoked if onConnectionEstablished - * was called, meaning the connection was already established. - * @param connection the connection that got disconnected. - */ - public void onConnectionLost(ChildProcessConnection connection) {} - } - - // Represents an invalid process handle; same as base/process/process.h kNullProcessHandle. - private static final int NULL_PROCESS_HANDLE = 0; - - // The handle for the thread we were created on and on which all methods should be called. - private final Handler mLauncherHandler; - - private final Delegate mDelegate; - - private final String[] mCommandLine; - private final FileDescriptorInfo[] mFilesToBeMapped; - - // The allocator used to create the connection. - private final ChildConnectionAllocator mConnectionAllocator; - - // The IBinder interfaces provided to the created service. - private final List mClientInterfaces; - - // The actual service connection. Set once we have connected to the service. - private ChildProcessConnection mConnection; - - /** - * Constructor. - * - * @param launcherHandler the handler for the thread where all operations should happen. - * @param delegate the delagate that gets notified of the launch progress. - * @param commandLine the command line that should be passed to the started process. - * @param filesToBeMapped the files that should be passed to the started process. - * @param connectionAllocator the allocator used to create connections to the service. - * @param clientInterfaces the interfaces that should be passed to the started process so it can - * communicate with the parent process. - */ - public ChildProcessLauncher(Handler launcherHandler, Delegate delegate, String[] commandLine, - FileDescriptorInfo[] filesToBeMapped, ChildConnectionAllocator connectionAllocator, - List clientInterfaces) { - assert connectionAllocator != null; - mLauncherHandler = launcherHandler; - isRunningOnLauncherThread(); - mCommandLine = commandLine; - mConnectionAllocator = connectionAllocator; - mDelegate = delegate; - mFilesToBeMapped = filesToBeMapped; - mClientInterfaces = clientInterfaces; - } - - /** - * Starts the child process and calls setup on it if {@param setupConnection} is true. - * @param setupConnection whether the setup should be performed on the connection once - * established - * @param queueIfNoFreeConnection whether to queue that request if no service connection is - * available. If the launcher was created with a connection provider, this parameter has no - * effect. - * @return true if the connection was started or was queued. - */ - public boolean start(final boolean setupConnection, final boolean queueIfNoFreeConnection) { - assert isRunningOnLauncherThread(); - try { - TraceEvent.begin("ChildProcessLauncher.start"); - ChildProcessConnection.ServiceCallback serviceCallback = - new ChildProcessConnection.ServiceCallback() { - @Override - public void onChildStarted() {} - - @Override - public void onChildStartFailed(ChildProcessConnection connection) { - assert isRunningOnLauncherThread(); - assert mConnection == connection; - Log.e(TAG, "ChildProcessConnection.start failed, trying again"); - mLauncherHandler.post(new Runnable() { - @Override - public void run() { - // The child process may already be bound to another client - // (this can happen if multi-process WebView is used in more - // than one process), so try starting the process again. - // This connection that failed to start has not been freed, - // so a new bound connection will be allocated. - mConnection = null; - start(setupConnection, queueIfNoFreeConnection); - } - }); - } - - @Override - public void onChildProcessDied(ChildProcessConnection connection) { - assert isRunningOnLauncherThread(); - assert mConnection == connection; - ChildProcessLauncher.this.onChildProcessDied(); - } - }; - mConnection = mDelegate.getBoundConnection(mConnectionAllocator, serviceCallback); - if (mConnection != null) { - assert mConnectionAllocator.isConnectionFromAllocator(mConnection); - setupConnection(); - return true; - } - if (!allocateAndSetupConnection( - serviceCallback, setupConnection, queueIfNoFreeConnection) - && !queueIfNoFreeConnection) { - return false; - } - return true; - } finally { - TraceEvent.end("ChildProcessLauncher.start"); - } - } - - public ChildProcessConnection getConnection() { - return mConnection; - } - - public ChildConnectionAllocator getConnectionAllocator() { - return mConnectionAllocator; - } - - private boolean allocateAndSetupConnection( - final ChildProcessConnection.ServiceCallback serviceCallback, - final boolean setupConnection, final boolean queueIfNoFreeConnection) { - assert mConnection == null; - Bundle serviceBundle = new Bundle(); - mDelegate.onBeforeConnectionAllocated(serviceBundle); - - mConnection = mConnectionAllocator.allocate( - ContextUtils.getApplicationContext(), serviceBundle, serviceCallback); - if (mConnection == null) { - if (!queueIfNoFreeConnection) { - Log.d(TAG, "Failed to allocate a child connection (no queuing)."); - return false; - } - mConnectionAllocator.queueAllocation( - () -> allocateAndSetupConnection( - serviceCallback, setupConnection, queueIfNoFreeConnection)); - return false; - } - - if (setupConnection) { - setupConnection(); - } - return true; - } - - private void setupConnection() { - ChildProcessConnection.ConnectionCallback connectionCallback = - new ChildProcessConnection.ConnectionCallback() { - @Override - public void onConnected(ChildProcessConnection connection) { - assert mConnection == connection; - onServiceConnected(); - } - }; - Bundle connectionBundle = createConnectionBundle(); - mDelegate.onBeforeConnectionSetup(connectionBundle); - mConnection.setupConnection(connectionBundle, getClientInterfaces(), connectionCallback); - } - - private void onServiceConnected() { - assert isRunningOnLauncherThread(); - - Log.d(TAG, "on connect callback, pid=%d", mConnection.getPid()); - - mDelegate.onConnectionEstablished(mConnection); - - // Proactively close the FDs rather than waiting for the GC to do it. - try { - for (FileDescriptorInfo fileInfo : mFilesToBeMapped) { - fileInfo.fd.close(); - } - } catch (IOException ioe) { - Log.w(TAG, "Failed to close FD.", ioe); - } - } - - public int getPid() { - assert isRunningOnLauncherThread(); - return mConnection == null ? NULL_PROCESS_HANDLE : mConnection.getPid(); - } - - public List getClientInterfaces() { - return mClientInterfaces; - } - - private boolean isRunningOnLauncherThread() { - return mLauncherHandler.getLooper() == Looper.myLooper(); - } - - private Bundle createConnectionBundle() { - Bundle bundle = new Bundle(); - bundle.putStringArray(ChildProcessConstants.EXTRA_COMMAND_LINE, mCommandLine); - bundle.putParcelableArray(ChildProcessConstants.EXTRA_FILES, mFilesToBeMapped); - return bundle; - } - - private void onChildProcessDied() { - assert isRunningOnLauncherThread(); - if (getPid() != 0) { - mDelegate.onConnectionLost(mConnection); - } - } - - public void stop() { - assert isRunningOnLauncherThread(); - Log.d(TAG, "stopping child connection: pid=%d", mConnection.getPid()); - mConnection.stop(); - } -} diff --git a/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java b/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java deleted file mode 100644 index 876ebbb17..000000000 --- a/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.process_launcher; - -import android.app.Service; -import android.content.Intent; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Parcelable; -import android.os.Process; -import android.os.RemoteException; -import android.util.SparseArray; - -import org.chromium.base.BaseSwitches; -import org.chromium.base.CommandLine; -import org.chromium.base.ContextUtils; -import org.chromium.base.Log; -import org.chromium.base.MemoryPressureLevel; -import org.chromium.base.ThreadUtils; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.MainDex; -import org.chromium.base.memory.MemoryPressureMonitor; - -import java.util.List; -import java.util.concurrent.Semaphore; - -import javax.annotation.concurrent.GuardedBy; - -/** - * This is the base class for child services; the embedding application should contain - * ProcessService0, 1.. etc subclasses that provide the concrete service entry points, so it can - * connect to more than one distinct process (i.e. one process per service number, up to limit of - * N). - * The embedding application must declare these service instances in the application section - * of its AndroidManifest.xml, first with some meta-data describing the services: - * - * and then N entries of the form: - * - * - * Subclasses must also provide a delegate in this class constructor. That delegate is responsible - * for loading native libraries and running the main entry point of the service. - */ -@JNINamespace("base::android") -@MainDex -public abstract class ChildProcessService extends Service { - private static final String MAIN_THREAD_NAME = "ChildProcessMain"; - private static final String TAG = "ChildProcessService"; - - // Only for a check that create is only called once. - private static boolean sCreateCalled; - - private final ChildProcessServiceDelegate mDelegate; - - private final Object mBinderLock = new Object(); - private final Object mLibraryInitializedLock = new Object(); - - // True if we should enforce that bindToCaller() is called before setupConnection(). - // Only set once in bind(), does not require synchronization. - private boolean mBindToCallerCheck; - - // PID of the client of this service, set in bindToCaller(), if mBindToCallerCheck is true. - @GuardedBy("mBinderLock") - private int mBoundCallingPid; - - // This is the native "Main" thread for the renderer / utility process. - private Thread mMainThread; - - // Parameters received via IPC, only accessed while holding the mMainThread monitor. - private String[] mCommandLineParams; - - // File descriptors that should be registered natively. - private FileDescriptorInfo[] mFdInfos; - - @GuardedBy("mLibraryInitializedLock") - private boolean mLibraryInitialized; - - // Called once the service is bound and all service related member variables have been set. - // Only set once in bind(), does not require synchronization. - private boolean mServiceBound; - - private final Semaphore mActivitySemaphore = new Semaphore(1); - - public ChildProcessService(ChildProcessServiceDelegate delegate) { - mDelegate = delegate; - } - - // Binder object used by clients for this service. - private final IChildProcessService.Stub mBinder = new IChildProcessService.Stub() { - // NOTE: Implement any IChildProcessService methods here. - @Override - public boolean bindToCaller() { - assert mBindToCallerCheck; - assert mServiceBound; - synchronized (mBinderLock) { - int callingPid = Binder.getCallingPid(); - if (mBoundCallingPid == 0) { - mBoundCallingPid = callingPid; - } else if (mBoundCallingPid != callingPid) { - Log.e(TAG, "Service is already bound by pid %d, cannot bind for pid %d", - mBoundCallingPid, callingPid); - return false; - } - } - return true; - } - - @Override - public void setupConnection(Bundle args, ICallbackInt pidCallback, List callbacks) - throws RemoteException { - assert mServiceBound; - synchronized (mBinderLock) { - if (mBindToCallerCheck && mBoundCallingPid == 0) { - Log.e(TAG, "Service has not been bound with bindToCaller()"); - pidCallback.call(-1); - return; - } - } - - pidCallback.call(Process.myPid()); - processConnectionBundle(args, callbacks); - } - - @Override - public void forceKill() { - assert mServiceBound; - Process.killProcess(Process.myPid()); - } - - @Override - public void onMemoryPressure(@MemoryPressureLevel int pressure) { - // This method is called by the host process when the host process reports pressure - // to its native side. The key difference between the host process and its services is - // that the host process polls memory pressure when it gets CRITICAL, and periodically - // invokes pressure listeners until pressure subsides. (See MemoryPressureMonitor for - // more info.) - // - // Services don't poll, so this side-channel is used to notify services about memory - // pressure from the host process's POV. - // - // However, since both host process and services listen to ComponentCallbacks2, we - // can't be sure that the host process won't get better signals than their services. - // I.e. we need to watch out for a situation where a service gets CRITICAL, but the - // host process gets MODERATE - in this case we need to ignore MODERATE. - // - // So we're ignoring pressure from the host process if it's better than the last - // reported pressure. I.e. the host process can drive pressure up, but it'll go - // down only when we the service get a signal through ComponentCallbacks2. - ThreadUtils.postOnUiThread(() -> { - if (pressure >= MemoryPressureMonitor.INSTANCE.getLastReportedPressure()) { - MemoryPressureMonitor.INSTANCE.notifyPressure(pressure); - } - }); - } - }; - - /** - * Loads Chrome's native libraries and initializes a ChildProcessService. - */ - // For sCreateCalled check. - @Override - public void onCreate() { - super.onCreate(); - Log.i(TAG, "Creating new ChildProcessService pid=%d", Process.myPid()); - if (sCreateCalled) { - throw new RuntimeException("Illegal child process reuse."); - } - sCreateCalled = true; - - // Initialize the context for the application that owns this ChildProcessService object. - ContextUtils.initApplicationContext(getApplicationContext()); - - mDelegate.onServiceCreated(); - - mMainThread = new Thread(new Runnable() { - @Override - public void run() { - try { - // CommandLine must be initialized before everything else. - synchronized (mMainThread) { - while (mCommandLineParams == null) { - mMainThread.wait(); - } - } - assert mServiceBound; - CommandLine.init(mCommandLineParams); - - if (CommandLine.getInstance().hasSwitch( - BaseSwitches.RENDERER_WAIT_FOR_JAVA_DEBUGGER)) { - android.os.Debug.waitForDebugger(); - } - - boolean nativeLibraryLoaded = false; - try { - nativeLibraryLoaded = mDelegate.loadNativeLibrary(getApplicationContext()); - } catch (Exception e) { - Log.e(TAG, "Failed to load native library.", e); - } - if (!nativeLibraryLoaded) { - System.exit(-1); - } - - synchronized (mLibraryInitializedLock) { - mLibraryInitialized = true; - mLibraryInitializedLock.notifyAll(); - } - synchronized (mMainThread) { - mMainThread.notifyAll(); - while (mFdInfos == null) { - mMainThread.wait(); - } - } - - SparseArray idsToKeys = mDelegate.getFileDescriptorsIdsToKeys(); - - int[] fileIds = new int[mFdInfos.length]; - String[] keys = new String[mFdInfos.length]; - int[] fds = new int[mFdInfos.length]; - long[] regionOffsets = new long[mFdInfos.length]; - long[] regionSizes = new long[mFdInfos.length]; - for (int i = 0; i < mFdInfos.length; i++) { - FileDescriptorInfo fdInfo = mFdInfos[i]; - String key = idsToKeys != null ? idsToKeys.get(fdInfo.id) : null; - if (key != null) { - keys[i] = key; - } else { - fileIds[i] = fdInfo.id; - } - fds[i] = fdInfo.fd.detachFd(); - regionOffsets[i] = fdInfo.offset; - regionSizes[i] = fdInfo.size; - } - nativeRegisterFileDescriptors(keys, fileIds, fds, regionOffsets, regionSizes); - - mDelegate.onBeforeMain(); - if (mActivitySemaphore.tryAcquire()) { - mDelegate.runMain(); - nativeExitChildProcess(); - } - } catch (InterruptedException e) { - Log.w(TAG, "%s startup failed: %s", MAIN_THREAD_NAME, e); - } - } - }, MAIN_THREAD_NAME); - mMainThread.start(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.i(TAG, "Destroying ChildProcessService pid=%d", Process.myPid()); - if (mActivitySemaphore.tryAcquire()) { - // TODO(crbug.com/457406): This is a bit hacky, but there is no known better solution - // as this service will get reused (at least if not sandboxed). - // In fact, we might really want to always exit() from onDestroy(), not just from - // the early return here. - System.exit(0); - return; - } - synchronized (mLibraryInitializedLock) { - try { - while (!mLibraryInitialized) { - // Avoid a potential race in calling through to native code before the library - // has loaded. - mLibraryInitializedLock.wait(); - } - } catch (InterruptedException e) { - // Ignore - } - } - mDelegate.onDestroy(); - } - - /* - * Returns the communication channel to the service. Note that even if multiple clients were to - * connect, we should only get one call to this method. So there is no need to synchronize - * member variables that are only set in this method and accessed from binder methods, as binder - * methods can't be called until this method returns. - * @param intent The intent that was used to bind to the service. - * @return the binder used by the client to setup the connection. - */ - @Override - public IBinder onBind(Intent intent) { - assert !mServiceBound; - - // We call stopSelf() to request that this service be stopped as soon as the client unbinds. - // Otherwise the system may keep it around and available for a reconnect. The child - // processes do not currently support reconnect; they must be initialized from scratch every - // time. - stopSelf(); - - mBindToCallerCheck = - intent.getBooleanExtra(ChildProcessConstants.EXTRA_BIND_TO_CALLER, false); - mServiceBound = true; - mDelegate.onServiceBound(intent); - // Don't block bind() with any extra work, post it to the application thread instead. - new Handler(Looper.getMainLooper()) - .post(() -> mDelegate.preloadNativeLibrary(getApplicationContext())); - return mBinder; - } - - private void processConnectionBundle(Bundle bundle, List clientInterfaces) { - // Required to unparcel FileDescriptorInfo. - ClassLoader classLoader = getApplicationContext().getClassLoader(); - bundle.setClassLoader(classLoader); - synchronized (mMainThread) { - if (mCommandLineParams == null) { - mCommandLineParams = - bundle.getStringArray(ChildProcessConstants.EXTRA_COMMAND_LINE); - mMainThread.notifyAll(); - } - // We must have received the command line by now - assert mCommandLineParams != null; - Parcelable[] fdInfosAsParcelable = - bundle.getParcelableArray(ChildProcessConstants.EXTRA_FILES); - if (fdInfosAsParcelable != null) { - // For why this arraycopy is necessary: - // http://stackoverflow.com/questions/8745893/i-dont-get-why-this-classcastexception-occurs - mFdInfos = new FileDescriptorInfo[fdInfosAsParcelable.length]; - System.arraycopy(fdInfosAsParcelable, 0, mFdInfos, 0, fdInfosAsParcelable.length); - } - mDelegate.onConnectionSetup(bundle, clientInterfaces); - mMainThread.notifyAll(); - } - } - - /** - * Helper for registering FileDescriptorInfo objects with GlobalFileDescriptors or - * FileDescriptorStore. - * This includes the IPC channel, the crash dump signals and resource related - * files. - */ - private static native void nativeRegisterFileDescriptors( - String[] keys, int[] id, int[] fd, long[] offset, long[] size); - - /** - * Force the child process to exit. - */ - private static native void nativeExitChildProcess(); -} diff --git a/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java b/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java deleted file mode 100644 index 7beffeffa..000000000 --- a/android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.process_launcher; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; -import android.util.SparseArray; - -import java.util.List; - -/** - * The interface that embedders should implement to specialize child service creation. - */ -public interface ChildProcessServiceDelegate { - /** Invoked when the service was created. This is the first method invoked on the delegate. */ - void onServiceCreated(); - - /** - * Called when the service is bound. Invoked on a background thread. - * @param intent the intent that started the service. - */ - void onServiceBound(Intent intent); - - /** - * Called once the connection has been setup. Invoked on a background thread. - * @param connectionBundle the bundle pass to the setupConnection call - * @param clientInterfaces the IBinders interfaces provided by the client - */ - void onConnectionSetup(Bundle connectionBundle, List clientInterfaces); - - /** - * Called when the service gets destroyed. - * Note that the system might kill the process hosting the service without this method being - * called. - */ - void onDestroy(); - - /** - * Called when the delegate should load the native library. - * @param hostContext The host context the library should be loaded with (i.e. Chrome). - * @return true if the library was loaded successfully, false otherwise in which case the - * service stops. - */ - boolean loadNativeLibrary(Context hostContext); - - /** - * Called when the delegate should preload the native library. - * Preloading is automatically done during library loading, but can also be called explicitly - * to speed up the loading. See {@link LibraryLoader.preloadNow}. - * @param hostContext The host context the library should be preloaded with (i.e. Chrome). - */ - void preloadNativeLibrary(Context hostContext); - - /** - * Should return a map that associatesfile descriptors' IDs to keys. - * This is needed as at the moment we use 2 different stores for the FDs in native code: - * base::FileDescriptorStore which associates FDs with string identifiers (the key), and - * base::GlobalDescriptors which associates FDs with int ids. - * FDs for which the returned map contains a mapping are added to base::FileDescriptorStore with - * the associated key, all others are added to base::GlobalDescriptors. - */ - SparseArray getFileDescriptorsIdsToKeys(); - - /** Called before the main method is invoked. */ - void onBeforeMain(); - - /** - * The main entry point for the service. This method should block as long as the service should - * be running. - */ - void runMain(); -} diff --git a/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.aidl b/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.aidl deleted file mode 100644 index e37d8c748..000000000 --- a/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.aidl +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.process_launcher; - -parcelable FileDescriptorInfo; diff --git a/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java b/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java deleted file mode 100644 index 3dc366389..000000000 --- a/android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.process_launcher; - -import android.os.Parcel; -import android.os.ParcelFileDescriptor; -import android.os.Parcelable; - -import org.chromium.base.annotations.MainDex; -import org.chromium.base.annotations.UsedByReflection; - -import javax.annotation.concurrent.Immutable; - -/** - * Parcelable class that contains file descriptor and file region information to - * be passed to child processes. - */ -@Immutable -@MainDex -@UsedByReflection("child_process_launcher_helper_android.cc") -public final class FileDescriptorInfo implements Parcelable { - public final int id; - public final ParcelFileDescriptor fd; - public final long offset; - public final long size; - - public FileDescriptorInfo(int id, ParcelFileDescriptor fd, long offset, long size) { - this.id = id; - this.fd = fd; - this.offset = offset; - this.size = size; - } - - FileDescriptorInfo(Parcel in) { - id = in.readInt(); - fd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); - offset = in.readLong(); - size = in.readLong(); - } - - @Override - public int describeContents() { - return CONTENTS_FILE_DESCRIPTOR; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(id); - dest.writeParcelable(fd, CONTENTS_FILE_DESCRIPTOR); - dest.writeLong(offset); - dest.writeLong(size); - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public FileDescriptorInfo createFromParcel(Parcel in) { - return new FileDescriptorInfo(in); - } - - @Override - public FileDescriptorInfo[] newArray(int size) { - return new FileDescriptorInfo[size]; - } - }; -} diff --git a/android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl b/android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl deleted file mode 100644 index db93cb0db..000000000 --- a/android/java/src/org/chromium/base/process_launcher/ICallbackInt.aidl +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.process_launcher; - -oneway interface ICallbackInt { - void call(int value); -} diff --git a/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl b/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl deleted file mode 100644 index 298e0bf49..000000000 --- a/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 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. - -package org.chromium.base.process_launcher; - -import android.os.Bundle; - -import org.chromium.base.process_launcher.ICallbackInt; - -interface IChildProcessService { - // On the first call to this method, the service will record the calling PID - // and return true. Subsequent calls will only return true if the calling PID - // is the same as the recorded one. - boolean bindToCaller(); - - // Sets up the initial IPC channel. - oneway void setupConnection(in Bundle args, ICallbackInt pidCallback, - in List clientInterfaces); - - // Forcefully kills the child process. - oneway void forceKill(); - - // Notifies about memory pressure. The argument is MemoryPressureLevel enum. - oneway void onMemoryPressure(int pressure); -} diff --git a/android/java/src/org/chromium/base/process_launcher/OWNERS b/android/java/src/org/chromium/base/process_launcher/OWNERS deleted file mode 100644 index c2edc66a7..000000000 --- a/android/java/src/org/chromium/base/process_launcher/OWNERS +++ /dev/null @@ -1,5 +0,0 @@ -boliu@chromium.org -jcivelli@chromium.org - -per-file *.aidl=set noparent -per-file *.aidl=file://ipc/SECURITY_OWNERS diff --git a/android/java/templates/BuildConfig.template b/android/java/templates/BuildConfig.template deleted file mode 100644 index 1006d12ee..000000000 --- a/android/java/templates/BuildConfig.template +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -#define Q(x) #x -#define QUOTE(x) Q(x) - -#if defined(USE_FINAL) -#define MAYBE_FINAL final -#else -#define MAYBE_FINAL -#endif - -/** - * Build configuration. Generated on a per-target basis. - */ -public class BuildConfig { - -#if defined(ENABLE_MULTIDEX) - public static MAYBE_FINAL boolean IS_MULTIDEX_ENABLED = true; -#else - public static MAYBE_FINAL boolean IS_MULTIDEX_ENABLED = false; -#endif - -#if defined(_FIREBASE_APP_ID) - public static MAYBE_FINAL String FIREBASE_APP_ID = QUOTE(_FIREBASE_APP_ID); -#else - public static MAYBE_FINAL String FIREBASE_APP_ID = ""; -#endif - -#if defined(_DCHECK_IS_ON) - public static MAYBE_FINAL boolean DCHECK_IS_ON = true; -#else - public static MAYBE_FINAL boolean DCHECK_IS_ON = false; -#endif - -#if defined(_IS_UBSAN) - public static MAYBE_FINAL boolean IS_UBSAN = true; -#else - public static MAYBE_FINAL boolean IS_UBSAN = false; -#endif - - // Sorted list of locales that have a compressed .pak within assets. - // Stored as an array because AssetManager.list() is slow. -#if defined(COMPRESSED_LOCALE_LIST) - public static MAYBE_FINAL String[] COMPRESSED_LOCALES = COMPRESSED_LOCALE_LIST; -#else - public static MAYBE_FINAL String[] COMPRESSED_LOCALES = {}; -#endif - - // Sorted list of locales that have an uncompressed .pak within assets. - // Stored as an array because AssetManager.list() is slow. -#if defined(UNCOMPRESSED_LOCALE_LIST) - public static MAYBE_FINAL String[] UNCOMPRESSED_LOCALES = UNCOMPRESSED_LOCALE_LIST; -#else - public static MAYBE_FINAL String[] UNCOMPRESSED_LOCALES = {}; -#endif - - // The ID of the android string resource that stores the product version. - // This layer of indirection is necessary to make the resource dependency - // optional for android_apk targets/base_java (ex. for cronet). -#if defined(_RESOURCES_VERSION_VARIABLE) - public static MAYBE_FINAL int R_STRING_PRODUCT_VERSION = _RESOURCES_VERSION_VARIABLE; -#else - // Default value, do not use. - public static MAYBE_FINAL int R_STRING_PRODUCT_VERSION = 0; -#endif -} diff --git a/android/java/templates/NativeLibraries.template b/android/java/templates/NativeLibraries.template deleted file mode 100644 index 68277df75..000000000 --- a/android/java/templates/NativeLibraries.template +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.library_loader; - -public class NativeLibraries { - /** - * IMPORTANT NOTE: The variables defined here must _not_ be 'final'. - * - * The reason for this is very subtle: - * - * - This template is used to generate several distinct, but similar - * files used in different contexts: - * - * o .../gen/templates/org/chromium/base/library_loader/NativeLibraries.java - * - * This file is used to build base.jar, which is the library - * jar used by chromium projects. However, the - * corresponding NativeLibraries.class file will _not_ be part - * of the final base.jar. - * - * o .../$PROJECT/native_libraries_java/NativeLibraries.java - * - * This file is used to build an APK (e.g. $PROJECT - * could be 'content_shell_apk'). Its content will depend on - * this target's specific build configuration, and differ from - * the source file above. - * - * - During the final link, all .jar files are linked together into - * a single .dex file, and the second version of NativeLibraries.class - * will be put into the final output file, and used at runtime. - * - * - If the variables were defined as 'final', their value would be - * optimized out inside of 'base.jar', and could not be specialized - * for every chromium program. This, however, doesn't apply to arrays of - * strings, which can be defined as final. - * - * This exotic scheme is used to avoid injecting project-specific, or - * even build-specific, values into the base layer. E.g. this is - * how the component build is supported on Android without modifying - * the sources of each and every Chromium-based target. - */ - - public static final int CPU_FAMILY_UNKNOWN = 0; - public static final int CPU_FAMILY_ARM = 1; - public static final int CPU_FAMILY_MIPS = 2; - public static final int CPU_FAMILY_X86 = 3; - -#if defined(ENABLE_CHROMIUM_LINKER_LIBRARY_IN_ZIP_FILE) && \ - !defined(ENABLE_CHROMIUM_LINKER) -#error "Must have ENABLE_CHROMIUM_LINKER to enable library in zip file" -#endif - - // Set to true to enable the use of the Chromium Linker. -#if defined(ENABLE_CHROMIUM_LINKER) - public static boolean sUseLinker = true; -#else - public static boolean sUseLinker = false; -#endif - -#if defined(ENABLE_CHROMIUM_LINKER_LIBRARY_IN_ZIP_FILE) - public static boolean sUseLibraryInZipFile = true; -#else - public static boolean sUseLibraryInZipFile = false; -#endif - -#if defined(ENABLE_CHROMIUM_LINKER_TESTS) - public static boolean sEnableLinkerTests = true; -#else - public static boolean sEnableLinkerTests = false; -#endif - - // This is the list of native libraries to be loaded (in the correct order) - // by LibraryLoader.java. The base java library is compiled with no - // array defined, and then the build system creates a version of the file - // with the real list of libraries required (which changes based upon which - // .apk is being built). - // TODO(cjhopman): This is public since it is referenced by NativeTestActivity.java - // directly. The two ways of library loading should be refactored into one. - public static final String[] LIBRARIES = -#if defined(NATIVE_LIBRARIES_LIST) - NATIVE_LIBRARIES_LIST; -#else - {}; -#endif - - // This is the expected version of the 'main' native library, which is the one that - // implements the initial set of base JNI functions including - // base::android::nativeGetVersionName() - static String sVersionNumber = -#if defined(NATIVE_LIBRARIES_VERSION_NUMBER) - NATIVE_LIBRARIES_VERSION_NUMBER; -#else - ""; -#endif - - public static int sCpuFamily = -#if defined(ANDROID_APP_CPU_FAMILY_ARM) - CPU_FAMILY_ARM; -#elif defined(ANDROID_APP_CPU_FAMILY_X86) - CPU_FAMILY_X86; -#elif defined(ANDROID_APP_CPU_FAMILY_MIPS) - CPU_FAMILY_MIPS; -#else - CPU_FAMILY_UNKNOWN; -#endif - -} diff --git a/android/java_exception_reporter.cc b/android/java_exception_reporter.cc deleted file mode 100644 index 96eb38e58..000000000 --- a/android/java_exception_reporter.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/java_exception_reporter.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/debug/dump_without_crashing.h" -#include "jni/JavaExceptionReporter_jni.h" - -using base::android::JavaParamRef; - -namespace base { -namespace android { - -namespace { - -void (*g_java_exception_callback)(const char*); - -} // namespace - -void InitJavaExceptionReporter() { - JNIEnv* env = base::android::AttachCurrentThread(); - constexpr bool crash_after_report = false; - Java_JavaExceptionReporter_installHandler(env, crash_after_report); -} - -void InitJavaExceptionReporterForChildProcess() { - JNIEnv* env = base::android::AttachCurrentThread(); - constexpr bool crash_after_report = true; - Java_JavaExceptionReporter_installHandler(env, crash_after_report); -} - -void SetJavaExceptionCallback(void (*callback)(const char*)) { - DCHECK(!g_java_exception_callback); - g_java_exception_callback = callback; -} - -void SetJavaException(const char* exception) { - DCHECK(g_java_exception_callback); - g_java_exception_callback(exception); -} - -void JNI_JavaExceptionReporter_ReportJavaException( - JNIEnv* env, - const JavaParamRef& jcaller, - jboolean crash_after_report, - const JavaParamRef& e) { - std::string exception_info = base::android::GetJavaExceptionInfo(env, e); - SetJavaException(exception_info.c_str()); - if (crash_after_report) { - LOG(ERROR) << exception_info; - LOG(FATAL) << "Uncaught exception"; - } - base::debug::DumpWithoutCrashing(); - SetJavaException(nullptr); -} - -void JNI_JavaExceptionReporter_ReportJavaStackTrace( - JNIEnv* env, - const JavaParamRef& jcaller, - const JavaParamRef& stackTrace) { - SetJavaException(ConvertJavaStringToUTF8(stackTrace).c_str()); - base::debug::DumpWithoutCrashing(); - SetJavaException(nullptr); -} - -} // namespace android -} // namespace base diff --git a/android/java_exception_reporter.h b/android/java_exception_reporter.h deleted file mode 100644 index c0a7ea639..000000000 --- a/android/java_exception_reporter.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_JAVA_EXCEPTION_REPORTER_H_ -#define BASE_ANDROID_JAVA_EXCEPTION_REPORTER_H_ - -#include - -#include "base/base_export.h" - -namespace base { -namespace android { - -// Install the exception handler. This should only be called once per process. -BASE_EXPORT void InitJavaExceptionReporter(); - -// Same as above except the handler ensures child process exists immediately -// after an unhandled exception. This is used for child processes because -// DumpWithoutCrashing does not work for child processes on Android. -BASE_EXPORT void InitJavaExceptionReporterForChildProcess(); - -// Sets a callback to be called with the contents of a Java exception, which may -// be nullptr. -BASE_EXPORT void SetJavaExceptionCallback(void (*)(const char* exception)); - -// Calls the Java exception callback, if any, with exception. -void SetJavaException(const char* exception); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_JAVA_EXCEPTION_REPORTER_H_ diff --git a/android/java_handler_thread.cc b/android/java_handler_thread.cc deleted file mode 100644 index ea87ba76d..000000000 --- a/android/java_handler_thread.cc +++ /dev/null @@ -1,132 +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/java_handler_thread.h" - -#include - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/bind.h" -#include "base/run_loop.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/platform_thread_internal_posix.h" -#include "base/threading/thread_restrictions.h" -#include "jni/JavaHandlerThread_jni.h" - -using base::android::AttachCurrentThread; - -namespace base { - -namespace android { - -JavaHandlerThread::JavaHandlerThread(const char* name, - base::ThreadPriority priority) - : JavaHandlerThread(Java_JavaHandlerThread_create( - AttachCurrentThread(), - ConvertUTF8ToJavaString(AttachCurrentThread(), name), - base::internal::ThreadPriorityToNiceValue(priority))) {} - -JavaHandlerThread::JavaHandlerThread( - const base::android::ScopedJavaLocalRef& obj) - : java_thread_(obj) {} - -JavaHandlerThread::~JavaHandlerThread() { - JNIEnv* env = base::android::AttachCurrentThread(); - DCHECK(!Java_JavaHandlerThread_isAlive(env, java_thread_)); - DCHECK(!message_loop_ || message_loop_->IsAborted()); - // TODO(mthiesse): We shouldn't leak the MessageLoop as this could affect - // future tests. - if (message_loop_ && message_loop_->IsAborted()) { - // When the message loop has been aborted due to a crash, we intentionally - // leak the message loop because the message loop hasn't been shut down - // properly and would trigger DCHECKS. This should only happen in tests, - // where we handle the exception instead of letting it take down the - // process. - message_loop_.release(); - } -} - -void JavaHandlerThread::Start() { - // Check the thread has not already been started. - DCHECK(!message_loop_); - - JNIEnv* env = base::android::AttachCurrentThread(); - base::WaitableEvent initialize_event( - WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - Java_JavaHandlerThread_startAndInitialize( - env, java_thread_, reinterpret_cast(this), - reinterpret_cast(&initialize_event)); - // Wait for thread to be initialized so it is ready to be used when Start - // returns. - base::ThreadRestrictions::ScopedAllowWait wait_allowed; - initialize_event.Wait(); -} - -void JavaHandlerThread::Stop() { - DCHECK(!task_runner()->BelongsToCurrentThread()); - task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&JavaHandlerThread::StopOnThread, base::Unretained(this))); - JNIEnv* env = base::android::AttachCurrentThread(); - Java_JavaHandlerThread_joinThread(env, java_thread_); -} - -void JavaHandlerThread::InitializeThread(JNIEnv* env, - const JavaParamRef& obj, - jlong event) { - // TYPE_JAVA to get the Android java style message loop. - message_loop_ = - std::make_unique(base::MessageLoop::TYPE_JAVA); - Init(); - reinterpret_cast(event)->Signal(); -} - -void JavaHandlerThread::OnLooperStopped(JNIEnv* env, - const JavaParamRef& obj) { - DCHECK(task_runner()->BelongsToCurrentThread()); - message_loop_.reset(); - CleanUp(); -} - -void JavaHandlerThread::StopMessageLoopForTesting() { - DCHECK(task_runner()->BelongsToCurrentThread()); - StopOnThread(); -} - -void JavaHandlerThread::JoinForTesting() { - DCHECK(!task_runner()->BelongsToCurrentThread()); - JNIEnv* env = base::android::AttachCurrentThread(); - Java_JavaHandlerThread_joinThread(env, java_thread_); -} - -void JavaHandlerThread::ListenForUncaughtExceptionsForTesting() { - DCHECK(!task_runner()->BelongsToCurrentThread()); - JNIEnv* env = base::android::AttachCurrentThread(); - Java_JavaHandlerThread_listenForUncaughtExceptionsForTesting(env, - java_thread_); -} - -ScopedJavaLocalRef JavaHandlerThread::GetUncaughtExceptionIfAny() { - DCHECK(!task_runner()->BelongsToCurrentThread()); - JNIEnv* env = base::android::AttachCurrentThread(); - return Java_JavaHandlerThread_getUncaughtExceptionIfAny(env, java_thread_); -} - -void JavaHandlerThread::StopOnThread() { - DCHECK(task_runner()->BelongsToCurrentThread()); - message_loop_->QuitWhenIdle(base::BindOnce( - &JavaHandlerThread::QuitThreadSafely, base::Unretained(this))); -} - -void JavaHandlerThread::QuitThreadSafely() { - DCHECK(task_runner()->BelongsToCurrentThread()); - JNIEnv* env = base::android::AttachCurrentThread(); - Java_JavaHandlerThread_quitThreadSafely(env, java_thread_, - reinterpret_cast(this)); -} - -} // namespace android -} // namespace base diff --git a/android/java_handler_thread.h b/android/java_handler_thread.h deleted file mode 100644 index d65dfe298..000000000 --- a/android/java_handler_thread.h +++ /dev/null @@ -1,95 +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_JAVA_HANDLER_THREAD_H_ -#define BASE_ANDROID_JAVA_HANDLER_THREAD_H_ - -#include - -#include - -#include "base/android/scoped_java_ref.h" -#include "base/message_loop/message_loop.h" -#include "base/single_thread_task_runner.h" - -namespace base { - -class MessageLoop; - -namespace android { - -// A Java Thread with a native message loop. To run tasks, post them -// to the message loop and they will be scheduled along with Java tasks -// on the thread. -// This is useful for callbacks where the receiver expects a thread -// with a prepared Looper. -class BASE_EXPORT JavaHandlerThread { - public: - // Create new thread. - explicit JavaHandlerThread( - const char* name, - base::ThreadPriority priority = base::ThreadPriority::NORMAL); - // Wrap and connect to an existing JavaHandlerThread. - // |obj| is an instance of JavaHandlerThread. - explicit JavaHandlerThread( - const base::android::ScopedJavaLocalRef& obj); - virtual ~JavaHandlerThread(); - - // Called from any thread. - base::MessageLoop* message_loop() const { return message_loop_.get(); } - - // Gets the TaskRunner associated with the message loop. - // Called from any thread. - scoped_refptr task_runner() const { - return message_loop_ ? message_loop_->task_runner() : nullptr; - } - - // Called from the parent thread. - void Start(); - void Stop(); - - // Called from java on the newly created thread. - // Start() will not return before this methods has finished. - void InitializeThread(JNIEnv* env, - const JavaParamRef& obj, - jlong event); - // Called from java on this thread. - void OnLooperStopped(JNIEnv* env, const JavaParamRef& obj); - - // Called from this thread. - void StopMessageLoopForTesting(); - // Called from this thread. - void JoinForTesting(); - - // Called from this thread. - // See comment in JavaHandlerThread.java regarding use of this function. - void ListenForUncaughtExceptionsForTesting(); - // Called from this thread. - ScopedJavaLocalRef GetUncaughtExceptionIfAny(); - - protected: - // Semantically the same as base::Thread#Init(), but unlike base::Thread the - // Android Looper will already be running. This Init() call will still run - // before other tasks are posted to the thread. - virtual void Init() {} - - // Semantically the same as base::Thread#CleanUp(), called after the message - // loop ends. The Android Looper will also have been quit by this point. - virtual void CleanUp() {} - - std::unique_ptr message_loop_; - - private: - void StartMessageLoop(); - - void StopOnThread(); - void QuitThreadSafely(); - - ScopedJavaGlobalRef java_thread_; -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_JAVA_HANDLER_THREAD_H_ diff --git a/android/java_runtime.cc b/android/java_runtime.cc deleted file mode 100644 index 5fae49a2a..000000000 --- a/android/java_runtime.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/java_runtime.h" - -#include "jni/Runtime_jni.h" - -namespace base { -namespace android { - -void JavaRuntime::GetMemoryUsage(long* total_memory, long* free_memory) { - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::ScopedJavaLocalRef runtime = - JNI_Runtime::Java_Runtime_getRuntime(env); - *total_memory = JNI_Runtime::Java_Runtime_totalMemory(env, runtime); - *free_memory = JNI_Runtime::Java_Runtime_freeMemory(env, runtime); -} - -} // namespace android -} // namespace base diff --git a/android/java_runtime.h b/android/java_runtime.h deleted file mode 100644 index 2034fb9c1..000000000 --- a/android/java_runtime.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_JAVA_RUNTIME_H_ -#define BASE_ANDROID_JAVA_RUNTIME_H_ - -#include "base/android/scoped_java_ref.h" -#include "base/base_export.h" - -namespace base { -namespace android { - -// Wrapper class for using the java.lang.Runtime object from jni. -class BASE_EXPORT JavaRuntime { - public: - // Fills the total memory used and memory allocated for objects by the java - // heap in the current process. Returns true on success. - static void GetMemoryUsage(long* total_memory, long* free_memory); -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_JAVA_RUNTIME_H_ diff --git a/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java b/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java deleted file mode 100644 index 20c626d19..000000000 --- a/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.app.Application; -import android.content.ComponentCallbacks; -import android.content.ComponentCallbacks2; -import android.content.Context; -import android.content.res.Configuration; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.BaseJUnit4ClassRunner; -import org.chromium.base.test.util.AdvancedMockContext; - -/** - * Tests for {@link org.chromium.base.test.util.AdvancedMockContext}. - */ -@RunWith(BaseJUnit4ClassRunner.class) -public class AdvancedMockContextTest { - private static class Callback1 implements ComponentCallbacks { - protected Configuration mConfiguration; - protected boolean mOnLowMemoryCalled; - - @Override - public void onConfigurationChanged(Configuration configuration) { - mConfiguration = configuration; - } - - @Override - public void onLowMemory() { - mOnLowMemoryCalled = true; - } - } - - private static class Callback2 extends Callback1 implements ComponentCallbacks2 { - private int mLevel; - - @Override - public void onTrimMemory(int level) { - mLevel = level; - } - } - - @Test - @SmallTest - public void testComponentCallbacksForTargetContext() { - Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - Application targetApplication = (Application) targetContext.getApplicationContext(); - AdvancedMockContext context = new AdvancedMockContext(targetContext); - Callback1 callback1 = new Callback1(); - Callback2 callback2 = new Callback2(); - context.registerComponentCallbacks(callback1); - context.registerComponentCallbacks(callback2); - - targetApplication.onLowMemory(); - Assert.assertTrue("onLowMemory should have been called.", callback1.mOnLowMemoryCalled); - Assert.assertTrue("onLowMemory should have been called.", callback2.mOnLowMemoryCalled); - - Configuration configuration = new Configuration(); - targetApplication.onConfigurationChanged(configuration); - Assert.assertEquals("onConfigurationChanged should have been called.", configuration, - callback1.mConfiguration); - Assert.assertEquals("onConfigurationChanged should have been called.", configuration, - callback2.mConfiguration); - - targetApplication.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE); - Assert.assertEquals("onTrimMemory should have been called.", - ComponentCallbacks2.TRIM_MEMORY_MODERATE, callback2.mLevel); - } -} diff --git a/android/javatests/src/org/chromium/base/ApiCompatibilityUtilsTest.java b/android/javatests/src/org/chromium/base/ApiCompatibilityUtilsTest.java deleted file mode 100644 index de0858a58..000000000 --- a/android/javatests/src/org/chromium/base/ApiCompatibilityUtilsTest.java +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.annotation.TargetApi; -import android.app.Activity; -import android.os.Build; -import android.os.SystemClock; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.BaseJUnit4ClassRunner; - -/** - * Test of ApiCompatibilityUtils - */ -@RunWith(BaseJUnit4ClassRunner.class) -public class ApiCompatibilityUtilsTest { - private static final long WAIT_TIMEOUT_IN_MS = 5000; - private static final long SLEEP_INTERVAL_IN_MS = 50; - - static class MockActivity extends Activity { - int mFinishAndRemoveTaskCallbackCount; - int mFinishCallbackCount; - boolean mIsFinishing; - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - @Override - public void finishAndRemoveTask() { - mFinishAndRemoveTaskCallbackCount++; - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) mIsFinishing = true; - } - - @Override - public void finish() { - mFinishCallbackCount++; - mIsFinishing = true; - } - - @Override - public boolean isFinishing() { - return mIsFinishing; - } - } - - @Test - @SmallTest - public void testFinishAndRemoveTask() { - InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { - @Override - public void run() { - MockActivity activity = new MockActivity(); - ApiCompatibilityUtils.finishAndRemoveTask(activity); - - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { - Assert.assertEquals(1, activity.mFinishAndRemoveTaskCallbackCount); - Assert.assertEquals(0, activity.mFinishCallbackCount); - } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { - long startTime = SystemClock.uptimeMillis(); - while (activity.mFinishCallbackCount == 0 - && SystemClock.uptimeMillis() - startTime < WAIT_TIMEOUT_IN_MS) { - try { - Thread.sleep(SLEEP_INTERVAL_IN_MS); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted thread sleep", e); - } - } - - // MockActivity#finishAndRemoveTask() never sets isFinishing() to true for - // LOLLIPOP to simulate an exceptional case. In that case, MockActivity#finish() - // should be called after 3 tries. - Assert.assertEquals(3, activity.mFinishAndRemoveTaskCallbackCount); - Assert.assertEquals(1, activity.mFinishCallbackCount); - } else { - Assert.assertEquals(0, activity.mFinishAndRemoveTaskCallbackCount); - Assert.assertEquals(1, activity.mFinishCallbackCount); - } - Assert.assertTrue(activity.mIsFinishing); - } - }); - } -} diff --git a/android/javatests/src/org/chromium/base/AssertsTest.java b/android/javatests/src/org/chromium/base/AssertsTest.java deleted file mode 100644 index 37e3b4084..000000000 --- a/android/javatests/src/org/chromium/base/AssertsTest.java +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.support.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.BaseJUnit4ClassRunner; - -/** - * Test that ensures Java asserts are working. - * - * Not a robolectric test because we want to make sure asserts are enabled after dexing. - */ -@RunWith(BaseJUnit4ClassRunner.class) -public class AssertsTest { - @Test - @SmallTest - @SuppressWarnings("UseCorrectAssertInTests") - public void testAssertsWorkAsExpected() { - if (BuildConfig.DCHECK_IS_ON) { - try { - assert false; - } catch (AssertionError e) { - // When DCHECK is on, asserts should throw AssertionErrors. - return; - } - Assert.fail("Java assert unexpectedly didn't fire."); - } else { - // When DCHECK isn't on, asserts should be removed by proguard. - assert false : "Java assert unexpectedly fired."; - } - } -} diff --git a/android/javatests/src/org/chromium/base/CommandLineInitUtilTest.java b/android/javatests/src/org/chromium/base/CommandLineInitUtilTest.java deleted file mode 100644 index a3be5dd15..000000000 --- a/android/javatests/src/org/chromium/base/CommandLineInitUtilTest.java +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.Feature; - -/** - * Test class for {@link CommandLineInitUtil}. - */ -@RunWith(AndroidJUnit4.class) -public class CommandLineInitUtilTest { - /** - * Verifies that the default command line flags get set for Chrome Public tests. - */ - @Test - @SmallTest - @Feature({"CommandLine"}) - public void testDefaultCommandLineFlagsSet() { - CommandLineInitUtil.initCommandLine(CommandLineFlags.getTestCmdLineFile()); - Assert.assertTrue("CommandLine not initialized.", CommandLine.isInitialized()); - - final CommandLine commandLine = CommandLine.getInstance(); - Assert.assertTrue(commandLine.hasSwitch("enable-test-intents")); - } -} diff --git a/android/javatests/src/org/chromium/base/CommandLineTest.java b/android/javatests/src/org/chromium/base/CommandLineTest.java deleted file mode 100644 index 787f85d99..000000000 --- a/android/javatests/src/org/chromium/base/CommandLineTest.java +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.support.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.BaseJUnit4ClassRunner; -import org.chromium.base.test.util.Feature; - -/** - * Tests for {@link CommandLine}. - * TODO(bauerb): Convert to local JUnit test - */ -@RunWith(BaseJUnit4ClassRunner.class) -public class CommandLineTest { - // A reference command line. Note that switch2 is [brea\d], switch3 is [and "butter"], - // and switch4 is [a "quoted" 'food'!] - static final String INIT_SWITCHES[] = { "init_command", "--SWITCH", "Arg", - "--switch2=brea\\d", "--switch3=and \"butter\"", - "--switch4=a \"quoted\" 'food'!", - "--", "--actually_an_arg" }; - - // The same command line, but in quoted string format. - static final char INIT_SWITCHES_BUFFER[] = - ("init_command --SWITCH Arg --switch2=brea\\d --switch3=\"and \\\"butt\"er\\\" " - + "--switch4='a \"quoted\" \\'food\\'!' " - + "-- --actually_an_arg").toCharArray(); - - static final String CL_ADDED_SWITCH = "zappo-dappo-doggy-trainer"; - static final String CL_ADDED_SWITCH_2 = "username"; - static final String CL_ADDED_VALUE_2 = "bozo"; - - @Before - public void setUp() throws Exception { - CommandLine.reset(); - } - - void checkInitSwitches() { - CommandLine cl = CommandLine.getInstance(); - Assert.assertFalse(cl.hasSwitch("init_command")); - Assert.assertFalse(cl.hasSwitch("switch")); - Assert.assertTrue(cl.hasSwitch("SWITCH")); - Assert.assertFalse(cl.hasSwitch("--SWITCH")); - Assert.assertFalse(cl.hasSwitch("Arg")); - Assert.assertFalse(cl.hasSwitch("actually_an_arg")); - Assert.assertEquals("brea\\d", cl.getSwitchValue("switch2")); - Assert.assertEquals("and \"butter\"", cl.getSwitchValue("switch3")); - Assert.assertEquals("a \"quoted\" 'food'!", cl.getSwitchValue("switch4")); - Assert.assertNull(cl.getSwitchValue("SWITCH")); - Assert.assertNull(cl.getSwitchValue("non-existant")); - } - - void checkSettingThenGetting() { - CommandLine cl = CommandLine.getInstance(); - - // Add a plain switch. - Assert.assertFalse(cl.hasSwitch(CL_ADDED_SWITCH)); - cl.appendSwitch(CL_ADDED_SWITCH); - Assert.assertTrue(cl.hasSwitch(CL_ADDED_SWITCH)); - - // Add a switch paired with a value. - Assert.assertFalse(cl.hasSwitch(CL_ADDED_SWITCH_2)); - Assert.assertNull(cl.getSwitchValue(CL_ADDED_SWITCH_2)); - cl.appendSwitchWithValue(CL_ADDED_SWITCH_2, CL_ADDED_VALUE_2); - Assert.assertTrue(CL_ADDED_VALUE_2.equals(cl.getSwitchValue(CL_ADDED_SWITCH_2))); - - // Append a few new things. - final String switchesAndArgs[] = { "dummy", "--superfast", "--speed=turbo" }; - Assert.assertFalse(cl.hasSwitch("dummy")); - Assert.assertFalse(cl.hasSwitch("superfast")); - Assert.assertNull(cl.getSwitchValue("speed")); - cl.appendSwitchesAndArguments(switchesAndArgs); - Assert.assertFalse(cl.hasSwitch("dummy")); - Assert.assertFalse(cl.hasSwitch("command")); - Assert.assertTrue(cl.hasSwitch("superfast")); - Assert.assertTrue("turbo".equals(cl.getSwitchValue("speed"))); - } - - void checkTokenizer(String[] expected, String toParse) { - String[] actual = CommandLine.tokenizeQuotedArguments(toParse.toCharArray()); - Assert.assertEquals(expected.length, actual.length); - for (int i = 0; i < expected.length; ++i) { - Assert.assertEquals("comparing element " + i, expected[i], actual[i]); - } - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testJavaInitialization() { - CommandLine.init(INIT_SWITCHES); - checkInitSwitches(); - checkSettingThenGetting(); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testBufferInitialization() { - CommandLine.init(CommandLine.tokenizeQuotedArguments(INIT_SWITCHES_BUFFER)); - checkInitSwitches(); - checkSettingThenGetting(); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testArgumentTokenizer() { - String toParse = " a\"\\bc de\\\"f g\"\\h ij k\" \"lm"; - String[] expected = { "a\\bc de\"f g\\h", - "ij", - "k lm" }; - checkTokenizer(expected, toParse); - - toParse = ""; - expected = new String[0]; - checkTokenizer(expected, toParse); - - toParse = " \t\n"; - checkTokenizer(expected, toParse); - - toParse = " \"a'b\" 'c\"d' \"e\\\"f\" 'g\\'h' \"i\\'j\" 'k\\\"l'" - + " m\"n\\'o\"p q'r\\\"s't"; - expected = new String[] { "a'b", - "c\"d", - "e\"f", - "g'h", - "i\\'j", - "k\\\"l", - "mn\\'op", - "qr\\\"st"}; - checkTokenizer(expected, toParse); - } -} diff --git a/android/javatests/src/org/chromium/base/EarlyTraceEventTest.java b/android/javatests/src/org/chromium/base/EarlyTraceEventTest.java deleted file mode 100644 index 59a478ac3..000000000 --- a/android/javatests/src/org/chromium/base/EarlyTraceEventTest.java +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import static org.chromium.base.EarlyTraceEvent.AsyncEvent; -import static org.chromium.base.EarlyTraceEvent.Event; - -import android.os.Process; -import android.support.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.library_loader.LibraryLoader; -import org.chromium.base.library_loader.LibraryProcessType; -import org.chromium.base.test.BaseJUnit4ClassRunner; -import org.chromium.base.test.util.Feature; - -/** - * Tests for {@link EarlyTraceEvent}. - * - * TODO(lizeb): Move to roboelectric tests. - */ -@RunWith(BaseJUnit4ClassRunner.class) -public class EarlyTraceEventTest { - private static final String EVENT_NAME = "MyEvent"; - private static final String EVENT_NAME2 = "MyOtherEvent"; - private static final long EVENT_ID = 1; - private static final long EVENT_ID2 = 2; - - @Before - public void setUp() throws Exception { - LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER); - EarlyTraceEvent.resetForTesting(); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testCanRecordEvent() { - EarlyTraceEvent.enable(); - long myThreadId = Process.myTid(); - long beforeNanos = Event.elapsedRealtimeNanos(); - EarlyTraceEvent.begin(EVENT_NAME); - EarlyTraceEvent.end(EVENT_NAME); - long afterNanos = Event.elapsedRealtimeNanos(); - - Assert.assertEquals(1, EarlyTraceEvent.sCompletedEvents.size()); - Assert.assertTrue(EarlyTraceEvent.sPendingEventByKey.isEmpty()); - Event event = EarlyTraceEvent.sCompletedEvents.get(0); - Assert.assertEquals(EVENT_NAME, event.mName); - Assert.assertEquals(myThreadId, event.mThreadId); - Assert.assertTrue( - beforeNanos <= event.mBeginTimeNanos && event.mBeginTimeNanos <= afterNanos); - Assert.assertTrue(event.mBeginTimeNanos <= event.mEndTimeNanos); - Assert.assertTrue(beforeNanos <= event.mEndTimeNanos && event.mEndTimeNanos <= afterNanos); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testCanRecordAsyncEvent() { - EarlyTraceEvent.enable(); - long beforeNanos = Event.elapsedRealtimeNanos(); - EarlyTraceEvent.startAsync(EVENT_NAME, EVENT_ID); - EarlyTraceEvent.finishAsync(EVENT_NAME, EVENT_ID); - long afterNanos = Event.elapsedRealtimeNanos(); - - Assert.assertEquals(2, EarlyTraceEvent.sAsyncEvents.size()); - Assert.assertTrue(EarlyTraceEvent.sPendingEventByKey.isEmpty()); - AsyncEvent eventStart = EarlyTraceEvent.sAsyncEvents.get(0); - AsyncEvent eventEnd = EarlyTraceEvent.sAsyncEvents.get(1); - Assert.assertEquals(EVENT_NAME, eventStart.mName); - Assert.assertEquals(EVENT_ID, eventStart.mId); - Assert.assertEquals(EVENT_NAME, eventEnd.mName); - Assert.assertEquals(EVENT_ID, eventEnd.mId); - Assert.assertTrue(beforeNanos <= eventStart.mTimestampNanos - && eventEnd.mTimestampNanos <= afterNanos); - Assert.assertTrue(eventStart.mTimestampNanos <= eventEnd.mTimestampNanos); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testRecordAsyncFinishEventWhenFinishing() { - EarlyTraceEvent.enable(); - EarlyTraceEvent.startAsync(EVENT_NAME, EVENT_ID); - EarlyTraceEvent.disable(); - - Assert.assertEquals(EarlyTraceEvent.STATE_FINISHING, EarlyTraceEvent.sState); - Assert.assertTrue(EarlyTraceEvent.sAsyncEvents.isEmpty()); - Assert.assertEquals(1, EarlyTraceEvent.sPendingAsyncEvents.size()); - EarlyTraceEvent.finishAsync(EVENT_NAME, EVENT_ID); - Assert.assertEquals(EarlyTraceEvent.STATE_FINISHED, EarlyTraceEvent.sState); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testCanRecordEventUsingTryWith() { - EarlyTraceEvent.enable(); - long myThreadId = Process.myTid(); - long beforeNanos = Event.elapsedRealtimeNanos(); - try (TraceEvent e = TraceEvent.scoped(EVENT_NAME)) { - // Required comment to pass presubmit checks. - } - long afterNanos = Event.elapsedRealtimeNanos(); - - Assert.assertEquals(1, EarlyTraceEvent.sCompletedEvents.size()); - Assert.assertTrue(EarlyTraceEvent.sPendingEventByKey.isEmpty()); - Event event = EarlyTraceEvent.sCompletedEvents.get(0); - Assert.assertEquals(EVENT_NAME, event.mName); - Assert.assertEquals(myThreadId, event.mThreadId); - Assert.assertTrue( - beforeNanos <= event.mBeginTimeNanos && event.mBeginTimeNanos <= afterNanos); - Assert.assertTrue(event.mBeginTimeNanos <= event.mEndTimeNanos); - Assert.assertTrue(beforeNanos <= event.mEndTimeNanos && event.mEndTimeNanos <= afterNanos); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testIncompleteEvent() { - EarlyTraceEvent.enable(); - EarlyTraceEvent.begin(EVENT_NAME); - - Assert.assertTrue(EarlyTraceEvent.sCompletedEvents.isEmpty()); - Assert.assertEquals(1, EarlyTraceEvent.sPendingEventByKey.size()); - EarlyTraceEvent.Event event = EarlyTraceEvent.sPendingEventByKey.get( - EarlyTraceEvent.makeEventKeyForCurrentThread(EVENT_NAME)); - Assert.assertEquals(EVENT_NAME, event.mName); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testNoDuplicatePendingEventsFromSameThread() { - EarlyTraceEvent.enable(); - EarlyTraceEvent.begin(EVENT_NAME); - try { - EarlyTraceEvent.begin(EVENT_NAME); - } catch (IllegalArgumentException e) { - // Expected. - return; - } - Assert.fail(); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testDuplicatePendingEventsFromDifferentThreads() throws Exception { - EarlyTraceEvent.enable(); - - Thread otherThread = new Thread(() -> { EarlyTraceEvent.begin(EVENT_NAME); }); - otherThread.start(); - otherThread.join(); - - // At this point we have a pending event with EVENT_NAME name. But events are per - // thread, so we should be able to start EVENT_NAME event in a different thread. - EarlyTraceEvent.begin(EVENT_NAME); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testIgnoreEventsWhenDisabled() { - EarlyTraceEvent.begin(EVENT_NAME); - EarlyTraceEvent.end(EVENT_NAME); - try (TraceEvent e = TraceEvent.scoped(EVENT_NAME2)) { - // Required comment to pass presubmit checks. - } - Assert.assertNull(EarlyTraceEvent.sCompletedEvents); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testIgnoreAsyncEventsWhenDisabled() { - EarlyTraceEvent.startAsync(EVENT_NAME, EVENT_ID); - EarlyTraceEvent.finishAsync(EVENT_NAME, EVENT_ID); - Assert.assertNull(EarlyTraceEvent.sAsyncEvents); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testIgnoreNewEventsWhenFinishing() { - EarlyTraceEvent.enable(); - EarlyTraceEvent.begin(EVENT_NAME); - EarlyTraceEvent.disable(); - - Assert.assertEquals(EarlyTraceEvent.STATE_FINISHING, EarlyTraceEvent.sState); - EarlyTraceEvent.begin(EVENT_NAME2); - EarlyTraceEvent.end(EVENT_NAME2); - - Assert.assertEquals(1, EarlyTraceEvent.sPendingEventByKey.size()); - Assert.assertTrue(EarlyTraceEvent.sCompletedEvents.isEmpty()); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testIgnoreNewAsyncEventsWhenFinishing() { - EarlyTraceEvent.enable(); - EarlyTraceEvent.startAsync(EVENT_NAME, EVENT_ID); - EarlyTraceEvent.disable(); - - Assert.assertEquals(EarlyTraceEvent.STATE_FINISHING, EarlyTraceEvent.sState); - EarlyTraceEvent.startAsync(EVENT_NAME2, EVENT_ID2); - - Assert.assertEquals(1, EarlyTraceEvent.sPendingAsyncEvents.size()); - Assert.assertTrue(EarlyTraceEvent.sAsyncEvents.isEmpty()); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testFinishingToFinished() { - EarlyTraceEvent.enable(); - EarlyTraceEvent.begin(EVENT_NAME); - EarlyTraceEvent.disable(); - - Assert.assertEquals(EarlyTraceEvent.STATE_FINISHING, EarlyTraceEvent.sState); - EarlyTraceEvent.begin(EVENT_NAME2); - EarlyTraceEvent.end(EVENT_NAME2); - EarlyTraceEvent.end(EVENT_NAME); - - Assert.assertEquals(EarlyTraceEvent.STATE_FINISHED, EarlyTraceEvent.sState); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testCannotBeReenabledOnceFinished() { - EarlyTraceEvent.enable(); - EarlyTraceEvent.begin(EVENT_NAME); - EarlyTraceEvent.end(EVENT_NAME); - EarlyTraceEvent.disable(); - Assert.assertEquals(EarlyTraceEvent.STATE_FINISHED, EarlyTraceEvent.sState); - - EarlyTraceEvent.enable(); - Assert.assertEquals(EarlyTraceEvent.STATE_FINISHED, EarlyTraceEvent.sState); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testThreadIdIsRecorded() throws Exception { - EarlyTraceEvent.enable(); - final long[] threadId = {0}; - - Thread thread = new Thread() { - @Override - public void run() { - TraceEvent.begin(EVENT_NAME); - threadId[0] = Process.myTid(); - TraceEvent.end(EVENT_NAME); - } - }; - thread.start(); - thread.join(); - - Assert.assertEquals(1, EarlyTraceEvent.sCompletedEvents.size()); - EarlyTraceEvent.Event event = EarlyTraceEvent.sCompletedEvents.get(0); - Assert.assertEquals(threadId[0], event.mThreadId); - } -} diff --git a/android/javatests/src/org/chromium/base/LocaleUtilsTest.java b/android/javatests/src/org/chromium/base/LocaleUtilsTest.java deleted file mode 100644 index fc37a1f56..000000000 --- a/android/javatests/src/org/chromium/base/LocaleUtilsTest.java +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.annotation.SuppressLint; -import android.os.Build; -import android.os.LocaleList; -import android.support.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.BaseJUnit4ClassRunner; -import org.chromium.base.test.util.MinAndroidSdkLevel; - -import java.util.Locale; - -/** - * Tests for the LocaleUtils class. - */ -@RunWith(BaseJUnit4ClassRunner.class) -public class LocaleUtilsTest { - // This is also a part of test for toLanguageTag when API level is lower than 24 - @Test - @SmallTest - public void testGetUpdatedLanguageForChromium() { - String language = "en"; - String updatedLanguage = LocaleUtils.getUpdatedLanguageForChromium(language); - Assert.assertEquals(language, updatedLanguage); - - language = "iw"; - updatedLanguage = LocaleUtils.getUpdatedLanguageForChromium(language); - Assert.assertEquals("he", updatedLanguage); - - language = "ji"; - updatedLanguage = LocaleUtils.getUpdatedLanguageForChromium(language); - Assert.assertEquals("yi", updatedLanguage); - - language = "in"; - updatedLanguage = LocaleUtils.getUpdatedLanguageForChromium(language); - Assert.assertEquals("id", updatedLanguage); - - language = "tl"; - updatedLanguage = LocaleUtils.getUpdatedLanguageForChromium(language); - Assert.assertEquals("fil", updatedLanguage); - } - - // This is also a part of test for toLanguageTags when API level is 24 or higher - @Test - @SmallTest - @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP) - public void testGetUpdatedLocaleForChromium() { - Locale locale = new Locale("jp"); - Locale updatedLocale = LocaleUtils.getUpdatedLocaleForChromium(locale); - Assert.assertEquals(locale, updatedLocale); - - locale = new Locale("iw"); - updatedLocale = LocaleUtils.getUpdatedLocaleForChromium(locale); - Assert.assertEquals(new Locale("he"), updatedLocale); - - locale = new Locale("ji"); - updatedLocale = LocaleUtils.getUpdatedLocaleForChromium(locale); - Assert.assertEquals(new Locale("yi"), updatedLocale); - - locale = new Locale("in"); - updatedLocale = LocaleUtils.getUpdatedLocaleForChromium(locale); - Assert.assertEquals(new Locale("id"), updatedLocale); - - locale = new Locale("tl"); - updatedLocale = LocaleUtils.getUpdatedLocaleForChromium(locale); - Assert.assertEquals(new Locale("fil"), updatedLocale); - } - - // This is also a part of test for forLanguageTag when API level is lower than 21 - @Test - @SmallTest - public void testGetUpdatedLanguageForAndroid() { - String language = "en"; - String updatedLanguage = LocaleUtils.getUpdatedLanguageForAndroid(language); - Assert.assertEquals(language, updatedLanguage); - - language = "und"; - updatedLanguage = LocaleUtils.getUpdatedLanguageForAndroid(language); - Assert.assertEquals("", updatedLanguage); - - language = "fil"; - updatedLanguage = LocaleUtils.getUpdatedLanguageForAndroid(language); - Assert.assertEquals("tl", updatedLanguage); - } - - // This is also a part of test for forLanguageTag when API level is 21 or higher - @Test - @SmallTest - @MinAndroidSdkLevel(Build.VERSION_CODES.LOLLIPOP) - public void testGetUpdatedLocaleForAndroid() { - Locale locale = new Locale("jp"); - Locale updatedLocale = LocaleUtils.getUpdatedLocaleForAndroid(locale); - Assert.assertEquals(locale, updatedLocale); - - locale = new Locale("und"); - updatedLocale = LocaleUtils.getUpdatedLocaleForAndroid(locale); - Assert.assertEquals(new Locale(""), updatedLocale); - - locale = new Locale("fil"); - updatedLocale = LocaleUtils.getUpdatedLocaleForAndroid(locale); - Assert.assertEquals(new Locale("tl"), updatedLocale); - } - - // Test for toLanguageTag when API level is lower than 24 - @Test - @SmallTest - public void testToLanguageTag() { - Locale locale = new Locale("en", "US"); - String localeString = LocaleUtils.toLanguageTag(locale); - Assert.assertEquals("en-US", localeString); - - locale = new Locale("jp"); - localeString = LocaleUtils.toLanguageTag(locale); - Assert.assertEquals("jp", localeString); - - locale = new Locale("mas"); - localeString = LocaleUtils.toLanguageTag(locale); - Assert.assertEquals("mas", localeString); - - locale = new Locale("es", "005"); - localeString = LocaleUtils.toLanguageTag(locale); - Assert.assertEquals("es-005", localeString); - - locale = new Locale("iw"); - localeString = LocaleUtils.toLanguageTag(locale); - Assert.assertEquals("he", localeString); - - locale = new Locale("ji"); - localeString = LocaleUtils.toLanguageTag(locale); - Assert.assertEquals("yi", localeString); - - locale = new Locale("in", "ID"); - localeString = LocaleUtils.toLanguageTag(locale); - Assert.assertEquals("id-ID", localeString); - - locale = new Locale("tl", "PH"); - localeString = LocaleUtils.toLanguageTag(locale); - Assert.assertEquals("fil-PH", localeString); - - locale = new Locale("no", "NO", "NY"); - localeString = LocaleUtils.toLanguageTag(locale); - Assert.assertEquals("nn-NO", localeString); - } - - // Test for toLanguageTags when API level is 24 or higher - @Test - @SmallTest - @MinAndroidSdkLevel(Build.VERSION_CODES.N) - @SuppressLint("NewApi") - public void testToLanguageTags() { - Locale locale1 = new Locale("en", "US"); - Locale locale2 = new Locale("es", "005"); - LocaleList localeList = new LocaleList(locale1, locale2); - String localeString = LocaleUtils.toLanguageTags(localeList); - Assert.assertEquals("en-US,es-005", localeString); - - locale1 = new Locale("jp"); - locale2 = new Locale("mas"); - localeList = new LocaleList(locale1, locale2); - localeString = LocaleUtils.toLanguageTags(localeList); - Assert.assertEquals("jp,mas", localeString); - - locale1 = new Locale("iw"); - locale2 = new Locale("ji"); - localeList = new LocaleList(locale1, locale2); - localeString = LocaleUtils.toLanguageTags(localeList); - Assert.assertEquals("he,yi", localeString); - - locale1 = new Locale("in", "ID"); - locale2 = new Locale("tl", "PH"); - localeList = new LocaleList(locale1, locale2); - localeString = LocaleUtils.toLanguageTags(localeList); - Assert.assertEquals("id-ID,fil-PH", localeString); - - locale1 = new Locale("no", "NO", "NY"); - localeList = new LocaleList(locale1); - localeString = LocaleUtils.toLanguageTags(localeList); - Assert.assertEquals("nn-NO", localeString); - } - - // Test for forLanguageTag when API level is lower than 21 - @Test - @SmallTest - public void testForLanguageTagCompat() { - String languageTag = ""; - Locale locale = new Locale(""); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "und"; - locale = new Locale(""); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "en"; - locale = new Locale("en"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "mas"; - locale = new Locale("mas"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "en-GB"; - locale = new Locale("en", "GB"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "es-419"; - locale = new Locale("es", "419"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - // Tests if updated Chromium language code and deprecated language code - // are pointing to the same Locale Object. - languageTag = "he"; - locale = new Locale("iw"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "iw"; - locale = new Locale("he"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "ji"; - locale = new Locale("yi"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "yi"; - locale = new Locale("ji"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "in"; - locale = new Locale("id"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "id"; - locale = new Locale("in"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - // Tests for Tagalog/Filipino if updated Chromium language code and - // language code are pointing to the same Locale Object. - languageTag = "tl"; - locale = new Locale("tl"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "fil"; - locale = new Locale("tl"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - // Test with invalid inputs. - languageTag = "notValidLanguage"; - locale = new Locale(""); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - - languageTag = "en-notValidCountry"; - locale = new Locale("en"); - Assert.assertEquals(locale, LocaleUtils.forLanguageTagCompat(languageTag)); - } -} diff --git a/android/javatests/src/org/chromium/base/ObserverListTest.java b/android/javatests/src/org/chromium/base/ObserverListTest.java deleted file mode 100644 index f3b3e939b..000000000 --- a/android/javatests/src/org/chromium/base/ObserverListTest.java +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.support.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.BaseJUnit4ClassRunner; -import org.chromium.base.test.util.Feature; - -import java.util.Collection; -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * Tests for (@link ObserverList}. - */ -@RunWith(BaseJUnit4ClassRunner.class) -public class ObserverListTest { - interface Observer { - void observe(int x); - } - - private static class Foo implements Observer { - private final int mScalar; - private int mTotal = 0; - - Foo(int scalar) { - mScalar = scalar; - } - - @Override - public void observe(int x) { - mTotal += x * mScalar; - } - } - - /** - * An observer which add a given Observer object to the list when observe is called. - */ - private static class FooAdder implements Observer { - private final ObserverList mList; - private final Observer mLucky; - - FooAdder(ObserverList list, Observer oblivious) { - mList = list; - mLucky = oblivious; - } - - @Override - public void observe(int x) { - mList.addObserver(mLucky); - } - } - - /** - * An observer which removes a given Observer object from the list when observe is called. - */ - private static class FooRemover implements Observer { - private final ObserverList mList; - private final Observer mDoomed; - - FooRemover(ObserverList list, Observer innocent) { - mList = list; - mDoomed = innocent; - } - - @Override - public void observe(int x) { - mList.removeObserver(mDoomed); - } - } - - private static int getSizeOfIterable(Iterable iterable) { - if (iterable instanceof Collection) return ((Collection) iterable).size(); - int num = 0; - for (T el : iterable) num++; - return num; - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testRemoveWhileIteration() { - ObserverList observerList = new ObserverList(); - Foo a = new Foo(1); - Foo b = new Foo(-1); - Foo c = new Foo(1); - Foo d = new Foo(-1); - Foo e = new Foo(-1); - FooRemover evil = new FooRemover(observerList, c); - - observerList.addObserver(a); - observerList.addObserver(b); - - for (Observer obs : observerList) obs.observe(10); - - // Removing an observer not in the list should do nothing. - observerList.removeObserver(e); - - observerList.addObserver(evil); - observerList.addObserver(c); - observerList.addObserver(d); - - for (Observer obs : observerList) obs.observe(10); - - // observe should be called twice on a. - Assert.assertEquals(20, a.mTotal); - // observe should be called twice on b. - Assert.assertEquals(-20, b.mTotal); - // evil removed c from the observerList before it got any callbacks. - Assert.assertEquals(0, c.mTotal); - // observe should be called once on d. - Assert.assertEquals(-10, d.mTotal); - // e was never added to the list, observe should not be called. - Assert.assertEquals(0, e.mTotal); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testAddWhileIteration() { - ObserverList observerList = new ObserverList(); - Foo a = new Foo(1); - Foo b = new Foo(-1); - Foo c = new Foo(1); - FooAdder evil = new FooAdder(observerList, c); - - observerList.addObserver(evil); - observerList.addObserver(a); - observerList.addObserver(b); - - for (Observer obs : observerList) obs.observe(10); - - Assert.assertTrue(observerList.hasObserver(c)); - Assert.assertEquals(10, a.mTotal); - Assert.assertEquals(-10, b.mTotal); - Assert.assertEquals(0, c.mTotal); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testIterator() { - ObserverList observerList = new ObserverList(); - observerList.addObserver(5); - observerList.addObserver(10); - observerList.addObserver(15); - Assert.assertEquals(3, getSizeOfIterable(observerList)); - - observerList.removeObserver(10); - Assert.assertEquals(2, getSizeOfIterable(observerList)); - - Iterator it = observerList.iterator(); - Assert.assertTrue(it.hasNext()); - Assert.assertTrue(5 == it.next()); - Assert.assertTrue(it.hasNext()); - Assert.assertTrue(15 == it.next()); - Assert.assertFalse(it.hasNext()); - - boolean removeExceptionThrown = false; - try { - it.remove(); - Assert.fail("Expecting UnsupportedOperationException to be thrown here."); - } catch (UnsupportedOperationException e) { - removeExceptionThrown = true; - } - Assert.assertTrue(removeExceptionThrown); - Assert.assertEquals(2, getSizeOfIterable(observerList)); - - boolean noElementExceptionThrown = false; - try { - it.next(); - Assert.fail("Expecting NoSuchElementException to be thrown here."); - } catch (NoSuchElementException e) { - noElementExceptionThrown = true; - } - Assert.assertTrue(noElementExceptionThrown); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testRewindableIterator() { - ObserverList observerList = new ObserverList(); - observerList.addObserver(5); - observerList.addObserver(10); - observerList.addObserver(15); - Assert.assertEquals(3, getSizeOfIterable(observerList)); - - ObserverList.RewindableIterator it = observerList.rewindableIterator(); - Assert.assertTrue(it.hasNext()); - Assert.assertTrue(5 == it.next()); - Assert.assertTrue(it.hasNext()); - Assert.assertTrue(10 == it.next()); - Assert.assertTrue(it.hasNext()); - Assert.assertTrue(15 == it.next()); - Assert.assertFalse(it.hasNext()); - - it.rewind(); - - Assert.assertTrue(it.hasNext()); - Assert.assertTrue(5 == it.next()); - Assert.assertTrue(it.hasNext()); - Assert.assertTrue(10 == it.next()); - Assert.assertTrue(it.hasNext()); - Assert.assertTrue(15 == it.next()); - Assert.assertEquals(5, (int) observerList.mObservers.get(0)); - observerList.removeObserver(5); - Assert.assertEquals(null, observerList.mObservers.get(0)); - - it.rewind(); - - Assert.assertEquals(10, (int) observerList.mObservers.get(0)); - Assert.assertTrue(it.hasNext()); - Assert.assertTrue(10 == it.next()); - Assert.assertTrue(it.hasNext()); - Assert.assertTrue(15 == it.next()); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testAddObserverReturnValue() { - ObserverList observerList = new ObserverList(); - - Object a = new Object(); - Assert.assertTrue(observerList.addObserver(a)); - Assert.assertFalse(observerList.addObserver(a)); - - Object b = new Object(); - Assert.assertTrue(observerList.addObserver(b)); - Assert.assertFalse(observerList.addObserver(null)); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testRemoveObserverReturnValue() { - ObserverList observerList = new ObserverList(); - - Object a = new Object(); - Object b = new Object(); - observerList.addObserver(a); - observerList.addObserver(b); - - Assert.assertTrue(observerList.removeObserver(a)); - Assert.assertFalse(observerList.removeObserver(a)); - Assert.assertFalse(observerList.removeObserver(new Object())); - Assert.assertTrue(observerList.removeObserver(b)); - Assert.assertFalse(observerList.removeObserver(null)); - - // If we remove an object while iterating, it will be replaced by 'null'. - observerList.addObserver(a); - Assert.assertTrue(observerList.removeObserver(a)); - Assert.assertFalse(observerList.removeObserver(null)); - } - - @Test - @SmallTest - @Feature({"Android-AppBase"}) - public void testSize() { - ObserverList observerList = new ObserverList(); - - Assert.assertEquals(0, observerList.size()); - Assert.assertTrue(observerList.isEmpty()); - - observerList.addObserver(null); - Assert.assertEquals(0, observerList.size()); - Assert.assertTrue(observerList.isEmpty()); - - Object a = new Object(); - observerList.addObserver(a); - Assert.assertEquals(1, observerList.size()); - Assert.assertFalse(observerList.isEmpty()); - - observerList.addObserver(a); - Assert.assertEquals(1, observerList.size()); - Assert.assertFalse(observerList.isEmpty()); - - observerList.addObserver(null); - Assert.assertEquals(1, observerList.size()); - Assert.assertFalse(observerList.isEmpty()); - - Object b = new Object(); - observerList.addObserver(b); - Assert.assertEquals(2, observerList.size()); - Assert.assertFalse(observerList.isEmpty()); - - observerList.removeObserver(null); - Assert.assertEquals(2, observerList.size()); - Assert.assertFalse(observerList.isEmpty()); - - observerList.removeObserver(new Object()); - Assert.assertEquals(2, observerList.size()); - Assert.assertFalse(observerList.isEmpty()); - - observerList.removeObserver(b); - Assert.assertEquals(1, observerList.size()); - Assert.assertFalse(observerList.isEmpty()); - - observerList.removeObserver(b); - Assert.assertEquals(1, observerList.size()); - Assert.assertFalse(observerList.isEmpty()); - - observerList.removeObserver(a); - Assert.assertEquals(0, observerList.size()); - Assert.assertTrue(observerList.isEmpty()); - - observerList.removeObserver(a); - observerList.removeObserver(b); - observerList.removeObserver(null); - observerList.removeObserver(new Object()); - Assert.assertEquals(0, observerList.size()); - Assert.assertTrue(observerList.isEmpty()); - - observerList.addObserver(new Object()); - observerList.addObserver(new Object()); - observerList.addObserver(new Object()); - observerList.addObserver(a); - Assert.assertEquals(4, observerList.size()); - Assert.assertFalse(observerList.isEmpty()); - - observerList.clear(); - Assert.assertEquals(0, observerList.size()); - Assert.assertTrue(observerList.isEmpty()); - - observerList.removeObserver(a); - observerList.removeObserver(b); - observerList.removeObserver(null); - observerList.removeObserver(new Object()); - Assert.assertEquals(0, observerList.size()); - Assert.assertTrue(observerList.isEmpty()); - } -} diff --git a/android/javatests/src/org/chromium/base/StrictModeContextTest.java b/android/javatests/src/org/chromium/base/StrictModeContextTest.java deleted file mode 100644 index 59f38edbf..000000000 --- a/android/javatests/src/org/chromium/base/StrictModeContextTest.java +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.os.StrictMode; -import android.support.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.test.BaseJUnit4ClassRunner; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; - -/** - * Tests for the StrictModeContext class. - */ -@RunWith(BaseJUnit4ClassRunner.class) -public class StrictModeContextTest { - private StrictMode.ThreadPolicy mOldThreadPolicy; - private StrictMode.VmPolicy mOldVmPolicy; - private FileOutputStream mFosForWriting; - private FileInputStream mFisForReading; - - @Before - public void setUp() throws Exception { - mFosForWriting = new FileOutputStream(File.createTempFile("foo", "bar")); - mFisForReading = new FileInputStream(File.createTempFile("foo", "baz")); - enableStrictMode(); - } - - @After - public void tearDown() throws Exception { - disableStrictMode(); - mFosForWriting.close(); - mFisForReading.close(); - } - - private void enableStrictMode() { - mOldThreadPolicy = StrictMode.getThreadPolicy(); - mOldVmPolicy = StrictMode.getVmPolicy(); - StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() - .detectAll() - .penaltyLog() - .penaltyDeath() - .build()); - StrictMode.setVmPolicy( - new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().penaltyDeath().build()); - } - - private void disableStrictMode() { - StrictMode.setThreadPolicy(mOldThreadPolicy); - StrictMode.setVmPolicy(mOldVmPolicy); - } - - private void writeToDisk() { - try { - mFosForWriting.write(ApiCompatibilityUtils.getBytesUtf8("Foo")); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private void assertWriteToDiskThrows() { - boolean didThrow = false; - try { - writeToDisk(); - } catch (Exception e) { - didThrow = true; - } - Assert.assertTrue("Expected disk write to throw.", didThrow); - } - - private void readFromDisk() { - try { - mFisForReading.read(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private void assertReadFromDiskThrows() { - boolean didThrow = false; - try { - readFromDisk(); - } catch (Exception e) { - didThrow = true; - } - Assert.assertTrue("Expected disk read to throw.", didThrow); - } - - @Test - @SmallTest - public void testAllowDiskWrites() { - try (StrictModeContext unused = StrictModeContext.allowDiskWrites()) { - writeToDisk(); - } - assertWriteToDiskThrows(); - } - - @Test - @SmallTest - public void testAllowDiskReads() { - try (StrictModeContext unused = StrictModeContext.allowDiskReads()) { - readFromDisk(); - assertWriteToDiskThrows(); - } - assertReadFromDiskThrows(); - } -} diff --git a/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java b/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java deleted file mode 100644 index 3ecfb3a56..000000000 --- a/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.metrics; - -import android.support.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import org.chromium.base.library_loader.LibraryLoader; -import org.chromium.base.library_loader.LibraryProcessType; -import org.chromium.base.test.BaseJUnit4ClassRunner; -import org.chromium.base.test.util.MetricsUtils.HistogramDelta; - -import java.util.concurrent.TimeUnit; - -/** - * Tests for the Java API for recording UMA histograms. - */ -@RunWith(BaseJUnit4ClassRunner.class) -public class RecordHistogramTest { - @Before - public void setUp() throws Exception { - LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER); - } - - /** - * Tests recording of boolean histograms. - */ - @Test - @SmallTest - public void testRecordBooleanHistogram() { - String histogram = "HelloWorld.BooleanMetric"; - HistogramDelta falseCount = new HistogramDelta(histogram, 0); - HistogramDelta trueCount = new HistogramDelta(histogram, 1); - Assert.assertEquals(0, trueCount.getDelta()); - Assert.assertEquals(0, falseCount.getDelta()); - - RecordHistogram.recordBooleanHistogram(histogram, true); - Assert.assertEquals(1, trueCount.getDelta()); - Assert.assertEquals(0, falseCount.getDelta()); - - RecordHistogram.recordBooleanHistogram(histogram, true); - Assert.assertEquals(2, trueCount.getDelta()); - Assert.assertEquals(0, falseCount.getDelta()); - - RecordHistogram.recordBooleanHistogram(histogram, false); - Assert.assertEquals(2, trueCount.getDelta()); - Assert.assertEquals(1, falseCount.getDelta()); - } - - /** - * Tests recording of enumerated histograms. - */ - @Test - @SmallTest - public void testRecordEnumeratedHistogram() { - String histogram = "HelloWorld.EnumeratedMetric"; - HistogramDelta zeroCount = new HistogramDelta(histogram, 0); - HistogramDelta oneCount = new HistogramDelta(histogram, 1); - HistogramDelta twoCount = new HistogramDelta(histogram, 2); - final int boundary = 3; - - Assert.assertEquals(0, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - - RecordHistogram.recordEnumeratedHistogram(histogram, 0, boundary); - Assert.assertEquals(1, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - - RecordHistogram.recordEnumeratedHistogram(histogram, 0, boundary); - Assert.assertEquals(2, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - - RecordHistogram.recordEnumeratedHistogram(histogram, 2, boundary); - Assert.assertEquals(2, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(1, twoCount.getDelta()); - } - - /** - * Tests recording of count histograms. - */ - @Test - @SmallTest - public void testRecordCountHistogram() { - String histogram = "HelloWorld.CountMetric"; - HistogramDelta zeroCount = new HistogramDelta(histogram, 0); - HistogramDelta oneCount = new HistogramDelta(histogram, 1); - HistogramDelta twoCount = new HistogramDelta(histogram, 2); - HistogramDelta eightThousandCount = new HistogramDelta(histogram, 8000); - - Assert.assertEquals(0, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - Assert.assertEquals(0, eightThousandCount.getDelta()); - - RecordHistogram.recordCountHistogram(histogram, 0); - Assert.assertEquals(1, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - Assert.assertEquals(0, eightThousandCount.getDelta()); - - RecordHistogram.recordCountHistogram(histogram, 0); - Assert.assertEquals(2, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - Assert.assertEquals(0, eightThousandCount.getDelta()); - - RecordHistogram.recordCountHistogram(histogram, 2); - Assert.assertEquals(2, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(1, twoCount.getDelta()); - Assert.assertEquals(0, eightThousandCount.getDelta()); - - RecordHistogram.recordCountHistogram(histogram, 8000); - Assert.assertEquals(2, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(1, twoCount.getDelta()); - Assert.assertEquals(1, eightThousandCount.getDelta()); - } - - /** - * Tests recording of custom times histograms. - */ - @Test - @SmallTest - public void testRecordCustomTimesHistogram() { - String histogram = "HelloWorld.CustomTimesMetric"; - HistogramDelta zeroCount = new HistogramDelta(histogram, 0); - HistogramDelta oneCount = new HistogramDelta(histogram, 1); - HistogramDelta twoCount = new HistogramDelta(histogram, 100); - - Assert.assertEquals(0, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - - TimeUnit milli = TimeUnit.MILLISECONDS; - - RecordHistogram.recordCustomTimesHistogram(histogram, 0, 1, 100, milli, 3); - Assert.assertEquals(1, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - - RecordHistogram.recordCustomTimesHistogram(histogram, 0, 1, 100, milli, 3); - Assert.assertEquals(2, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - - RecordHistogram.recordCustomTimesHistogram(histogram, 95, 1, 100, milli, 3); - Assert.assertEquals(2, zeroCount.getDelta()); - Assert.assertEquals(1, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - - RecordHistogram.recordCustomTimesHistogram(histogram, 200, 1, 100, milli, 3); - Assert.assertEquals(2, zeroCount.getDelta()); - Assert.assertEquals(1, oneCount.getDelta()); - Assert.assertEquals(1, twoCount.getDelta()); - } - - /** - * Tests recording of linear count histograms. - */ - @Test - @SmallTest - public void testRecordLinearCountHistogram() { - String histogram = "HelloWorld.LinearCountMetric"; - HistogramDelta zeroCount = new HistogramDelta(histogram, 0); - HistogramDelta oneCount = new HistogramDelta(histogram, 1); - HistogramDelta twoCount = new HistogramDelta(histogram, 2); - final int min = 1; - final int max = 3; - final int numBuckets = 4; - - Assert.assertEquals(0, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - - RecordHistogram.recordLinearCountHistogram(histogram, 0, min, max, numBuckets); - Assert.assertEquals(1, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - - RecordHistogram.recordLinearCountHistogram(histogram, 0, min, max, numBuckets); - Assert.assertEquals(2, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(0, twoCount.getDelta()); - - RecordHistogram.recordLinearCountHistogram(histogram, 2, min, max, numBuckets); - Assert.assertEquals(2, zeroCount.getDelta()); - Assert.assertEquals(0, oneCount.getDelta()); - Assert.assertEquals(1, twoCount.getDelta()); - } -} diff --git a/android/jni_android.cc b/android/jni_android.cc deleted file mode 100644 index aad84fba1..000000000 --- a/android/jni_android.cc +++ /dev/null @@ -1,327 +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/android/jni_android.h" - -#include -#include - -#include - -#include "base/android/java_exception_reporter.h" -#include "base/android/jni_string.h" -#include "base/debug/debugging_buildflags.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/threading/thread_local.h" - -namespace { -using base::android::GetClass; -using base::android::MethodID; -using base::android::ScopedJavaLocalRef; - -JavaVM* g_jvm = NULL; -base::LazyInstance>::Leaky - g_class_loader = LAZY_INSTANCE_INITIALIZER; -jmethodID g_class_loader_load_class_method_id = 0; - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) -base::LazyInstance>::Leaky - g_stack_frame_pointer = LAZY_INSTANCE_INITIALIZER; -#endif - -bool g_fatal_exception_occurred = false; - -} // namespace - -namespace base { -namespace android { - -JNIEnv* AttachCurrentThread() { - DCHECK(g_jvm); - JNIEnv* env = nullptr; - jint ret = g_jvm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_2); - if (ret == JNI_EDETACHED || !env) { - JavaVMAttachArgs args; - args.version = JNI_VERSION_1_2; - args.group = nullptr; - - // 16 is the maximum size for thread names on Android. - char thread_name[16]; - int err = prctl(PR_GET_NAME, thread_name); - if (err < 0) { - DPLOG(ERROR) << "prctl(PR_GET_NAME)"; - args.name = nullptr; - } else { - args.name = thread_name; - } - - ret = g_jvm->AttachCurrentThread(&env, &args); - DCHECK_EQ(JNI_OK, ret); - } - return env; -} - -JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) { - DCHECK(g_jvm); - JavaVMAttachArgs args; - args.version = JNI_VERSION_1_2; - args.name = thread_name.c_str(); - args.group = NULL; - JNIEnv* env = NULL; - jint ret = g_jvm->AttachCurrentThread(&env, &args); - DCHECK_EQ(JNI_OK, ret); - return env; -} - -void DetachFromVM() { - // Ignore the return value, if the thread is not attached, DetachCurrentThread - // will fail. But it is ok as the native thread may never be attached. - if (g_jvm) - g_jvm->DetachCurrentThread(); -} - -void InitVM(JavaVM* vm) { - DCHECK(!g_jvm || g_jvm == vm); - g_jvm = vm; -} - -bool IsVMInitialized() { - return g_jvm != NULL; -} - -void InitReplacementClassLoader(JNIEnv* env, - const JavaRef& class_loader) { - DCHECK(g_class_loader.Get().is_null()); - DCHECK(!class_loader.is_null()); - - ScopedJavaLocalRef class_loader_clazz = - GetClass(env, "java/lang/ClassLoader"); - CHECK(!ClearException(env)); - g_class_loader_load_class_method_id = - env->GetMethodID(class_loader_clazz.obj(), - "loadClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); - CHECK(!ClearException(env)); - - DCHECK(env->IsInstanceOf(class_loader.obj(), class_loader_clazz.obj())); - g_class_loader.Get().Reset(class_loader); -} - -ScopedJavaLocalRef GetClass(JNIEnv* env, const char* class_name) { - jclass clazz; - if (!g_class_loader.Get().is_null()) { - // ClassLoader.loadClass expects a classname with components separated by - // dots instead of the slashes that JNIEnv::FindClass expects. The JNI - // generator generates names with slashes, so we have to replace them here. - // TODO(torne): move to an approach where we always use ClassLoader except - // for the special case of base::android::GetClassLoader(), and change the - // JNI generator to generate dot-separated names. http://crbug.com/461773 - size_t bufsize = strlen(class_name) + 1; - char dotted_name[bufsize]; - memmove(dotted_name, class_name, bufsize); - for (size_t i = 0; i < bufsize; ++i) { - if (dotted_name[i] == '/') { - dotted_name[i] = '.'; - } - } - - clazz = static_cast( - env->CallObjectMethod(g_class_loader.Get().obj(), - g_class_loader_load_class_method_id, - ConvertUTF8ToJavaString(env, dotted_name).obj())); - } else { - clazz = env->FindClass(class_name); - } - if (ClearException(env) || !clazz) { - LOG(FATAL) << "Failed to find class " << class_name; - } - return ScopedJavaLocalRef(env, clazz); -} - -jclass LazyGetClass( - JNIEnv* env, - const char* class_name, - base::subtle::AtomicWord* atomic_class_id) { - static_assert(sizeof(subtle::AtomicWord) >= sizeof(jclass), - "AtomicWord can't be smaller than jclass"); - subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_class_id); - if (value) - return reinterpret_cast(value); - ScopedJavaGlobalRef clazz; - clazz.Reset(GetClass(env, class_name)); - subtle::AtomicWord null_aw = reinterpret_cast(NULL); - subtle::AtomicWord cas_result = base::subtle::Release_CompareAndSwap( - atomic_class_id, - null_aw, - reinterpret_cast(clazz.obj())); - if (cas_result == null_aw) { - // We intentionally leak the global ref since we now storing it as a raw - // pointer in |atomic_class_id|. - return clazz.Release(); - } else { - return reinterpret_cast(cas_result); - } -} - -template -jmethodID MethodID::Get(JNIEnv* env, - jclass clazz, - const char* method_name, - const char* jni_signature) { - jmethodID id = type == TYPE_STATIC ? - env->GetStaticMethodID(clazz, method_name, jni_signature) : - env->GetMethodID(clazz, method_name, jni_signature); - if (base::android::ClearException(env) || !id) { - LOG(FATAL) << "Failed to find " << - (type == TYPE_STATIC ? "static " : "") << - "method " << method_name << " " << jni_signature; - } - return id; -} - -// If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call -// into ::Get() above. If there's a race, it's ok since the values are the same -// (and the duplicated effort will happen only once). -template -jmethodID MethodID::LazyGet(JNIEnv* env, - jclass clazz, - const char* method_name, - const char* jni_signature, - base::subtle::AtomicWord* atomic_method_id) { - static_assert(sizeof(subtle::AtomicWord) >= sizeof(jmethodID), - "AtomicWord can't be smaller than jMethodID"); - subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_method_id); - if (value) - return reinterpret_cast(value); - jmethodID id = MethodID::Get(env, clazz, method_name, jni_signature); - base::subtle::Release_Store( - atomic_method_id, reinterpret_cast(id)); - return id; -} - -// Various template instantiations. -template jmethodID MethodID::Get( - JNIEnv* env, jclass clazz, const char* method_name, - const char* jni_signature); - -template jmethodID MethodID::Get( - JNIEnv* env, jclass clazz, const char* method_name, - const char* jni_signature); - -template jmethodID MethodID::LazyGet( - JNIEnv* env, jclass clazz, const char* method_name, - const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); - -template jmethodID MethodID::LazyGet( - JNIEnv* env, jclass clazz, const char* method_name, - const char* jni_signature, base::subtle::AtomicWord* atomic_method_id); - -bool HasException(JNIEnv* env) { - return env->ExceptionCheck() != JNI_FALSE; -} - -bool ClearException(JNIEnv* env) { - if (!HasException(env)) - return false; - env->ExceptionDescribe(); - env->ExceptionClear(); - return true; -} - -void CheckException(JNIEnv* env) { - if (!HasException(env)) - return; - - jthrowable java_throwable = env->ExceptionOccurred(); - if (java_throwable) { - // Clear the pending exception, since a local reference is now held. - env->ExceptionDescribe(); - env->ExceptionClear(); - - if (g_fatal_exception_occurred) { - // Another exception (probably OOM) occurred during GetJavaExceptionInfo. - base::android::SetJavaException( - "Java OOM'ed in exception handling, check logcat"); - } else { - g_fatal_exception_occurred = true; - // RVO should avoid any extra copies of the exception string. - base::android::SetJavaException( - GetJavaExceptionInfo(env, java_throwable).c_str()); - } - } - - // Now, feel good about it and die. - LOG(FATAL) << "Please include Java exception stack in crash report"; -} - -std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { - ScopedJavaLocalRef throwable_clazz = - GetClass(env, "java/lang/Throwable"); - jmethodID throwable_printstacktrace = - MethodID::Get( - env, throwable_clazz.obj(), "printStackTrace", - "(Ljava/io/PrintStream;)V"); - - // Create an instance of ByteArrayOutputStream. - ScopedJavaLocalRef bytearray_output_stream_clazz = - GetClass(env, "java/io/ByteArrayOutputStream"); - jmethodID bytearray_output_stream_constructor = - MethodID::Get( - env, bytearray_output_stream_clazz.obj(), "", "()V"); - jmethodID bytearray_output_stream_tostring = - MethodID::Get( - env, bytearray_output_stream_clazz.obj(), "toString", - "()Ljava/lang/String;"); - ScopedJavaLocalRef bytearray_output_stream(env, - env->NewObject(bytearray_output_stream_clazz.obj(), - bytearray_output_stream_constructor)); - CheckException(env); - - // Create an instance of PrintStream. - ScopedJavaLocalRef printstream_clazz = - GetClass(env, "java/io/PrintStream"); - jmethodID printstream_constructor = - MethodID::Get( - env, printstream_clazz.obj(), "", - "(Ljava/io/OutputStream;)V"); - ScopedJavaLocalRef printstream(env, - env->NewObject(printstream_clazz.obj(), printstream_constructor, - bytearray_output_stream.obj())); - CheckException(env); - - // Call Throwable.printStackTrace(PrintStream) - env->CallVoidMethod(java_throwable, throwable_printstacktrace, - printstream.obj()); - CheckException(env); - - // Call ByteArrayOutputStream.toString() - ScopedJavaLocalRef exception_string( - env, static_cast( - env->CallObjectMethod(bytearray_output_stream.obj(), - bytearray_output_stream_tostring))); - CheckException(env); - - return ConvertJavaStringToUTF8(exception_string); -} - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -JNIStackFrameSaver::JNIStackFrameSaver(void* current_fp) { - previous_fp_ = g_stack_frame_pointer.Pointer()->Get(); - g_stack_frame_pointer.Pointer()->Set(current_fp); -} - -JNIStackFrameSaver::~JNIStackFrameSaver() { - g_stack_frame_pointer.Pointer()->Set(previous_fp_); -} - -void* JNIStackFrameSaver::SavedFrame() { - return g_stack_frame_pointer.Pointer()->Get(); -} - -#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -} // namespace android -} // namespace base diff --git a/android/jni_android.h b/android/jni_android.h deleted file mode 100644 index fba6113cd..000000000 --- a/android/jni_android.h +++ /dev/null @@ -1,173 +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_ANDROID_JNI_ANDROID_H_ -#define BASE_ANDROID_JNI_ANDROID_H_ - -#include -#include - -#include - -#include "base/android/scoped_java_ref.h" -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "base/debug/debugging_buildflags.h" -#include "base/debug/stack_trace.h" -#include "base/macros.h" - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -// When profiling is enabled (enable_profiling=true) this macro is added to -// all generated JNI stubs so that it becomes the last thing that runs before -// control goes into Java. -// -// This macro saves stack frame pointer of the current function. Saved value -// used later by JNI_LINK_SAVED_FRAME_POINTER. -#define JNI_SAVE_FRAME_POINTER \ - base::android::JNIStackFrameSaver jni_frame_saver(__builtin_frame_address(0)) - -// When profiling is enabled (enable_profiling=true) this macro is added to -// all generated JNI callbacks so that it becomes the first thing that runs -// after control returns from Java. -// -// This macro links stack frame of the current function to the stack frame -// saved by JNI_SAVE_FRAME_POINTER, allowing frame-based unwinding -// (used by the heap profiler) to produce complete traces. -#define JNI_LINK_SAVED_FRAME_POINTER \ - base::debug::ScopedStackFrameLinker jni_frame_linker( \ - __builtin_frame_address(0), \ - base::android::JNIStackFrameSaver::SavedFrame()) - -#else - -// Frame-based stack unwinding is not supported, do nothing. -#define JNI_SAVE_FRAME_POINTER -#define JNI_LINK_SAVED_FRAME_POINTER - -#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -namespace base { -namespace android { - -// Used to mark symbols to be exported in a shared library's symbol table. -#define JNI_EXPORT __attribute__ ((visibility("default"))) - -// Contains the registration method information for initializing JNI bindings. -struct RegistrationMethod { - const char* name; - bool (*func)(JNIEnv* env); -}; - -// Attaches the current thread to the VM (if necessary) and return the JNIEnv*. -BASE_EXPORT JNIEnv* AttachCurrentThread(); - -// Same to AttachCurrentThread except that thread name will be set to -// |thread_name| if it is the first call. Otherwise, thread_name won't be -// changed. AttachCurrentThread() doesn't regard underlying platform thread -// name, but just resets it to "Thread-???". This function should be called -// right after new thread is created if it is important to keep thread name. -BASE_EXPORT JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name); - -// Detaches the current thread from VM if it is attached. -BASE_EXPORT void DetachFromVM(); - -// Initializes the global JVM. -BASE_EXPORT void InitVM(JavaVM* vm); - -// Returns true if the global JVM has been initialized. -BASE_EXPORT bool IsVMInitialized(); - -// Initializes the global ClassLoader used by the GetClass and LazyGetClass -// methods. This is needed because JNI will use the base ClassLoader when there -// is no Java code on the stack. The base ClassLoader doesn't know about any of -// the application classes and will fail to lookup anything other than system -// classes. -BASE_EXPORT void InitReplacementClassLoader( - JNIEnv* env, - const JavaRef& class_loader); - -// Finds the class named |class_name| and returns it. -// Use this method instead of invoking directly the JNI FindClass method (to -// prevent leaking local references). -// This method triggers a fatal assertion if the class could not be found. -// Use HasClass if you need to check whether the class exists. -BASE_EXPORT ScopedJavaLocalRef GetClass(JNIEnv* env, - const char* class_name); - -// The method will initialize |atomic_class_id| to contain a global ref to the -// class. And will return that ref on subsequent calls. It's the caller's -// responsibility to release the ref when it is no longer needed. -// The caller is responsible to zero-initialize |atomic_method_id|. -// It's fine to simultaneously call this on multiple threads referencing the -// same |atomic_method_id|. -BASE_EXPORT jclass LazyGetClass( - JNIEnv* env, - const char* class_name, - base::subtle::AtomicWord* atomic_class_id); - -// This class is a wrapper for JNIEnv Get(Static)MethodID. -class BASE_EXPORT MethodID { - public: - enum Type { - TYPE_STATIC, - TYPE_INSTANCE, - }; - - // Returns the method ID for the method with the specified name and signature. - // This method triggers a fatal assertion if the method could not be found. - template - static jmethodID Get(JNIEnv* env, - jclass clazz, - const char* method_name, - const char* jni_signature); - - // The caller is responsible to zero-initialize |atomic_method_id|. - // It's fine to simultaneously call this on multiple threads referencing the - // same |atomic_method_id|. - template - static jmethodID LazyGet(JNIEnv* env, - jclass clazz, - const char* method_name, - const char* jni_signature, - base::subtle::AtomicWord* atomic_method_id); -}; - -// Returns true if an exception is pending in the provided JNIEnv*. -BASE_EXPORT bool HasException(JNIEnv* env); - -// If an exception is pending in the provided JNIEnv*, this function clears it -// and returns true. -BASE_EXPORT bool ClearException(JNIEnv* env); - -// This function will call CHECK() macro if there's any pending exception. -BASE_EXPORT void CheckException(JNIEnv* env); - -// This returns a string representation of the java stack trace. -BASE_EXPORT std::string GetJavaExceptionInfo(JNIEnv* env, - jthrowable java_throwable); - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -// Saves caller's PC and stack frame in a thread-local variable. -// Implemented only when profiling is enabled (enable_profiling=true). -class BASE_EXPORT JNIStackFrameSaver { - public: - JNIStackFrameSaver(void* current_fp); - ~JNIStackFrameSaver(); - static void* SavedFrame(); - - private: - void* previous_fp_; - - DISALLOW_COPY_AND_ASSIGN(JNIStackFrameSaver); -}; - -#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_JNI_ANDROID_H_ diff --git a/android/jni_android_unittest.cc b/android/jni_android_unittest.cc deleted file mode 100644 index dabd48007..000000000 --- a/android/jni_android_unittest.cc +++ /dev/null @@ -1,61 +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/android/jni_android.h" - -#include "base/at_exit.h" -#include "base/logging.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace android { - -namespace { - -base::subtle::AtomicWord g_atomic_id = 0; -int LazyMethodIDCall(JNIEnv* env, jclass clazz, int p) { - jmethodID id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, clazz, - "abs", - "(I)I", - &g_atomic_id); - - return env->CallStaticIntMethod(clazz, id, p); -} - -int MethodIDCall(JNIEnv* env, jclass clazz, jmethodID id, int p) { - return env->CallStaticIntMethod(clazz, id, p); -} - -} // namespace - -TEST(JNIAndroidMicrobenchmark, MethodId) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef clazz(GetClass(env, "java/lang/Math")); - base::Time start_lazy = base::Time::Now(); - int o = 0; - for (int i = 0; i < 1024; ++i) - o += LazyMethodIDCall(env, clazz.obj(), i); - base::Time end_lazy = base::Time::Now(); - - jmethodID id = reinterpret_cast(g_atomic_id); - base::Time start = base::Time::Now(); - for (int i = 0; i < 1024; ++i) - o += MethodIDCall(env, clazz.obj(), id, i); - base::Time end = base::Time::Now(); - - // On a Galaxy Nexus, results were in the range of: - // JNI LazyMethodIDCall (us) 1984 - // JNI MethodIDCall (us) 1861 - LOG(ERROR) << "JNI LazyMethodIDCall (us) " << - base::TimeDelta(end_lazy - start_lazy).InMicroseconds(); - LOG(ERROR) << "JNI MethodIDCall (us) " << - base::TimeDelta(end - start).InMicroseconds(); - LOG(ERROR) << "JNI " << o; -} - - -} // namespace android -} // namespace base diff --git a/android/jni_array.cc b/android/jni_array.cc deleted file mode 100644 index 52b1679bb..000000000 --- a/android/jni_array.cc +++ /dev/null @@ -1,311 +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/android/jni_array.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/logging.h" - -namespace base { -namespace android { -namespace { - -// As |GetArrayLength| makes no guarantees about the returned value (e.g., it -// may be -1 if |array| is not a valid Java array), provide a safe wrapper -// that always returns a valid, non-negative size. -template -size_t SafeGetArrayLength(JNIEnv* env, JavaArrayType jarray) { - DCHECK(jarray); - jsize length = env->GetArrayLength(jarray); - DCHECK_GE(length, 0) << "Invalid array length: " << length; - return static_cast(std::max(0, length)); -} - -} // namespace - -ScopedJavaLocalRef ToJavaByteArray(JNIEnv* env, - const uint8_t* bytes, - size_t len) { - jbyteArray byte_array = env->NewByteArray(len); - CheckException(env); - DCHECK(byte_array); - - env->SetByteArrayRegion( - byte_array, 0, len, reinterpret_cast(bytes)); - CheckException(env); - - return ScopedJavaLocalRef(env, byte_array); -} - -ScopedJavaLocalRef ToJavaByteArray( - JNIEnv* env, - const std::vector& bytes) { - return ToJavaByteArray(env, bytes.data(), bytes.size()); -} - -ScopedJavaLocalRef ToJavaBooleanArray(JNIEnv* env, - const bool* bools, - size_t len) { - jbooleanArray boolean_array = env->NewBooleanArray(len); - CheckException(env); - DCHECK(boolean_array); - - env->SetBooleanArrayRegion(boolean_array, 0, len, - reinterpret_cast(bools)); - CheckException(env); - - return ScopedJavaLocalRef(env, boolean_array); -} - -ScopedJavaLocalRef ToJavaIntArray( - JNIEnv* env, const int* ints, size_t len) { - jintArray int_array = env->NewIntArray(len); - CheckException(env); - DCHECK(int_array); - - env->SetIntArrayRegion( - int_array, 0, len, reinterpret_cast(ints)); - CheckException(env); - - return ScopedJavaLocalRef(env, int_array); -} - -ScopedJavaLocalRef ToJavaIntArray( - JNIEnv* env, const std::vector& ints) { - return ToJavaIntArray(env, ints.data(), ints.size()); -} - -ScopedJavaLocalRef ToJavaLongArray(JNIEnv* env, - const int64_t* longs, - size_t len) { - jlongArray long_array = env->NewLongArray(len); - CheckException(env); - DCHECK(long_array); - - env->SetLongArrayRegion( - long_array, 0, len, reinterpret_cast(longs)); - CheckException(env); - - return ScopedJavaLocalRef(env, long_array); -} - -// Returns a new Java long array converted from the given int64_t array. -BASE_EXPORT ScopedJavaLocalRef ToJavaLongArray( - JNIEnv* env, - const std::vector& longs) { - return ToJavaLongArray(env, longs.data(), longs.size()); -} - -// Returns a new Java float array converted from the given C++ float array. -BASE_EXPORT ScopedJavaLocalRef ToJavaFloatArray( - JNIEnv* env, const float* floats, size_t len) { - jfloatArray float_array = env->NewFloatArray(len); - CheckException(env); - DCHECK(float_array); - - env->SetFloatArrayRegion( - float_array, 0, len, reinterpret_cast(floats)); - CheckException(env); - - return ScopedJavaLocalRef(env, float_array); -} - -BASE_EXPORT ScopedJavaLocalRef ToJavaFloatArray( - JNIEnv* env, - const std::vector& floats) { - return ToJavaFloatArray(env, floats.data(), floats.size()); -} - -ScopedJavaLocalRef ToJavaArrayOfByteArray( - JNIEnv* env, const std::vector& v) { - ScopedJavaLocalRef byte_array_clazz = GetClass(env, "[B"); - jobjectArray joa = env->NewObjectArray(v.size(), - byte_array_clazz.obj(), NULL); - CheckException(env); - - for (size_t i = 0; i < v.size(); ++i) { - ScopedJavaLocalRef byte_array = ToJavaByteArray( - env, reinterpret_cast(v[i].data()), v[i].length()); - env->SetObjectArrayElement(joa, i, byte_array.obj()); - } - return ScopedJavaLocalRef(env, joa); -} - -ScopedJavaLocalRef ToJavaArrayOfStrings( - JNIEnv* env, const std::vector& v) { - ScopedJavaLocalRef string_clazz = GetClass(env, "java/lang/String"); - jobjectArray joa = env->NewObjectArray(v.size(), string_clazz.obj(), NULL); - CheckException(env); - - for (size_t i = 0; i < v.size(); ++i) { - ScopedJavaLocalRef item = ConvertUTF8ToJavaString(env, v[i]); - env->SetObjectArrayElement(joa, i, item.obj()); - } - return ScopedJavaLocalRef(env, joa); -} - -ScopedJavaLocalRef ToJavaArrayOfStrings( - JNIEnv* env, const std::vector& v) { - ScopedJavaLocalRef string_clazz = GetClass(env, "java/lang/String"); - jobjectArray joa = env->NewObjectArray(v.size(), string_clazz.obj(), NULL); - CheckException(env); - - for (size_t i = 0; i < v.size(); ++i) { - ScopedJavaLocalRef item = ConvertUTF16ToJavaString(env, v[i]); - env->SetObjectArrayElement(joa, i, item.obj()); - } - return ScopedJavaLocalRef(env, joa); -} - -void AppendJavaStringArrayToStringVector(JNIEnv* env, - jobjectArray array, - std::vector* out) { - DCHECK(out); - if (!array) - return; - size_t len = SafeGetArrayLength(env, array); - size_t back = out->size(); - out->resize(back + len); - for (size_t i = 0; i < len; ++i) { - ScopedJavaLocalRef str(env, - static_cast(env->GetObjectArrayElement(array, i))); - ConvertJavaStringToUTF16(env, str.obj(), out->data() + back + i); - } -} - -void AppendJavaStringArrayToStringVector(JNIEnv* env, - jobjectArray array, - std::vector* out) { - DCHECK(out); - if (!array) - return; - size_t len = SafeGetArrayLength(env, array); - size_t back = out->size(); - out->resize(back + len); - for (size_t i = 0; i < len; ++i) { - ScopedJavaLocalRef str(env, - static_cast(env->GetObjectArrayElement(array, i))); - ConvertJavaStringToUTF8(env, str.obj(), out->data() + back + i); - } -} - -void AppendJavaByteArrayToByteVector(JNIEnv* env, - jbyteArray byte_array, - std::vector* out) { - DCHECK(out); - if (!byte_array) - return; - size_t len = SafeGetArrayLength(env, byte_array); - if (!len) - return; - size_t back = out->size(); - out->resize(back + len); - env->GetByteArrayRegion(byte_array, 0, len, - reinterpret_cast(out->data() + back)); -} - -void JavaByteArrayToByteVector(JNIEnv* env, - jbyteArray byte_array, - std::vector* out) { - DCHECK(out); - DCHECK(byte_array); - out->clear(); - AppendJavaByteArrayToByteVector(env, byte_array, out); -} - -void JavaBooleanArrayToBoolVector(JNIEnv* env, - jbooleanArray boolean_array, - std::vector* out) { - DCHECK(out); - if (!boolean_array) - return; - size_t len = SafeGetArrayLength(env, boolean_array); - if (!len) - return; - out->resize(len); - // It is not possible to get bool* out of vector. - jboolean* values = env->GetBooleanArrayElements(boolean_array, nullptr); - for (size_t i = 0; i < len; ++i) { - out->at(i) = static_cast(values[i]); - } -} - -void JavaIntArrayToIntVector(JNIEnv* env, - jintArray int_array, - std::vector* out) { - DCHECK(out); - size_t len = SafeGetArrayLength(env, int_array); - out->resize(len); - if (!len) - return; - env->GetIntArrayRegion(int_array, 0, len, out->data()); -} - -void JavaLongArrayToInt64Vector(JNIEnv* env, - jlongArray long_array, - std::vector* out) { - DCHECK(out); - std::vector temp; - JavaLongArrayToLongVector(env, long_array, &temp); - out->resize(0); - out->insert(out->begin(), temp.begin(), temp.end()); -} - -void JavaLongArrayToLongVector(JNIEnv* env, - jlongArray long_array, - std::vector* out) { - DCHECK(out); - size_t len = SafeGetArrayLength(env, long_array); - out->resize(len); - if (!len) - return; - env->GetLongArrayRegion(long_array, 0, len, out->data()); -} - -void JavaFloatArrayToFloatVector(JNIEnv* env, - jfloatArray float_array, - std::vector* out) { - DCHECK(out); - size_t len = SafeGetArrayLength(env, float_array); - out->resize(len); - if (!len) - return; - env->GetFloatArrayRegion(float_array, 0, len, out->data()); -} - -void JavaArrayOfByteArrayToStringVector( - JNIEnv* env, - jobjectArray array, - std::vector* out) { - DCHECK(out); - size_t len = SafeGetArrayLength(env, array); - out->resize(len); - for (size_t i = 0; i < len; ++i) { - ScopedJavaLocalRef bytes_array( - env, static_cast( - env->GetObjectArrayElement(array, i))); - jsize bytes_len = env->GetArrayLength(bytes_array.obj()); - jbyte* bytes = env->GetByteArrayElements(bytes_array.obj(), nullptr); - (*out)[i].assign(reinterpret_cast(bytes), bytes_len); - env->ReleaseByteArrayElements(bytes_array.obj(), bytes, JNI_ABORT); - } -} - -void JavaArrayOfIntArrayToIntVector( - JNIEnv* env, - jobjectArray array, - std::vector>* out) { - DCHECK(out); - size_t len = SafeGetArrayLength(env, array); - out->resize(len); - for (size_t i = 0; i < len; ++i) { - ScopedJavaLocalRef int_array( - env, static_cast(env->GetObjectArrayElement(array, i))); - JavaIntArrayToIntVector(env, int_array.obj(), &out->at(i)); - } -} - -} // namespace android -} // namespace base diff --git a/android/jni_array.h b/android/jni_array.h deleted file mode 100644 index 66c56ef30..000000000 --- a/android/jni_array.h +++ /dev/null @@ -1,134 +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_ANDROID_JNI_ARRAY_H_ -#define BASE_ANDROID_JNI_ARRAY_H_ - -#include -#include -#include -#include -#include - -#include "base/android/scoped_java_ref.h" -#include "base/strings/string16.h" - -namespace base { -namespace android { - -// Returns a new Java byte array converted from the given bytes array. -BASE_EXPORT ScopedJavaLocalRef ToJavaByteArray(JNIEnv* env, - const uint8_t* bytes, - size_t len); - -BASE_EXPORT ScopedJavaLocalRef ToJavaByteArray( - JNIEnv* env, - const std::vector& bytes); - -// Returns a new Java boolean array converted from the given bool array. -BASE_EXPORT ScopedJavaLocalRef -ToJavaBooleanArray(JNIEnv* env, const bool* bools, size_t len); - -// Returns a new Java int array converted from the given int array. -BASE_EXPORT ScopedJavaLocalRef ToJavaIntArray( - JNIEnv* env, const int* ints, size_t len); - -BASE_EXPORT ScopedJavaLocalRef ToJavaIntArray( - JNIEnv* env, const std::vector& ints); - -// Returns a new Java long array converted from the given int64_t array. -BASE_EXPORT ScopedJavaLocalRef ToJavaLongArray(JNIEnv* env, - const int64_t* longs, - size_t len); - -BASE_EXPORT ScopedJavaLocalRef ToJavaLongArray( - JNIEnv* env, - const std::vector& longs); - -// Returns a new Java float array converted from the given C++ float array. -BASE_EXPORT ScopedJavaLocalRef ToJavaFloatArray( - JNIEnv* env, const float* floats, size_t len); - -BASE_EXPORT ScopedJavaLocalRef ToJavaFloatArray( - JNIEnv* env, - const std::vector& floats); - -// Returns a array of Java byte array converted from |v|. -BASE_EXPORT ScopedJavaLocalRef ToJavaArrayOfByteArray( - JNIEnv* env, const std::vector& v); - -BASE_EXPORT ScopedJavaLocalRef ToJavaArrayOfStrings( - JNIEnv* env, const std::vector& v); - -BASE_EXPORT ScopedJavaLocalRef ToJavaArrayOfStrings( - JNIEnv* env, const std::vector& v); - -// Converts a Java string array to a native array. -BASE_EXPORT void AppendJavaStringArrayToStringVector( - JNIEnv* env, - jobjectArray array, - std::vector* out); - -BASE_EXPORT void AppendJavaStringArrayToStringVector( - JNIEnv* env, - jobjectArray array, - std::vector* out); - -// Appends the Java bytes in |bytes_array| onto the end of |out|. -BASE_EXPORT void AppendJavaByteArrayToByteVector(JNIEnv* env, - jbyteArray byte_array, - std::vector* out); - -// Replaces the content of |out| with the Java bytes in |bytes_array|. -BASE_EXPORT void JavaByteArrayToByteVector(JNIEnv* env, - jbyteArray byte_array, - std::vector* out); - -// Replaces the content of |out| with the Java booleans in |boolean_array|. -BASE_EXPORT void JavaBooleanArrayToBoolVector(JNIEnv* env, - jbooleanArray boolean_array, - std::vector* out); - -// Replaces the content of |out| with the Java ints in |int_array|. -BASE_EXPORT void JavaIntArrayToIntVector( - JNIEnv* env, - jintArray int_array, - std::vector* out); - -// Replaces the content of |out| with the Java longs in |long_array|. -BASE_EXPORT void JavaLongArrayToInt64Vector(JNIEnv* env, - jlongArray long_array, - std::vector* out); - -// Replaces the content of |out| with the Java longs in |long_array|. -BASE_EXPORT void JavaLongArrayToLongVector( - JNIEnv* env, - jlongArray long_array, - std::vector* out); - -// Replaces the content of |out| with the Java floats in |float_array|. -BASE_EXPORT void JavaFloatArrayToFloatVector( - JNIEnv* env, - jfloatArray float_array, - std::vector* out); - -// Assuming |array| is an byte[][] (array of byte arrays), replaces the -// content of |out| with the corresponding vector of strings. No UTF-8 -// conversion is performed. -BASE_EXPORT void JavaArrayOfByteArrayToStringVector( - JNIEnv* env, - jobjectArray array, - std::vector* out); - -// Assuming |array| is an int[][] (array of int arrays), replaces the -// contents of |out| with the corresponding vectors of ints. -BASE_EXPORT void JavaArrayOfIntArrayToIntVector( - JNIEnv* env, - jobjectArray array, - std::vector>* out); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_JNI_ARRAY_H_ diff --git a/android/jni_array_unittest.cc b/android/jni_array_unittest.cc deleted file mode 100644 index 245cd5082..000000000 --- a/android/jni_array_unittest.cc +++ /dev/null @@ -1,389 +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/android/jni_array.h" - -#include -#include - -#include - -#include "base/android/jni_android.h" -#include "base/android/scoped_java_ref.h" -#include "base/macros.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace android { - -TEST(JniArray, BasicConversions) { - const uint8_t kBytes[] = {0, 1, 2, 3}; - const size_t kLen = arraysize(kBytes); - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef bytes = ToJavaByteArray(env, kBytes, kLen); - ASSERT_TRUE(bytes.obj()); - - std::vector inputVector(kBytes, kBytes + kLen); - ScopedJavaLocalRef bytesFromVector = - ToJavaByteArray(env, inputVector); - ASSERT_TRUE(bytesFromVector.obj()); - - std::vector vectorFromBytes(5); - std::vector vectorFromVector(5); - JavaByteArrayToByteVector(env, bytes.obj(), &vectorFromBytes); - JavaByteArrayToByteVector(env, bytesFromVector.obj(), &vectorFromVector); - EXPECT_EQ(4U, vectorFromBytes.size()); - EXPECT_EQ(4U, vectorFromVector.size()); - std::vector expected_vec(kBytes, kBytes + kLen); - EXPECT_EQ(expected_vec, vectorFromBytes); - EXPECT_EQ(expected_vec, vectorFromVector); - - AppendJavaByteArrayToByteVector(env, bytes.obj(), &vectorFromBytes); - EXPECT_EQ(8U, vectorFromBytes.size()); - expected_vec.insert(expected_vec.end(), kBytes, kBytes + kLen); - EXPECT_EQ(expected_vec, vectorFromBytes); -} - -void CheckBoolConversion(JNIEnv* env, - const bool* bool_array, - const size_t len, - const ScopedJavaLocalRef& booleans) { - ASSERT_TRUE(booleans.obj()); - - jsize java_array_len = env->GetArrayLength(booleans.obj()); - ASSERT_EQ(static_cast(len), java_array_len); - - jboolean value; - for (size_t i = 0; i < len; ++i) { - env->GetBooleanArrayRegion(booleans.obj(), i, 1, &value); - ASSERT_EQ(bool_array[i], value); - } -} - -TEST(JniArray, BoolConversions) { - const bool kBools[] = {false, true, false}; - const size_t kLen = arraysize(kBools); - - JNIEnv* env = AttachCurrentThread(); - CheckBoolConversion(env, kBools, kLen, ToJavaBooleanArray(env, kBools, kLen)); -} - -void CheckIntConversion( - JNIEnv* env, - const int* int_array, - const size_t len, - const ScopedJavaLocalRef& ints) { - ASSERT_TRUE(ints.obj()); - - jsize java_array_len = env->GetArrayLength(ints.obj()); - ASSERT_EQ(static_cast(len), java_array_len); - - jint value; - for (size_t i = 0; i < len; ++i) { - env->GetIntArrayRegion(ints.obj(), i, 1, &value); - ASSERT_EQ(int_array[i], value); - } -} - -TEST(JniArray, IntConversions) { - const int kInts[] = {0, 1, -1, std::numeric_limits::min(), - std::numeric_limits::max()}; - const size_t kLen = arraysize(kInts); - - JNIEnv* env = AttachCurrentThread(); - CheckIntConversion(env, kInts, kLen, ToJavaIntArray(env, kInts, kLen)); - - const std::vector vec(kInts, kInts + kLen); - CheckIntConversion(env, kInts, kLen, ToJavaIntArray(env, vec)); -} - -void CheckLongConversion(JNIEnv* env, - const int64_t* long_array, - const size_t len, - const ScopedJavaLocalRef& longs) { - ASSERT_TRUE(longs.obj()); - - jsize java_array_len = env->GetArrayLength(longs.obj()); - ASSERT_EQ(static_cast(len), java_array_len); - - jlong value; - for (size_t i = 0; i < len; ++i) { - env->GetLongArrayRegion(longs.obj(), i, 1, &value); - ASSERT_EQ(long_array[i], value); - } -} - -TEST(JniArray, LongConversions) { - const int64_t kLongs[] = {0, 1, -1, std::numeric_limits::min(), - std::numeric_limits::max()}; - const size_t kLen = arraysize(kLongs); - - JNIEnv* env = AttachCurrentThread(); - CheckLongConversion(env, kLongs, kLen, ToJavaLongArray(env, kLongs, kLen)); - - const std::vector vec(kLongs, kLongs + kLen); - CheckLongConversion(env, kLongs, kLen, ToJavaLongArray(env, vec)); -} - -void CheckIntArrayConversion(JNIEnv* env, - ScopedJavaLocalRef jints, - std::vector int_vector, - const size_t len) { - jint value; - for (size_t i = 0; i < len; ++i) { - env->GetIntArrayRegion(jints.obj(), i, 1, &value); - ASSERT_EQ(int_vector[i], value); - } -} - -void CheckBoolArrayConversion(JNIEnv* env, - ScopedJavaLocalRef jbooleans, - std::vector bool_vector, - const size_t len) { - jboolean value; - for (size_t i = 0; i < len; ++i) { - env->GetBooleanArrayRegion(jbooleans.obj(), i, 1, &value); - ASSERT_EQ(bool_vector[i], value); - } -} - -void CheckFloatConversion( - JNIEnv* env, - const float* float_array, - const size_t len, - const ScopedJavaLocalRef& floats) { - ASSERT_TRUE(floats.obj()); - - jsize java_array_len = env->GetArrayLength(floats.obj()); - ASSERT_EQ(static_cast(len), java_array_len); - - jfloat value; - for (size_t i = 0; i < len; ++i) { - env->GetFloatArrayRegion(floats.obj(), i, 1, &value); - ASSERT_EQ(float_array[i], value); - } -} - -TEST(JniArray, FloatConversions) { - const float kFloats[] = { 0.0f, 1.0f, -10.0f}; - const size_t kLen = arraysize(kFloats); - - JNIEnv* env = AttachCurrentThread(); - CheckFloatConversion(env, kFloats, kLen, - ToJavaFloatArray(env, kFloats, kLen)); - - const std::vector vec(kFloats, kFloats + kLen); - CheckFloatConversion(env, kFloats, kLen, ToJavaFloatArray(env, vec)); -} - -TEST(JniArray, JavaBooleanArrayToBoolVector) { - const bool kBools[] = {false, true, false}; - const size_t kLen = arraysize(kBools); - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef jbooleans(env, env->NewBooleanArray(kLen)); - ASSERT_TRUE(jbooleans.obj()); - - for (size_t i = 0; i < kLen; ++i) { - jboolean j = static_cast(kBools[i]); - env->SetBooleanArrayRegion(jbooleans.obj(), i, 1, &j); - ASSERT_FALSE(HasException(env)); - } - - std::vector bools; - JavaBooleanArrayToBoolVector(env, jbooleans.obj(), &bools); - - ASSERT_EQ(static_cast(bools.size()), - env->GetArrayLength(jbooleans.obj())); - - CheckBoolArrayConversion(env, jbooleans, bools, kLen); -} - -TEST(JniArray, JavaIntArrayToIntVector) { - const int kInts[] = {0, 1, -1}; - const size_t kLen = arraysize(kInts); - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef jints(env, env->NewIntArray(kLen)); - ASSERT_TRUE(jints.obj()); - - for (size_t i = 0; i < kLen; ++i) { - jint j = static_cast(kInts[i]); - env->SetIntArrayRegion(jints.obj(), i, 1, &j); - ASSERT_FALSE(HasException(env)); - } - - std::vector ints; - JavaIntArrayToIntVector(env, jints.obj(), &ints); - - ASSERT_EQ(static_cast(ints.size()), env->GetArrayLength(jints.obj())); - - CheckIntArrayConversion(env, jints, ints, kLen); -} - -TEST(JniArray, JavaLongArrayToInt64Vector) { - const int64_t kInt64s[] = {0LL, 1LL, -1LL}; - const size_t kLen = arraysize(kInt64s); - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef jlongs(env, env->NewLongArray(kLen)); - ASSERT_TRUE(jlongs.obj()); - - for (size_t i = 0; i < kLen; ++i) { - jlong j = static_cast(kInt64s[i]); - env->SetLongArrayRegion(jlongs.obj(), i, 1, &j); - ASSERT_FALSE(HasException(env)); - } - - std::vector int64s; - JavaLongArrayToInt64Vector(env, jlongs.obj(), &int64s); - - ASSERT_EQ(static_cast(int64s.size()), - env->GetArrayLength(jlongs.obj())); - - jlong value; - for (size_t i = 0; i < kLen; ++i) { - env->GetLongArrayRegion(jlongs.obj(), i, 1, &value); - ASSERT_EQ(int64s[i], value); - ASSERT_EQ(kInt64s[i], int64s[i]); - } -} - -TEST(JniArray, JavaLongArrayToLongVector) { - const int64_t kInt64s[] = {0LL, 1LL, -1LL}; - const size_t kLen = arraysize(kInt64s); - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef jlongs(env, env->NewLongArray(kLen)); - ASSERT_TRUE(jlongs.obj()); - - for (size_t i = 0; i < kLen; ++i) { - jlong j = static_cast(kInt64s[i]); - env->SetLongArrayRegion(jlongs.obj(), i, 1, &j); - ASSERT_FALSE(HasException(env)); - } - - std::vector jlongs_vector; - JavaLongArrayToLongVector(env, jlongs.obj(), &jlongs_vector); - - ASSERT_EQ(static_cast(jlongs_vector.size()), - env->GetArrayLength(jlongs.obj())); - - jlong value; - for (size_t i = 0; i < kLen; ++i) { - env->GetLongArrayRegion(jlongs.obj(), i, 1, &value); - ASSERT_EQ(jlongs_vector[i], value); - } -} - -TEST(JniArray, JavaFloatArrayToFloatVector) { - const float kFloats[] = {0.0, 0.5, -0.5}; - const size_t kLen = arraysize(kFloats); - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef jfloats(env, env->NewFloatArray(kLen)); - ASSERT_TRUE(jfloats.obj()); - - for (size_t i = 0; i < kLen; ++i) { - jfloat j = static_cast(kFloats[i]); - env->SetFloatArrayRegion(jfloats.obj(), i, 1, &j); - ASSERT_FALSE(HasException(env)); - } - - std::vector floats; - JavaFloatArrayToFloatVector(env, jfloats.obj(), &floats); - - ASSERT_EQ(static_cast(floats.size()), - env->GetArrayLength(jfloats.obj())); - - jfloat value; - for (size_t i = 0; i < kLen; ++i) { - env->GetFloatArrayRegion(jfloats.obj(), i, 1, &value); - ASSERT_EQ(floats[i], value); - } -} - -TEST(JniArray, JavaArrayOfByteArrayToStringVector) { - const int kMaxItems = 50; - JNIEnv* env = AttachCurrentThread(); - - // Create a byte[][] object. - ScopedJavaLocalRef byte_array_clazz(env, env->FindClass("[B")); - ASSERT_TRUE(byte_array_clazz.obj()); - - ScopedJavaLocalRef array( - env, env->NewObjectArray(kMaxItems, byte_array_clazz.obj(), NULL)); - ASSERT_TRUE(array.obj()); - - // Create kMaxItems byte buffers. - char text[16]; - for (int i = 0; i < kMaxItems; ++i) { - snprintf(text, sizeof text, "%d", i); - ScopedJavaLocalRef byte_array = - ToJavaByteArray(env, reinterpret_cast(text), - static_cast(strlen(text))); - ASSERT_TRUE(byte_array.obj()); - - env->SetObjectArrayElement(array.obj(), i, byte_array.obj()); - ASSERT_FALSE(HasException(env)); - } - - // Convert to std::vector, check the content. - std::vector vec; - JavaArrayOfByteArrayToStringVector(env, array.obj(), &vec); - - EXPECT_EQ(static_cast(kMaxItems), vec.size()); - for (int i = 0; i < kMaxItems; ++i) { - snprintf(text, sizeof text, "%d", i); - EXPECT_STREQ(text, vec[i].c_str()); - } -} - -TEST(JniArray, JavaArrayOfIntArrayToIntVector) { - const size_t kNumItems = 4; - JNIEnv* env = AttachCurrentThread(); - - // Create an int[][] object. - ScopedJavaLocalRef int_array_clazz(env, env->FindClass("[I")); - ASSERT_TRUE(int_array_clazz.obj()); - - ScopedJavaLocalRef array( - env, env->NewObjectArray(kNumItems, int_array_clazz.obj(), nullptr)); - ASSERT_TRUE(array.obj()); - - // Populate int[][] object. - const int kInts0[] = {0, 1, -1, std::numeric_limits::min(), - std::numeric_limits::max()}; - const size_t kLen0 = arraysize(kInts0); - ScopedJavaLocalRef int_array0 = ToJavaIntArray(env, kInts0, kLen0); - env->SetObjectArrayElement(array.obj(), 0, int_array0.obj()); - - const int kInts1[] = {3, 4, 5}; - const size_t kLen1 = arraysize(kInts1); - ScopedJavaLocalRef int_array1 = ToJavaIntArray(env, kInts1, kLen1); - env->SetObjectArrayElement(array.obj(), 1, int_array1.obj()); - - const int kInts2[] = {}; - const size_t kLen2 = 0; - ScopedJavaLocalRef int_array2 = ToJavaIntArray(env, kInts2, kLen2); - env->SetObjectArrayElement(array.obj(), 2, int_array2.obj()); - - const int kInts3[] = {16}; - const size_t kLen3 = arraysize(kInts3); - ScopedJavaLocalRef int_array3 = ToJavaIntArray(env, kInts3, kLen3); - env->SetObjectArrayElement(array.obj(), 3, int_array3.obj()); - - // Convert to std::vector>, check the content. - std::vector> out; - JavaArrayOfIntArrayToIntVector(env, array.obj(), &out); - - EXPECT_EQ(kNumItems, out.size()); - CheckIntArrayConversion(env, int_array0, out[0], kLen0); - CheckIntArrayConversion(env, int_array1, out[1], kLen1); - CheckIntArrayConversion(env, int_array2, out[2], kLen2); - CheckIntArrayConversion(env, int_array3, out[3], kLen3); -} - -} // namespace android -} // namespace base diff --git a/android/jni_generator/AndroidManifest.xml b/android/jni_generator/AndroidManifest.xml deleted file mode 100644 index ec28ff5d3..000000000 --- a/android/jni_generator/AndroidManifest.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/android/jni_generator/BUILD.gn b/android/jni_generator/BUILD.gn deleted file mode 100644 index 915da9755..000000000 --- a/android/jni_generator/BUILD.gn +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//base/android/jni_generator/jni_exception_list.gni") -import("//build/config/android/rules.gni") -import("//testing/test.gni") - -generate_jni("jni_sample_header") { - sources = [ - "java/src/org/chromium/example/jni_generator/SampleForTests.java", - ] - jni_package = "example" -} - -android_library("jni_sample_java") { - java_files = - [ "java/src/org/chromium/example/jni_generator/SampleForTests.java" ] - deps = [ - "//base:base_java", - ] -} - -source_set("jni_sample_native_side") { - deps = [ - ":jni_sample_header", - "//base", - ] - sources = [ - "sample_for_tests.cc", - "sample_for_tests.h", - ] -} - -shared_library("jni_sample_lib") { - sources = [ - "sample_entry_point.cc", - ] - - deps = [ - ":jni_sample_native_side", - ":sample_jni_registration", - "//base", - ] -} - -android_apk("sample_jni_apk") { - apk_name = "SampleJni" - android_manifest = "AndroidManifest.xml" - deps = [ - ":jni_sample_java", - "//base:base_java", - ] - shared_libraries = [ ":jni_sample_lib" ] -} - -generate_jni_registration("sample_jni_registration") { - target = ":sample_jni_apk" - output = "$target_gen_dir/${target_name}.h" - exception_files = jni_exception_files -} - -# Serves to test that generated bindings compile properly. -group("jni_generator_tests") { - deps = [ - ":sample_jni_apk", - ] -} diff --git a/android/jni_generator/PRESUBMIT.py b/android/jni_generator/PRESUBMIT.py deleted file mode 100644 index bc76d5bb9..000000000 --- a/android/jni_generator/PRESUBMIT.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2018 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Presubmit script for android buildbot. - -See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for -details on the presubmit API built into depot_tools. -""" - - -def CommonChecks(input_api, output_api): - base_android_jni_generator_dir = input_api.PresubmitLocalPath() - - env = dict(input_api.environ) - env.update({ - 'PYTHONPATH': base_android_jni_generator_dir, - 'PYTHONDONTWRITEBYTECODE': '1', - }) - - return input_api.canned_checks.RunUnitTests( - input_api, - output_api, - unit_tests=[ - input_api.os_path.join( - base_android_jni_generator_dir, 'jni_generator_tests.py') - ], - env=env, - ) - - -def CheckChangeOnUpload(input_api, output_api): - return CommonChecks(input_api, output_api) - - -def CheckChangeOnCommit(input_api, output_api): - return CommonChecks(input_api, output_api) diff --git a/android/jni_generator/README.md b/android/jni_generator/README.md deleted file mode 100644 index 17ad150c3..000000000 --- a/android/jni_generator/README.md +++ /dev/null @@ -1,118 +0,0 @@ -# Overview -JNI (Java Native Interface) is the mechanism that enables Java code to call -native functions, and native code to call Java functions. - - * Native code calls into Java using apis from ``, which basically mirror - Java's reflection APIs. - * Java code calls native functions by declaring body-less functions with the - `native` keyword, and then calling them as normal Java functions. - -`jni_generator` generates boiler-plate code with the goal of making our code: - 1. easier to write, and - 2. typesafe. - -`jni_generator` uses regular expressions to parse .Java files, so don't do -anything too fancy. E.g.: - * Classes must be either explicitly imported, or are assumed to be in -the same package. To use `java.lang` classes, add an explicit import. - * Inner classes need to be referenced through the outer class. E.g.: - `void call(Outer.Inner inner)` - -The presense of any JNI within a class will result in ProGuard obfuscation for -the class to be disabled. - -### Exposing Native Methods - -**Without Crazy Linker:** - * Java->Native calls are exported from the shared library and lazily resolved - by the runtime (via `dlsym()`). - -**With Crazy Linker:** - * Java->Native calls are explicitly registered with JNI on the native side. - Explicit registration is necessary because crazy linker provides its own - `dlsym()`, but JNI is hardcoded to use the system's `dlsym()`. - * The logic to explicitly register stubs is generated by - `jni_registration_generator.py`. - * This script finds all native methods by scanning all source `.java` files - of an APK. Inefficient, but very convenient. - * Since `dlsym()` is not used in this case, we use a linker script to avoid - the cost of exporting symbols from the shared library (refer to - `//build/config/android:hide_all_but_jni_onload`). - * `jni_registration_generator.py` exposes two registrations methods: - * `RegisterNonMainDexNatives` - Registers native functions needed by multiple - process types (e.g. Rendereres, GPU process). - * `RegisterMainDexNatives` - Registers native functions needed only by the - browser process. - -### Exposing Java Methods - -Java methods just need to be annotated with `@CalledByNative`. The generated -functions can be put into a namespace using `@JNINamespace("your_namespace")`. - -## Usage - -Because the generator does not generate any source files, generated headers must -not be `#included` by multiple sources. If there are Java functions that need to -be called by multiple sources, one source should be chosen to expose the -functions to the others via additional wrapper functions. - -### Calling Java -> Native - - * Methods marked as `native` will have stubs generated for them that forward - calls to C++ function (that you must write). - * If the first parameter is a C++ object (e.g. `long mNativePointer`), then the - bindings will automatically generate the appropriate cast and call into C++ - code (JNI itself is only C). - -### Calling Native -> Java - - * Methods annotated with `@CalledByNative` will have stubs generated for them. - * Just call the generated stubs defined in generated `.h` files. - -### Java Objects and Garbage Collection - -All pointers to Java objects must be registered with JNI in order to prevent -garbage collection from invalidating them. - -For Strings & Arrays - it's common practice to use the `//base/android/jni_*` -helpers to convert them to `std::vectors` and `std::strings` as soon as -possible. - -For other objects - use smart pointers to store them: - * `ScopedJavaLocalRef<>` - When lifetime is the current function's scope. - * `ScopedJavaGlobalRef<>` - When lifetime is longer than the current function's - scope. - * `JavaObjectWeakGlobalRef<>` - Weak reference (do not prevent garbage - collection). - * `JavaParamRef<>` - Use to accept any of the above as a parameter to a - function without creating a redundant registration. - -### Additional Guidelines / Advice - -Minimize the surface API between the two sides. Rather than calling multiple -functions across boundaries, call only one (and then on the other side, call as -many little functions as required). - -If a Java object "owns" a native one, store the pointer via -`"long mNativeClassName"`. Ensure to eventually call a native method to delete -the object. For example, have a `close()` that deletes the native object. - -The best way to pass "compound" types across in either direction is to -create an inner class with PODs and a factory function. If possible, make mark -all the fields as "final". - -## Build Rules - - * `generate_jni` - Generates a header file with stubs for given `.java` files - * `generate_jar_jni` - Generates a header file with stubs for a given `.jar` - file - * `generate_jni_registration` - Generates a header file with functions to - register native-side JNI methods (required only when using crazy linker). - -Refer to [//build/config/android/rules.gni](https://cs.chromium.org/chromium/src/build/config/android/rules.gni) -for more about the GN templates. - -## Changing `jni_generator` - - * Python unit tests live in `jni_generator_tests.py` - * A working demo app exists as `//base/android/jni_generator:sample_jni_apk` diff --git a/android/jni_generator/SampleForTests_jni.golden b/android/jni_generator/SampleForTests_jni.golden deleted file mode 100644 index c3fe96851..000000000 --- a/android/jni_generator/SampleForTests_jni.golden +++ /dev/null @@ -1,494 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/example/jni_generator/SampleForTests - -#ifndef org_chromium_example_jni_generator_SampleForTests_JNI -#define org_chromium_example_jni_generator_SampleForTests_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char - kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA[]; -const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA[] = - "org/chromium/example/jni_generator/SampleForTests$InnerStructA"; - -JNI_REGISTRATION_EXPORT extern const char - kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass[]; -const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass[] = - "org/chromium/example/jni_generator/SampleForTests$InnerClass"; - -JNI_REGISTRATION_EXPORT extern const char - kClassPath_org_chromium_example_jni_1generator_SampleForTests[]; -const char kClassPath_org_chromium_example_jni_1generator_SampleForTests[] = - "org/chromium/example/jni_generator/SampleForTests"; - -JNI_REGISTRATION_EXPORT extern const char - kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB[]; -const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB[] = - "org/chromium/example/jni_generator/SampleForTests$InnerStructB"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz = 0; -#ifndef org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz_defined -#define org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz_defined -inline jclass org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(JNIEnv* - env) { - return base::android::LazyGetClass(env, - kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA, - &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz); -} -#endif -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_clazz = 0; -#ifndef org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_clazz_defined -#define org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_clazz_defined -inline jclass org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_clazz(JNIEnv* env) - { - return base::android::LazyGetClass(env, - kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass, - &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_clazz); -} -#endif -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_clazz = 0; -#ifndef org_chromium_example_jni_1generator_SampleForTests_clazz_defined -#define org_chromium_example_jni_1generator_SampleForTests_clazz_defined -inline jclass org_chromium_example_jni_1generator_SampleForTests_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, - kClassPath_org_chromium_example_jni_1generator_SampleForTests, - &g_org_chromium_example_jni_1generator_SampleForTests_clazz); -} -#endif -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz = 0; -#ifndef org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz_defined -#define org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz_defined -inline jclass org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(JNIEnv* - env) { - return base::android::LazyGetClass(env, - kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB, - &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -namespace base { -namespace android { - -static jlong JNI_SampleForTests_Init(JNIEnv* env, const base::android::JavaParamRef& - jcaller, - const base::android::JavaParamRef& param); - -JNI_GENERATOR_EXPORT jlong Java_org_chromium_example_jni_1generator_SampleForTests_nativeInit( - JNIEnv* env, - jobject jcaller, - jstring param) { - return JNI_SampleForTests_Init(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, param)); -} - -JNI_GENERATOR_EXPORT void Java_org_chromium_example_jni_1generator_SampleForTests_nativeDestroy( - JNIEnv* env, - jobject jcaller, - jlong nativeCPPClass) { - CPPClass* native = reinterpret_cast(nativeCPPClass); - CHECK_NATIVE_PTR(env, jcaller, native, "Destroy"); - return native->Destroy(env, base::android::JavaParamRef(env, jcaller)); -} - -static jdouble JNI_SampleForTests_GetDoubleFunction(JNIEnv* env, const - base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jdouble - Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetDoubleFunction( - JNIEnv* env, - jobject jcaller) { - return JNI_SampleForTests_GetDoubleFunction(env, base::android::JavaParamRef(env, - jcaller)); -} - -static jfloat JNI_SampleForTests_GetFloatFunction(JNIEnv* env, const - base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jfloat - Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetFloatFunction( - JNIEnv* env, - jclass jcaller) { - return JNI_SampleForTests_GetFloatFunction(env, base::android::JavaParamRef(env, - jcaller)); -} - -static void JNI_SampleForTests_SetNonPODDatatype(JNIEnv* env, const - base::android::JavaParamRef& jcaller, - const base::android::JavaParamRef& rect); - -JNI_GENERATOR_EXPORT void - Java_org_chromium_example_jni_1generator_SampleForTests_nativeSetNonPODDatatype( - JNIEnv* env, - jobject jcaller, - jobject rect) { - return JNI_SampleForTests_SetNonPODDatatype(env, base::android::JavaParamRef(env, - jcaller), base::android::JavaParamRef(env, rect)); -} - -static base::android::ScopedJavaLocalRef JNI_SampleForTests_GetNonPODDatatype(JNIEnv* env, - const base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jobject - Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetNonPODDatatype( - JNIEnv* env, - jobject jcaller) { - return JNI_SampleForTests_GetNonPODDatatype(env, base::android::JavaParamRef(env, - jcaller)).Release(); -} - -JNI_GENERATOR_EXPORT jint Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod( - JNIEnv* env, - jobject jcaller, - jlong nativeCPPClass) { - CPPClass* native = reinterpret_cast(nativeCPPClass); - CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); - return native->Method(env, base::android::JavaParamRef(env, jcaller)); -} - -JNI_GENERATOR_EXPORT jdouble - Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethodOtherP0( - JNIEnv* env, - jobject jcaller, - jlong nativePtr) { - CPPClass::InnerClass* native = reinterpret_cast(nativePtr); - CHECK_NATIVE_PTR(env, jcaller, native, "MethodOtherP0", 0); - return native->MethodOtherP0(env, base::android::JavaParamRef(env, jcaller)); -} - -JNI_GENERATOR_EXPORT void Java_org_chromium_example_jni_1generator_SampleForTests_nativeAddStructB( - JNIEnv* env, - jobject jcaller, - jlong nativeCPPClass, - jobject b) { - CPPClass* native = reinterpret_cast(nativeCPPClass); - CHECK_NATIVE_PTR(env, jcaller, native, "AddStructB"); - return native->AddStructB(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, b)); -} - -JNI_GENERATOR_EXPORT void - Java_org_chromium_example_jni_1generator_SampleForTests_nativeIterateAndDoSomethingWithStructB( - JNIEnv* env, - jobject jcaller, - jlong nativeCPPClass) { - CPPClass* native = reinterpret_cast(nativeCPPClass); - CHECK_NATIVE_PTR(env, jcaller, native, "IterateAndDoSomethingWithStructB"); - return native->IterateAndDoSomethingWithStructB(env, base::android::JavaParamRef(env, - jcaller)); -} - -JNI_GENERATOR_EXPORT jstring - Java_org_chromium_example_jni_1generator_SampleForTests_nativeReturnAString( - JNIEnv* env, - jobject jcaller, - jlong nativeCPPClass) { - CPPClass* native = reinterpret_cast(nativeCPPClass); - CHECK_NATIVE_PTR(env, jcaller, native, "ReturnAString", NULL); - return native->ReturnAString(env, base::android::JavaParamRef(env, jcaller)).Release(); -} - -static jint JNI_InnerClass_GetInnerIntFunction(JNIEnv* env, const - base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jint - Java_org_chromium_example_jni_1generator_SampleForTests_00024InnerClass_nativeGetInnerIntFunction( - JNIEnv* env, - jclass jcaller) { - return JNI_InnerClass_GetInnerIntFunction(env, base::android::JavaParamRef(env, jcaller)); -} - - -static base::subtle::AtomicWord g_org_chromium_example_jni_1generator_SampleForTests_javaMethod = 0; -static jint Java_SampleForTests_javaMethod(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper foo, - JniIntWrapper bar) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "javaMethod", - "(II)I", - &g_org_chromium_example_jni_1generator_SampleForTests_javaMethod); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id, as_jint(foo), as_jint(bar)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_staticJavaMethod = 0; -static jboolean Java_SampleForTests_staticJavaMethod(JNIEnv* env) { - CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - org_chromium_example_jni_1generator_SampleForTests_clazz(env), false); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "staticJavaMethod", - "()Z", - &g_org_chromium_example_jni_1generator_SampleForTests_staticJavaMethod); - - jboolean ret = - env->CallStaticBooleanMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_packagePrivateJavaMethod = 0; -static void Java_SampleForTests_packagePrivateJavaMethod(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "packagePrivateJavaMethod", - "()V", - &g_org_chromium_example_jni_1generator_SampleForTests_packagePrivateJavaMethod); - - env->CallVoidMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_methodWithGenericParams = 0; -static void Java_SampleForTests_methodWithGenericParams(JNIEnv* env, const - base::android::JavaRef& obj, const base::android::JavaRef& foo, - const base::android::JavaRef& bar) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "methodWithGenericParams", - "(Ljava/util/Map;Ljava/util/LinkedList;)V", - &g_org_chromium_example_jni_1generator_SampleForTests_methodWithGenericParams); - - env->CallVoidMethod(obj.obj(), - method_id, foo.obj(), bar.obj()); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_org_chromium_example_jni_1generator_SampleForTests_Constructor = - 0; -static base::android::ScopedJavaLocalRef Java_SampleForTests_Constructor(JNIEnv* env, - JniIntWrapper foo, - JniIntWrapper bar) { - CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "", - "(II)V", - &g_org_chromium_example_jni_1generator_SampleForTests_Constructor); - - jobject ret = - env->NewObject(org_chromium_example_jni_1generator_SampleForTests_clazz(env), - method_id, as_jint(foo), as_jint(bar)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_methodThatThrowsException = 0; -static void Java_SampleForTests_methodThatThrowsException(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "methodThatThrowsException", - "()V", - &g_org_chromium_example_jni_1generator_SampleForTests_methodThatThrowsException); - - env->CallVoidMethod(obj.obj(), - method_id); -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_javaMethodWithAnnotatedParam = 0; -static void Java_SampleForTests_javaMethodWithAnnotatedParam(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper foo) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "javaMethodWithAnnotatedParam", - "(I)V", - &g_org_chromium_example_jni_1generator_SampleForTests_javaMethodWithAnnotatedParam); - - env->CallVoidMethod(obj.obj(), - method_id, as_jint(foo)); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_create = 0; -static base::android::ScopedJavaLocalRef Java_InnerStructA_create(JNIEnv* env, jlong l, - JniIntWrapper i, - const base::android::JavaRef& s) { - CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(env), - org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(env), - "create", - "(JILjava/lang/String;)Lorg/chromium/example/jni_generator/SampleForTests$InnerStructA;", - &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_create); - - jobject ret = -env->CallStaticObjectMethod(org_chromium_example_jni_1generator_SampleForTests_00024InnerStructA_clazz(env), - method_id, l, as_jint(i), s.obj()); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_example_jni_1generator_SampleForTests_addStructA = 0; -static void Java_SampleForTests_addStructA(JNIEnv* env, const base::android::JavaRef& obj, - const base::android::JavaRef& a) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "addStructA", - "(Lorg/chromium/example/jni_generator/SampleForTests$InnerStructA;)V", - &g_org_chromium_example_jni_1generator_SampleForTests_addStructA); - - env->CallVoidMethod(obj.obj(), - method_id, a.obj()); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_iterateAndDoSomething = 0; -static void Java_SampleForTests_iterateAndDoSomething(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "iterateAndDoSomething", - "()V", - &g_org_chromium_example_jni_1generator_SampleForTests_iterateAndDoSomething); - - env->CallVoidMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getKey = 0; -static jlong Java_InnerStructB_getKey(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env), - "getKey", - "()J", - &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getKey); - - jlong ret = - env->CallLongMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getValue = 0; -static base::android::ScopedJavaLocalRef Java_InnerStructB_getValue(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_clazz(env), - "getValue", - "()Ljava/lang/String;", - &g_org_chromium_example_jni_1generator_SampleForTests_00024InnerStructB_getValue); - - jstring ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_getInnerInterface = 0; -static base::android::ScopedJavaLocalRef Java_SampleForTests_getInnerInterface(JNIEnv* env) - { - CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "getInnerInterface", - "()Lorg/chromium/example/jni_generator/SampleForTests$InnerInterface;", - &g_org_chromium_example_jni_1generator_SampleForTests_getInnerInterface); - - jobject ret = - env->CallStaticObjectMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env), - method_id); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_example_jni_1generator_SampleForTests_getInnerEnum = - 0; -static base::android::ScopedJavaLocalRef Java_SampleForTests_getInnerEnum(JNIEnv* env) { - CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "getInnerEnum", - "()Lorg/chromium/example/jni_generator/SampleForTests$InnerEnum;", - &g_org_chromium_example_jni_1generator_SampleForTests_getInnerEnum); - - jobject ret = - env->CallStaticObjectMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env), - method_id); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -} // namespace android -} // namespace base - -#endif // org_chromium_example_jni_generator_SampleForTests_JNI diff --git a/android/jni_generator/android_jar.classes b/android/jni_generator/android_jar.classes deleted file mode 100644 index 7d412cee4..000000000 --- a/android/jni_generator/android_jar.classes +++ /dev/null @@ -1,98 +0,0 @@ -java/lang/AbstractMethodError.class -java/lang/AbstractStringBuilder.class -java/lang/Appendable.class -java/lang/ArithmeticException.class -java/lang/ArrayIndexOutOfBoundsException.class -java/lang/ArrayStoreException.class -java/lang/AssertionError.class -java/lang/AutoCloseable.class -java/lang/Boolean.class -java/lang/Byte.class -java/lang/Character.class -java/lang/Character$Subset.class -java/lang/Character$UnicodeBlock.class -java/lang/CharSequence.class -java/lang/ClassCastException.class -java/lang/ClassCircularityError.class -java/lang/Class.class -java/lang/ClassFormatError.class -java/lang/ClassLoader.class -java/lang/ClassNotFoundException.class -java/lang/Cloneable.class -java/lang/CloneNotSupportedException.class -java/lang/Comparable.class -java/lang/Compiler.class -java/lang/Deprecated.class -java/lang/Double.class -java/lang/Enum.class -java/lang/EnumConstantNotPresentException.class -java/lang/Error.class -java/lang/Exception.class -java/lang/ExceptionInInitializerError.class -java/lang/Float.class -java/lang/IllegalAccessError.class -java/lang/IllegalAccessException.class -java/lang/IllegalArgumentException.class -java/lang/IllegalMonitorStateException.class -java/lang/IllegalStateException.class -java/lang/IncompatibleClassChangeError.class -java/lang/IndexOutOfBoundsException.class -java/lang/InheritableThreadLocal.class -java/lang/InstantiationError.class -java/lang/InstantiationException.class -java/lang/Integer.class -java/lang/InternalError.class -java/lang/InterruptedException.class -java/lang/Iterable.class -java/lang/LinkageError.class -java/lang/Long.class -java/lang/Math.class -java/lang/NegativeArraySizeException.class -java/lang/NoClassDefFoundError.class -java/lang/NoSuchFieldError.class -java/lang/NoSuchFieldException.class -java/lang/NoSuchMethodError.class -java/lang/NoSuchMethodException.class -java/lang/NullPointerException.class -java/lang/Number.class -java/lang/NumberFormatException.class -java/lang/Object.class -java/lang/OutOfMemoryError.class -java/lang/Override.class -java/lang/Package.class -java/lang/ProcessBuilder.class -java/lang/Process.class -java/lang/Readable.class -java/lang/ReflectiveOperationException.class -java/lang/Runnable.class -java/lang/Runtime.class -java/lang/RuntimeException.class -java/lang/RuntimePermission.class -java/lang/SafeVarargs.class -java/lang/SecurityException.class -java/lang/SecurityManager.class -java/lang/Short.class -java/lang/StackOverflowError.class -java/lang/StackTraceElement.class -java/lang/StrictMath.class -java/lang/StringBuffer.class -java/lang/StringBuilder.class -java/lang/String.class -java/lang/StringIndexOutOfBoundsException.class -java/lang/SuppressWarnings.class -java/lang/System.class -java/lang/Thread.class -java/lang/ThreadDeath.class -java/lang/ThreadGroup.class -java/lang/ThreadLocal.class -java/lang/Thread$State.class -java/lang/Thread$UncaughtExceptionHandler.class -java/lang/Throwable.class -java/lang/TypeNotPresentException.class -java/lang/UnknownError.class -java/lang/UnsatisfiedLinkError.class -java/lang/UnsupportedClassVersionError.class -java/lang/UnsupportedOperationException.class -java/lang/VerifyError.class -java/lang/VirtualMachineError.class -java/lang/Void.class diff --git a/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java b/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java deleted file mode 100644 index ba3abe7e5..000000000 --- a/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 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. - -package org.chromium.example.jni_generator; - -import android.graphics.Rect; - -import org.chromium.base.annotations.AccessedByNative; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.CalledByNativeUnchecked; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeCall; -import org.chromium.base.annotations.NativeClassQualifiedName; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -// This class serves as a reference test for the bindings generator, and as example documentation -// for how to use the jni generator. -// The C++ counter-part is sample_for_tests.cc. -// jni_generator/BUILD.gn has a jni_generator_tests target that will: -// * Generate a header file for the JNI bindings based on this file. -// * Generate a header file containing registration methods required to use C++ methods from this -// file. -// * Compile sample_for_tests.cc using the generated header file. -// * link a native executable to prove the generated header + cc file are self-contained. -// All comments are informational only, and are ignored by the jni generator. -// -// This JNINamespace annotation indicates that all native methods should be -// generated inside this namespace, including the native class that this -// object binds to. -@JNINamespace("base::android") -class SampleForTests { - // Classes can store their C++ pointer counterpart as an int that is normally initialized by - // calling out a nativeInit() function. Replace "CPPClass" with your particular class name! - long mNativeCPPObject; - - // You can define methods and attributes on the java class just like any other. - // Methods without the @CalledByNative annotation won't be exposed to JNI. - public SampleForTests() { - } - - public void startExample() { - // Calls C++ Init(...) method and holds a pointer to the C++ class. - mNativeCPPObject = nativeInit("myParam"); - } - - public void doStuff() { - // This will call CPPClass::Method() using nativePtr as a pointer to the object. This must - // be done to: - // * avoid leaks. - // * using finalizers are not allowed to destroy the cpp class. - nativeMethod(mNativeCPPObject); - } - - public void finishExample() { - // We're done, so let's destroy nativePtr object. - nativeDestroy(mNativeCPPObject); - } - - // --------------------------------------------------------------------------------------------- - // The following methods demonstrate exporting Java methods for invocation from C++ code. - // Java functions are mapping into C global functions by prefixing the method name with - // "Java__" - // This is triggered by the @CalledByNative annotation; the methods may be named as you wish. - - // Exported to C++ as: - // Java_SampleForTests_javaMethod(JNIEnv* env, jobject caller, jint foo, jint bar) - // Typically the C++ code would have obtained the jobject via the Init() call described above. - @CalledByNative - public int javaMethod(int foo, int bar) { - return 0; - } - - // Exported to C++ as Java_SampleForTests_staticJavaMethod(JNIEnv* env) - // Note no jobject argument, as it is static. - @CalledByNative - public static boolean staticJavaMethod() { - return true; - } - - // No prefix, so this method is package private. It will still be exported. - @CalledByNative - void packagePrivateJavaMethod() { - } - - // Method signature with generics in params. - @CalledByNative - public void methodWithGenericParams( - Map> foo, LinkedList bar) {} - - // Constructors will be exported to C++ as: - // Java_SampleForTests_Constructor(JNIEnv* env, jint foo, jint bar) - @CalledByNative - public SampleForTests(int foo, int bar) {} - - // Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that - // call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to - // call ClearException() and act as appropriate. - // See more details at the "@CalledByNativeUnchecked" annotation. - @CalledByNativeUnchecked - void methodThatThrowsException() throws Exception {} - - // The generator is not confused by inline comments: - // @CalledByNative void thisShouldNotAppearInTheOutput(); - // @CalledByNativeUnchecked public static void neitherShouldThis(int foo); - - /** - * The generator is not confused by block comments: - * @CalledByNative void thisShouldNotAppearInTheOutputEither(); - * @CalledByNativeUnchecked public static void andDefinitelyNotThis(int foo); - */ - - // String constants that look like comments don't confuse the generator: - private String mArrgh = "*/*"; - - private @interface SomeAnnotation {} - - // The generator is not confused by @Annotated parameters. - @CalledByNative - void javaMethodWithAnnotatedParam(@SomeAnnotation int foo) { - } - - // --------------------------------------------------------------------------------------------- - // Java fields which are accessed from C++ code only must be annotated with @AccessedByNative to - // prevent them being eliminated when unreferenced code is stripped. - @AccessedByNative - private int mJavaField; - - // --------------------------------------------------------------------------------------------- - // The following methods demonstrate declaring methods to call into C++ from Java. - // The generator detects the "native" and "static" keywords, the type and name of the first - // parameter, and the "native" prefix to the function name to determine the C++ function - // signatures. Besides these constraints the methods can be freely named. - - // This declares a C++ function which the application code must implement: - // static jint Init(JNIEnv* env, jobject caller); - // The jobject parameter refers back to this java side object instance. - // The implementation must return the pointer to the C++ object cast to jint. - // The caller of this method should store it, and supply it as a the nativeCPPClass param to - // subsequent native method calls (see the methods below that take an "int native..." as first - // param). - private native long nativeInit(String param); - - // This defines a function binding to the associated C++ class member function. The name is - // derived from |nativeDestroy| and |nativeCPPClass| to arrive at CPPClass::Destroy() (i.e. - // native prefixes stripped). - // - // The |nativeCPPClass| is automatically cast to type CPPClass*, in order to obtain the object - // on - // which to invoke the member function. Replace "CPPClass" with your particular class name! - private native void nativeDestroy(long nativeCPPClass); - - // This declares a C++ function which the application code must implement: - // static jdouble GetDoubleFunction(JNIEnv* env, jobject caller); - // The jobject parameter refers back to this java side object instance. - private native double nativeGetDoubleFunction(); - - // Similar to nativeGetDoubleFunction(), but here the C++ side will receive a jclass rather than - // jobject param, as the function is declared static. - private static native float nativeGetFloatFunction(); - - // This function takes a non-POD datatype. We have a list mapping them to their full classpath - // in jni_generator.py JavaParamToJni. If you require a new datatype, make sure you add to that - // function. - private native void nativeSetNonPODDatatype(Rect rect); - - // This declares a C++ function which the application code must implement: - // static ScopedJavaLocalRef GetNonPODDatatype(JNIEnv* env, jobject caller); - // The jobject parameter refers back to this java side object instance. - // Note that it returns a ScopedJavaLocalRef so that you don' have to worry about - // deleting the JNI local reference. This is similar with Strings and arrays. - private native Object nativeGetNonPODDatatype(); - - // Similar to nativeDestroy above, this will cast nativeCPPClass into pointer of CPPClass type - // and call its Method member function. Replace "CPPClass" with your particular class name! - private native int nativeMethod(long nativeCPPClass); - - // Similar to nativeMethod above, but here the C++ fully qualified class name is taken from the - // annotation rather than parameter name, which can thus be chosen freely. - @NativeClassQualifiedName("CPPClass::InnerClass") - private native double nativeMethodOtherP0(long nativePtr); - - // This "struct" will be created by the native side using |createInnerStructA|, - // and used by the java-side somehow. - // Note that |@CalledByNative| has to contain the inner class name. - static class InnerStructA { - private final long mLong; - private final int mInt; - private final String mString; - - private InnerStructA(long l, int i, String s) { - mLong = l; - mInt = i; - mString = s; - } - - @CalledByNative("InnerStructA") - private static InnerStructA create(long l, int i, String s) { - return new InnerStructA(l, i, s); - } - } - - private List mListInnerStructA = new ArrayList(); - - @CalledByNative - private void addStructA(InnerStructA a) { - // Called by the native side to append another element. - mListInnerStructA.add(a); - } - - @CalledByNative - private void iterateAndDoSomething() { - Iterator it = mListInnerStructA.iterator(); - while (it.hasNext()) { - InnerStructA element = it.next(); - // Now, do something with element. - } - // Done, clear the list. - mListInnerStructA.clear(); - } - - // This "struct" will be created by the java side passed to native, which - // will use its getters. - // Note that |@CalledByNative| has to contain the inner class name. - static class InnerStructB { - private final long mKey; - private final String mValue; - - private InnerStructB(long k, String v) { - mKey = k; - mValue = v; - } - - @CalledByNative("InnerStructB") - private long getKey() { - return mKey; - } - - @CalledByNative("InnerStructB") - private String getValue() { - return mValue; - } - } - - List mListInnerStructB = new ArrayList(); - - void iterateAndDoSomethingWithMap() { - Iterator it = mListInnerStructB.iterator(); - while (it.hasNext()) { - InnerStructB element = it.next(); - // Now, do something with element. - nativeAddStructB(mNativeCPPObject, element); - } - nativeIterateAndDoSomethingWithStructB(mNativeCPPObject); - } - - native void nativeAddStructB(long nativeCPPClass, InnerStructB b); - native void nativeIterateAndDoSomethingWithStructB(long nativeCPPClass); - native String nativeReturnAString(long nativeCPPClass); - - // This inner class shows how to annotate native methods on inner classes. - static class InnerClass { - @NativeCall("InnerClass") - private static native int nativeGetInnerIntFunction(); - } - - interface InnerInterface {} - enum InnerEnum {} - - @CalledByNative - static InnerInterface getInnerInterface() { - return null; - } - - @CalledByNative - static InnerEnum getInnerEnum() { - return null; - } -} diff --git a/android/jni_generator/jni_exception_list.gni b/android/jni_generator/jni_exception_list.gni deleted file mode 100644 index 31d027cab..000000000 --- a/android/jni_generator/jni_exception_list.gni +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2017 The Chromium 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("//device/vr/buildflags/buildflags.gni") - -jni_exception_files = - [ "//base/android/java/src/org/chromium/base/library_loader/Linker.java" ] - -# Exclude it from JNI registration if VR is not enabled. -if (!enable_vr) { - jni_exception_files += [ "//chrome/android/java/src/org/chromium/chrome/browser/vr/VrShellDelegate.java" ] -} diff --git a/android/jni_generator/jni_generator.py b/android/jni_generator/jni_generator.py deleted file mode 100755 index ef676e26e..000000000 --- a/android/jni_generator/jni_generator.py +++ /dev/null @@ -1,1409 +0,0 @@ -#!/usr/bin/env python -# 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. - -"""Extracts native methods from a Java file and generates the JNI bindings. -If you change this, please run and update the tests.""" - -import collections -import errno -import optparse -import os -import re -from string import Template -import subprocess -import sys -import textwrap -import zipfile - -CHROMIUM_SRC = os.path.join( - os.path.dirname(__file__), os.pardir, os.pardir, os.pardir) -BUILD_ANDROID_GYP = os.path.join( - CHROMIUM_SRC, 'build', 'android', 'gyp') - -sys.path.append(BUILD_ANDROID_GYP) - -from util import build_utils - - -# Match single line comments, multiline comments, character literals, and -# double-quoted strings. -_COMMENT_REMOVER_REGEX = re.compile( - r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', - re.DOTALL | re.MULTILINE) - -_EXTRACT_NATIVES_REGEX = re.compile( - r'(@NativeClassQualifiedName' - r'\(\"(?P.*?)\"\)\s+)?' - r'(@NativeCall(\(\"(?P.*?)\"\))\s+)?' - r'(?P\w+\s\w+|\w+|\s+)\s*native ' - r'(?P\S*) ' - r'(?Pnative\w+)\((?P.*?)\);') - -_MAIN_DEX_REGEX = re.compile( - r'^\s*(?:@(?:\w+\.)*\w+\s+)*@MainDex\b', - re.MULTILINE) - -# Use 100 columns rather than 80 because it makes many lines more readable. -_WRAP_LINE_LENGTH = 100 -# WrapOutput() is fairly slow. Pre-creating TextWrappers helps a bit. -_WRAPPERS_BY_INDENT = [ - textwrap.TextWrapper(width=_WRAP_LINE_LENGTH, expand_tabs=False, - replace_whitespace=False, - subsequent_indent=' ' * (indent + 4), - break_long_words=False) - for indent in xrange(50)] # 50 chosen experimentally. - - -class ParseError(Exception): - """Exception thrown when we can't parse the input file.""" - - def __init__(self, description, *context_lines): - Exception.__init__(self) - self.description = description - self.context_lines = context_lines - - def __str__(self): - context = '\n'.join(self.context_lines) - return '***\nERROR: %s\n\n%s\n***' % (self.description, context) - - -class Param(object): - """Describes a param for a method, either java or native.""" - - def __init__(self, **kwargs): - self.datatype = kwargs['datatype'] - self.name = kwargs['name'] - - -class NativeMethod(object): - """Describes a C/C++ method that is called by Java code""" - - def __init__(self, **kwargs): - self.static = kwargs['static'] - self.java_class_name = kwargs['java_class_name'] - self.return_type = kwargs['return_type'] - self.name = kwargs['name'] - self.params = kwargs['params'] - if self.params: - assert type(self.params) is list - assert type(self.params[0]) is Param - if (self.params and - self.params[0].datatype == kwargs.get('ptr_type', 'int') and - self.params[0].name.startswith('native')): - self.type = 'method' - self.p0_type = self.params[0].name[len('native'):] - if kwargs.get('native_class_name'): - self.p0_type = kwargs['native_class_name'] - else: - self.type = 'function' - self.method_id_var_name = kwargs.get('method_id_var_name', None) - - -class CalledByNative(object): - """Describes a java method exported to c/c++""" - - def __init__(self, **kwargs): - self.system_class = kwargs['system_class'] - self.unchecked = kwargs['unchecked'] - self.static = kwargs['static'] - self.java_class_name = kwargs['java_class_name'] - self.return_type = kwargs['return_type'] - self.name = kwargs['name'] - self.params = kwargs['params'] - self.method_id_var_name = kwargs.get('method_id_var_name', None) - self.signature = kwargs.get('signature') - self.is_constructor = kwargs.get('is_constructor', False) - self.env_call = GetEnvCall(self.is_constructor, self.static, - self.return_type) - self.static_cast = GetStaticCastForReturnType(self.return_type) - - -class ConstantField(object): - def __init__(self, **kwargs): - self.name = kwargs['name'] - self.value = kwargs['value'] - - -def JavaDataTypeToC(java_type): - """Returns a C datatype for the given java type.""" - java_pod_type_map = { - 'int': 'jint', - 'byte': 'jbyte', - 'char': 'jchar', - 'short': 'jshort', - 'boolean': 'jboolean', - 'long': 'jlong', - 'double': 'jdouble', - 'float': 'jfloat', - } - java_type_map = { - 'void': 'void', - 'String': 'jstring', - 'Class': 'jclass', - 'Throwable': 'jthrowable', - 'java/lang/String': 'jstring', - 'java/lang/Class': 'jclass', - 'java/lang/Throwable': 'jthrowable', - } - - java_type = _StripGenerics(java_type) - if java_type in java_pod_type_map: - return java_pod_type_map[java_type] - elif java_type in java_type_map: - return java_type_map[java_type] - elif java_type.endswith('[]'): - if java_type[:-2] in java_pod_type_map: - return java_pod_type_map[java_type[:-2]] + 'Array' - return 'jobjectArray' - else: - return 'jobject' - - -def WrapCTypeForDeclaration(c_type): - """Wrap the C datatype in a JavaRef if required.""" - if re.match(RE_SCOPED_JNI_TYPES, c_type): - return 'const base::android::JavaParamRef<' + c_type + '>&' - else: - return c_type - - -def _JavaDataTypeToCForDeclaration(java_type): - """Returns a JavaRef-wrapped C datatype for the given java type.""" - return WrapCTypeForDeclaration(JavaDataTypeToC(java_type)) - - -def JavaDataTypeToCForCalledByNativeParam(java_type): - """Returns a C datatype to be when calling from native.""" - if java_type == 'int': - return 'JniIntWrapper' - else: - c_type = JavaDataTypeToC(java_type) - if re.match(RE_SCOPED_JNI_TYPES, c_type): - return 'const base::android::JavaRef<' + c_type + '>&' - else: - return c_type - - -def JavaReturnValueToC(java_type): - """Returns a valid C return value for the given java type.""" - java_pod_type_map = { - 'int': '0', - 'byte': '0', - 'char': '0', - 'short': '0', - 'boolean': 'false', - 'long': '0', - 'double': '0', - 'float': '0', - 'void': '' - } - return java_pod_type_map.get(java_type, 'NULL') - - -def _GetJNIFirstParamType(native): - if native.type == 'function' and native.static: - return 'jclass' - return 'jobject' - - -def _GetJNIFirstParam(native, for_declaration): - c_type = _GetJNIFirstParamType(native) - if for_declaration: - c_type = WrapCTypeForDeclaration(c_type) - return [c_type + ' jcaller'] - - -def _GetParamsInDeclaration(native): - """Returns the params for the forward declaration. - - Args: - native: the native dictionary describing the method. - - Returns: - A string containing the params. - """ - return ',\n '.join(_GetJNIFirstParam(native, True) + - [_JavaDataTypeToCForDeclaration(param.datatype) + ' ' + - param.name - for param in native.params]) - - -def GetParamsInStub(native): - """Returns the params for the stub declaration. - - Args: - native: the native dictionary describing the method. - - Returns: - A string containing the params. - """ - params = [JavaDataTypeToC(p.datatype) + ' ' + p.name for p in native.params] - return ',\n '.join(_GetJNIFirstParam(native, False) + params) - - -def _StripGenerics(value): - """Strips Java generics from a string.""" - nest_level = 0 # How deeply we are nested inside the generics. - start_index = 0 # Starting index of the last non-generic region. - out = [] - - for i, c in enumerate(value): - if c == '<': - if nest_level == 0: - out.append(value[start_index:i]) - nest_level += 1 - elif c == '>': - start_index = i + 1 - nest_level -= 1 - out.append(value[start_index:]) - - return ''.join(out) - - -class JniParams(object): - """Get JNI related parameters.""" - - def __init__(self, fully_qualified_class): - self._fully_qualified_class = 'L' + fully_qualified_class - self._package = '/'.join(fully_qualified_class.split('/')[:-1]) - self._imports = [] - self._inner_classes = [] - self._implicit_imports = [] - - def ExtractImportsAndInnerClasses(self, contents): - contents = contents.replace('\n', '') - re_import = re.compile(r'import.*?(?P\S*?);') - for match in re.finditer(re_import, contents): - self._imports += ['L' + match.group('class').replace('.', '/')] - - re_inner = re.compile(r'(class|interface|enum)\s+?(?P\w+?)\W') - for match in re.finditer(re_inner, contents): - inner = match.group('name') - if not self._fully_qualified_class.endswith(inner): - self._inner_classes += [self._fully_qualified_class + '$' + - inner] - - re_additional_imports = re.compile( - r'@JNIAdditionalImport\(\s*{?(?P.*?)}?\s*\)') - for match in re.finditer(re_additional_imports, contents): - for class_name in match.group('class_names').split(','): - self._AddAdditionalImport(class_name.strip()) - - def JavaToJni(self, param): - """Converts a java param into a JNI signature type.""" - pod_param_map = { - 'int': 'I', - 'boolean': 'Z', - 'char': 'C', - 'short': 'S', - 'long': 'J', - 'double': 'D', - 'float': 'F', - 'byte': 'B', - 'void': 'V', - } - object_param_list = [ - 'Ljava/lang/Boolean', - 'Ljava/lang/Integer', - 'Ljava/lang/Long', - 'Ljava/lang/Object', - 'Ljava/lang/String', - 'Ljava/lang/Class', - 'Ljava/lang/CharSequence', - 'Ljava/lang/Runnable', - 'Ljava/lang/Throwable', - ] - - prefix = '' - # Array? - while param[-2:] == '[]': - prefix += '[' - param = param[:-2] - # Generic? - if '<' in param: - param = param[:param.index('<')] - if param in pod_param_map: - return prefix + pod_param_map[param] - if '/' in param: - # Coming from javap, use the fully qualified param directly. - return prefix + 'L' + param + ';' - - for qualified_name in (object_param_list + - [self._fully_qualified_class] + self._inner_classes): - if (qualified_name.endswith('/' + param) or - qualified_name.endswith('$' + param.replace('.', '$')) or - qualified_name == 'L' + param): - return prefix + qualified_name + ';' - - # Is it from an import? (e.g. referecing Class from import pkg.Class; - # note that referencing an inner class Inner from import pkg.Class.Inner - # is not supported). - for qualified_name in self._imports: - if qualified_name.endswith('/' + param): - # Ensure it's not an inner class. - components = qualified_name.split('/') - if len(components) > 2 and components[-2][0].isupper(): - raise SyntaxError('Inner class (%s) can not be imported ' - 'and used by JNI (%s). Please import the outer ' - 'class and use Outer.Inner instead.' % - (qualified_name, param)) - return prefix + qualified_name + ';' - - # Is it an inner class from an outer class import? (e.g. referencing - # Class.Inner from import pkg.Class). - if '.' in param: - components = param.split('.') - outer = '/'.join(components[:-1]) - inner = components[-1] - for qualified_name in self._imports: - if qualified_name.endswith('/' + outer): - return (prefix + qualified_name + '$' + inner + ';') - raise SyntaxError('Inner class (%s) can not be ' - 'used directly by JNI. Please import the outer ' - 'class, probably:\n' - 'import %s.%s;' % - (param, self._package.replace('/', '.'), - outer.replace('/', '.'))) - - self._CheckImplicitImports(param) - - # Type not found, falling back to same package as this class. - return (prefix + 'L' + self._package + '/' + param + ';') - - def _AddAdditionalImport(self, class_name): - assert class_name.endswith('.class') - raw_class_name = class_name[:-len('.class')] - if '.' in raw_class_name: - raise SyntaxError('%s cannot be used in @JNIAdditionalImport. ' - 'Only import unqualified outer classes.' % class_name) - new_import = 'L%s/%s' % (self._package, raw_class_name) - if new_import in self._imports: - raise SyntaxError('Do not use JNIAdditionalImport on an already ' - 'imported class: %s' % (new_import.replace('/', '.'))) - self._imports += [new_import] - - def _CheckImplicitImports(self, param): - # Ensure implicit imports, such as java.lang.*, are not being treated - # as being in the same package. - if not self._implicit_imports: - # This file was generated from android.jar and lists - # all classes that are implicitly imported. - with file(os.path.join(os.path.dirname(sys.argv[0]), - 'android_jar.classes'), 'r') as f: - self._implicit_imports = f.readlines() - for implicit_import in self._implicit_imports: - implicit_import = implicit_import.strip().replace('.class', '') - implicit_import = implicit_import.replace('/', '.') - if implicit_import.endswith('.' + param): - raise SyntaxError('Ambiguous class (%s) can not be used directly ' - 'by JNI.\nPlease import it, probably:\n\n' - 'import %s;' % - (param, implicit_import)) - - def Signature(self, params, returns): - """Returns the JNI signature for the given datatypes.""" - items = ['('] - items += [self.JavaToJni(param.datatype) for param in params] - items += [')'] - items += [self.JavaToJni(returns)] - return '"{}"'.format(''.join(items)) - - @staticmethod - def ParseJavaPSignature(signature_line): - prefix = 'Signature: ' - index = signature_line.find(prefix) - if index == -1: - prefix = 'descriptor: ' - index = signature_line.index(prefix) - return '"%s"' % signature_line[index + len(prefix):] - - @staticmethod - def Parse(params): - """Parses the params into a list of Param objects.""" - if not params: - return [] - ret = [] - params = _StripGenerics(params) - for p in params.split(','): - items = p.split() - - # Remove @Annotations from parameters. - while items[0].startswith('@'): - del items[0] - - if 'final' in items: - items.remove('final') - - param = Param( - datatype=items[0], - name=(items[1] if len(items) > 1 else 'p%s' % len(ret)), - ) - ret += [param] - return ret - - -def ExtractJNINamespace(contents): - re_jni_namespace = re.compile('.*?@JNINamespace\("(.*?)"\)') - m = re.findall(re_jni_namespace, contents) - if not m: - return '' - return m[0] - - -def ExtractFullyQualifiedJavaClassName(java_file_name, contents): - re_package = re.compile('.*?package (.*?);') - matches = re.findall(re_package, contents) - if not matches: - raise SyntaxError('Unable to find "package" line in %s' % java_file_name) - return (matches[0].replace('.', '/') + '/' + - os.path.splitext(os.path.basename(java_file_name))[0]) - - -def ExtractNatives(contents, ptr_type): - """Returns a list of dict containing information about a native method.""" - contents = contents.replace('\n', '') - natives = [] - for match in _EXTRACT_NATIVES_REGEX.finditer(contents): - native = NativeMethod( - static='static' in match.group('qualifiers'), - java_class_name=match.group('java_class_name'), - native_class_name=match.group('native_class_name'), - return_type=match.group('return_type'), - name=match.group('name').replace('native', ''), - params=JniParams.Parse(match.group('params')), - ptr_type=ptr_type) - natives += [native] - return natives - - -def IsMainDexJavaClass(contents): - """Returns True if the class or any of its methods are annotated as @MainDex. - - JNI registration doesn't always need to be completed for non-browser processes - since most Java code is only used by the browser process. Classes that are - needed by non-browser processes must explicitly be annotated with @MainDex - to force JNI registration. - """ - return bool(_MAIN_DEX_REGEX.search(contents)) - - -def GetBinaryClassName(fully_qualified_class): - """Returns a string concatenating the Java package and class.""" - escaped = fully_qualified_class.replace('_', '_1') - return escaped.replace('/', '_').replace('$', '_00024') - - -def GetRegistrationFunctionName(fully_qualified_class): - """Returns the register name with a given class.""" - return 'RegisterNative_' + GetBinaryClassName(fully_qualified_class) - - -def GetStaticCastForReturnType(return_type): - type_map = { 'String' : 'jstring', - 'java/lang/String' : 'jstring', - 'Class': 'jclass', - 'java/lang/Class': 'jclass', - 'Throwable': 'jthrowable', - 'java/lang/Throwable': 'jthrowable', - 'boolean[]': 'jbooleanArray', - 'byte[]': 'jbyteArray', - 'char[]': 'jcharArray', - 'short[]': 'jshortArray', - 'int[]': 'jintArray', - 'long[]': 'jlongArray', - 'float[]': 'jfloatArray', - 'double[]': 'jdoubleArray' } - return_type = _StripGenerics(return_type) - ret = type_map.get(return_type, None) - if ret: - return ret - if return_type.endswith('[]'): - return 'jobjectArray' - return None - - -def GetEnvCall(is_constructor, is_static, return_type): - """Maps the types availabe via env->Call__Method.""" - if is_constructor: - return 'NewObject' - env_call_map = {'boolean': 'Boolean', - 'byte': 'Byte', - 'char': 'Char', - 'short': 'Short', - 'int': 'Int', - 'long': 'Long', - 'float': 'Float', - 'void': 'Void', - 'double': 'Double', - 'Object': 'Object', - } - call = env_call_map.get(return_type, 'Object') - if is_static: - call = 'Static' + call - return 'Call' + call + 'Method' - - -def GetMangledParam(datatype): - """Returns a mangled identifier for the datatype.""" - if len(datatype) <= 2: - return datatype.replace('[', 'A') - ret = '' - for i in range(1, len(datatype)): - c = datatype[i] - if c == '[': - ret += 'A' - elif c.isupper() or datatype[i - 1] in ['/', 'L']: - ret += c.upper() - return ret - - -def GetMangledMethodName(jni_params, name, params, return_type): - """Returns a mangled method name for the given signature. - - The returned name can be used as a C identifier and will be unique for all - valid overloads of the same method. - - Args: - jni_params: JniParams object. - name: string. - params: list of Param. - return_type: string. - - Returns: - A mangled name. - """ - mangled_items = [] - for datatype in [return_type] + [x.datatype for x in params]: - mangled_items += [GetMangledParam(jni_params.JavaToJni(datatype))] - mangled_name = name + '_'.join(mangled_items) - assert re.match(r'[0-9a-zA-Z_]+', mangled_name) - return mangled_name - - -def MangleCalledByNatives(jni_params, called_by_natives): - """Mangles all the overloads from the call_by_natives list.""" - method_counts = collections.defaultdict( - lambda: collections.defaultdict(lambda: 0)) - for called_by_native in called_by_natives: - java_class_name = called_by_native.java_class_name - name = called_by_native.name - method_counts[java_class_name][name] += 1 - for called_by_native in called_by_natives: - java_class_name = called_by_native.java_class_name - method_name = called_by_native.name - method_id_var_name = method_name - if method_counts[java_class_name][method_name] > 1: - method_id_var_name = GetMangledMethodName(jni_params, method_name, - called_by_native.params, - called_by_native.return_type) - called_by_native.method_id_var_name = method_id_var_name - return called_by_natives - - -# Regex to match the JNI types that should be wrapped in a JavaRef. -RE_SCOPED_JNI_TYPES = re.compile('jobject|jclass|jstring|jthrowable|.*Array') - - -# Regex to match a string like "@CalledByNative public void foo(int bar)". -RE_CALLED_BY_NATIVE = re.compile( - r'@CalledByNative(?P(?:Unchecked)?)(?:\("(?P.*)"\))?' - r'(?:\s+@\w+(?:\(.*\))?)*' # Ignore any other annotations. - r'\s+(?P(' - r'(private|protected|public|static|abstract|final|default|synchronized)' - r'\s*)*)' - r'(?:\s*@\w+)?' # Ignore annotations in return types. - r'\s*(?P\S*?)' - r'\s*(?P\w+)' - r'\s*\((?P[^\)]*)\)') - -# Removes empty lines that are indented (i.e. start with 2x spaces). -def RemoveIndentedEmptyLines(string): - return re.sub('^(?: {2})+$\n', '', string, flags=re.MULTILINE) - - -def ExtractCalledByNatives(jni_params, contents): - """Parses all methods annotated with @CalledByNative. - - Args: - jni_params: JniParams object. - contents: the contents of the java file. - - Returns: - A list of dict with information about the annotated methods. - TODO(bulach): return a CalledByNative object. - - Raises: - ParseError: if unable to parse. - """ - called_by_natives = [] - for match in re.finditer(RE_CALLED_BY_NATIVE, contents): - return_type = match.group('return_type') - name = match.group('name') - if not return_type: - is_constructor = True - return_type = name - name = "Constructor" - else: - is_constructor = False - - called_by_natives += [CalledByNative( - system_class=False, - unchecked='Unchecked' in match.group('Unchecked'), - static='static' in match.group('prefix'), - java_class_name=match.group('annotation') or '', - return_type=return_type, - name=name, - is_constructor=is_constructor, - params=JniParams.Parse(match.group('params')))] - # Check for any @CalledByNative occurrences that weren't matched. - unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n') - for line1, line2 in zip(unmatched_lines, unmatched_lines[1:]): - if '@CalledByNative' in line1: - raise ParseError('could not parse @CalledByNative method signature', - line1, line2) - return MangleCalledByNatives(jni_params, called_by_natives) - - -def RemoveComments(contents): - # We need to support both inline and block comments, and we need to handle - # strings that contain '//' or '/*'. - # TODO(bulach): This is a bit hacky. It would be cleaner to use a real Java - # parser. Maybe we could ditch JNIFromJavaSource and just always use - # JNIFromJavaP; or maybe we could rewrite this script in Java and use APT. - # http://code.google.com/p/chromium/issues/detail?id=138941 - def replacer(match): - # Replace matches that are comments with nothing; return literals/strings - # unchanged. - s = match.group(0) - if s.startswith('/'): - return '' - else: - return s - return _COMMENT_REMOVER_REGEX.sub(replacer, contents) - - -class JNIFromJavaP(object): - """Uses 'javap' to parse a .class file and generate the JNI header file.""" - - def __init__(self, contents, options): - self.contents = contents - self.namespace = options.namespace - for line in contents: - class_name = re.match( - '.*?(public).*?(class|interface) (?P\S+?)( |\Z)', - line) - if class_name: - self.fully_qualified_class = class_name.group('class_name') - break - self.fully_qualified_class = self.fully_qualified_class.replace('.', '/') - # Java 7's javap includes type parameters in output, like HashSet. Strip - # away the <...> and use the raw class name that Java 6 would've given us. - self.fully_qualified_class = self.fully_qualified_class.split('<', 1)[0] - self.jni_params = JniParams(self.fully_qualified_class) - self.java_class_name = self.fully_qualified_class.split('/')[-1] - if not self.namespace: - self.namespace = 'JNI_' + self.java_class_name - re_method = re.compile('(?P.*?)(?P\S+?) (?P\w+?)' - '\((?P.*?)\)') - self.called_by_natives = [] - for lineno, content in enumerate(contents[2:], 2): - match = re.match(re_method, content) - if not match: - continue - self.called_by_natives += [CalledByNative( - system_class=True, - unchecked=False, - static='static' in match.group('prefix'), - java_class_name='', - return_type=match.group('return_type').replace('.', '/'), - name=match.group('name'), - params=JniParams.Parse(match.group('params').replace('.', '/')), - signature=JniParams.ParseJavaPSignature(contents[lineno + 1]))] - re_constructor = re.compile('(.*?)public ' + - self.fully_qualified_class.replace('/', '.') + - '\((?P.*?)\)') - for lineno, content in enumerate(contents[2:], 2): - match = re.match(re_constructor, content) - if not match: - continue - self.called_by_natives += [CalledByNative( - system_class=True, - unchecked=False, - static=False, - java_class_name='', - return_type=self.fully_qualified_class, - name='Constructor', - params=JniParams.Parse(match.group('params').replace('.', '/')), - signature=JniParams.ParseJavaPSignature(contents[lineno + 1]), - is_constructor=True)] - self.called_by_natives = MangleCalledByNatives(self.jni_params, - self.called_by_natives) - self.constant_fields = [] - re_constant_field = re.compile('.*?public static final int (?P.*?);') - re_constant_field_value = re.compile( - '.*?Constant(Value| value): int (?P(-*[0-9]+)?)') - for lineno, content in enumerate(contents[2:], 2): - match = re.match(re_constant_field, content) - if not match: - continue - value = re.match(re_constant_field_value, contents[lineno + 2]) - if not value: - value = re.match(re_constant_field_value, contents[lineno + 3]) - if value: - self.constant_fields.append( - ConstantField(name=match.group('name'), - value=value.group('value'))) - - self.inl_header_file_generator = InlHeaderFileGenerator( - self.namespace, self.fully_qualified_class, [], self.called_by_natives, - self.constant_fields, self.jni_params, options) - - def GetContent(self): - return self.inl_header_file_generator.GetContent() - - @staticmethod - def CreateFromClass(class_file, options): - class_name = os.path.splitext(os.path.basename(class_file))[0] - p = subprocess.Popen(args=[options.javap, '-c', '-verbose', - '-s', class_name], - cwd=os.path.dirname(class_file), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout, _ = p.communicate() - jni_from_javap = JNIFromJavaP(stdout.split('\n'), options) - return jni_from_javap - - -class JNIFromJavaSource(object): - """Uses the given java source file to generate the JNI header file.""" - - def __init__(self, contents, fully_qualified_class, options): - contents = RemoveComments(contents) - self.jni_params = JniParams(fully_qualified_class) - self.jni_params.ExtractImportsAndInnerClasses(contents) - jni_namespace = ExtractJNINamespace(contents) or options.namespace - natives = ExtractNatives(contents, options.ptr_type) - called_by_natives = ExtractCalledByNatives(self.jni_params, contents) - if len(natives) == 0 and len(called_by_natives) == 0: - raise SyntaxError('Unable to find any JNI methods for %s.' % - fully_qualified_class) - inl_header_file_generator = InlHeaderFileGenerator( - jni_namespace, fully_qualified_class, natives, called_by_natives, [], - self.jni_params, options) - self.content = inl_header_file_generator.GetContent() - - def GetContent(self): - return self.content - - @staticmethod - def CreateFromFile(java_file_name, options): - contents = file(java_file_name).read() - fully_qualified_class = ExtractFullyQualifiedJavaClassName(java_file_name, - contents) - return JNIFromJavaSource(contents, fully_qualified_class, options) - - -class HeaderFileGeneratorHelper(object): - """Include helper methods for header generators.""" - - def __init__(self, class_name, fully_qualified_class): - self.class_name = class_name - self.fully_qualified_class = fully_qualified_class - - def GetStubName(self, native): - """Return the name of the stub function for this native method. - - Args: - native: the native dictionary describing the method. - - Returns: - A string with the stub function name (used by the JVM). - """ - template = Template("Java_${JAVA_NAME}_native${NAME}") - - java_name = self.fully_qualified_class - if native.java_class_name: - java_name += '$' + native.java_class_name - - values = {'NAME': native.name, - 'JAVA_NAME': GetBinaryClassName(java_name)} - return template.substitute(values) - - def GetUniqueClasses(self, origin): - ret = {self.class_name: self.fully_qualified_class} - for entry in origin: - class_name = self.class_name - jni_class_path = self.fully_qualified_class - if entry.java_class_name: - class_name = entry.java_class_name - jni_class_path = self.fully_qualified_class + '$' + class_name - ret[class_name] = jni_class_path - return ret - - def GetClassPathLines(self, classes, declare_only=False): - """Returns the ClassPath constants.""" - ret = [] - if declare_only: - template = Template(""" -extern const char kClassPath_${JAVA_CLASS}[]; -""") - else: - template = Template(""" -JNI_REGISTRATION_EXPORT extern const char kClassPath_${JAVA_CLASS}[]; -const char kClassPath_${JAVA_CLASS}[] = \ -"${JNI_CLASS_PATH}"; -""") - - for full_clazz in classes.itervalues(): - values = { - 'JAVA_CLASS': GetBinaryClassName(full_clazz), - 'JNI_CLASS_PATH': full_clazz, - } - ret += [template.substitute(values)] - - class_getter = """\ -#ifndef ${JAVA_CLASS}_clazz_defined -#define ${JAVA_CLASS}_clazz_defined -inline jclass ${JAVA_CLASS}_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_${JAVA_CLASS}, \ -&g_${JAVA_CLASS}_clazz); -} -#endif -""" - if declare_only: - template = Template("""\ -extern base::subtle::AtomicWord g_${JAVA_CLASS}_clazz; -""" + class_getter) - else: - template = Template("""\ -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_${JAVA_CLASS}_clazz = 0; -""" + class_getter) - - for full_clazz in classes.itervalues(): - values = { - 'JAVA_CLASS': GetBinaryClassName(full_clazz), - } - ret += [template.substitute(values)] - - return ''.join(ret) - - -class InlHeaderFileGenerator(object): - """Generates an inline header file for JNI integration.""" - - def __init__(self, namespace, fully_qualified_class, natives, - called_by_natives, constant_fields, jni_params, options): - self.namespace = namespace - self.fully_qualified_class = fully_qualified_class - self.class_name = self.fully_qualified_class.split('/')[-1] - self.natives = natives - self.called_by_natives = called_by_natives - self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' - self.constant_fields = constant_fields - self.jni_params = jni_params - self.options = options - self.helper = HeaderFileGeneratorHelper( - self.class_name, fully_qualified_class) - - - def GetContent(self): - """Returns the content of the JNI binding file.""" - template = Template("""\ -// 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 autogenerated by -// ${SCRIPT_NAME} -// For -// ${FULLY_QUALIFIED_CLASS} - -#ifndef ${HEADER_GUARD} -#define ${HEADER_GUARD} - -#include - -${INCLUDES} - -// Step 1: Forward declarations. -$CLASS_PATH_DEFINITIONS - -// Step 2: Constants (optional). - -$CONSTANT_FIELDS\ - -// Step 3: Method stubs. -$METHOD_STUBS - -#endif // ${HEADER_GUARD} -""") - values = { - 'SCRIPT_NAME': self.options.script_name, - 'FULLY_QUALIFIED_CLASS': self.fully_qualified_class, - 'CLASS_PATH_DEFINITIONS': self.GetClassPathDefinitionsString(), - 'CONSTANT_FIELDS': self.GetConstantFieldsString(), - 'METHOD_STUBS': self.GetMethodStubsString(), - 'HEADER_GUARD': self.header_guard, - 'INCLUDES': self.GetIncludesString(), - } - open_namespace = self.GetOpenNamespaceString() - if open_namespace: - close_namespace = self.GetCloseNamespaceString() - values['METHOD_STUBS'] = '\n'.join([ - open_namespace, values['METHOD_STUBS'], close_namespace]) - - constant_fields = values['CONSTANT_FIELDS'] - if constant_fields: - values['CONSTANT_FIELDS'] = '\n'.join([ - open_namespace, constant_fields, close_namespace]) - - return WrapOutput(template.substitute(values)) - - def GetClassPathDefinitionsString(self): - classes = self.helper.GetUniqueClasses(self.called_by_natives) - classes.update(self.helper.GetUniqueClasses(self.natives)) - return self.helper.GetClassPathLines(classes) - - def GetConstantFieldsString(self): - if not self.constant_fields: - return '' - ret = ['enum Java_%s_constant_fields {' % self.class_name] - for c in self.constant_fields: - ret += [' %s = %s,' % (c.name, c.value)] - ret += ['};', ''] - return '\n'.join(ret) - - def GetMethodStubsString(self): - """Returns the code corresponding to method stubs.""" - ret = [] - for native in self.natives: - ret += [self.GetNativeStub(native)] - ret += self.GetLazyCalledByNativeMethodStubs() - return '\n'.join(ret) - - def GetLazyCalledByNativeMethodStubs(self): - return [self.GetLazyCalledByNativeMethodStub(called_by_native) - for called_by_native in self.called_by_natives] - - def GetIncludesString(self): - if not self.options.includes: - return '' - includes = self.options.includes.split(',') - return '\n'.join('#include "%s"' % x for x in includes) + '\n' - - def GetOpenNamespaceString(self): - if self.namespace: - all_namespaces = ['namespace %s {' % ns - for ns in self.namespace.split('::')] - return '\n'.join(all_namespaces) + '\n' - return '' - - def GetCloseNamespaceString(self): - if self.namespace: - all_namespaces = ['} // namespace %s' % ns - for ns in self.namespace.split('::')] - all_namespaces.reverse() - return '\n' + '\n'.join(all_namespaces) - return '' - - def GetCalledByNativeParamsInDeclaration(self, called_by_native): - return ',\n '.join([ - JavaDataTypeToCForCalledByNativeParam(param.datatype) + ' ' + - param.name - for param in called_by_native.params]) - - def GetJavaParamRefForCall(self, c_type, name): - return Template( - 'base::android::JavaParamRef<${TYPE}>(env, ${NAME})').substitute({ - 'TYPE': c_type, - 'NAME': name, - }) - - def GetJNIFirstParamForCall(self, native): - c_type = _GetJNIFirstParamType(native) - return [self.GetJavaParamRefForCall(c_type, 'jcaller')] - - def GetImplementationMethodName(self, native): - class_name = self.class_name - if native.java_class_name is not None: - # Inner class - class_name = native.java_class_name - return "JNI_%s_%s" % (class_name, native.name) - - def GetNativeStub(self, native): - is_method = native.type == 'method' - - if is_method: - params = native.params[1:] - else: - params = native.params - params_in_call = ['env'] + self.GetJNIFirstParamForCall(native) - for p in params: - c_type = JavaDataTypeToC(p.datatype) - if re.match(RE_SCOPED_JNI_TYPES, c_type): - params_in_call.append(self.GetJavaParamRefForCall(c_type, p.name)) - else: - params_in_call.append(p.name) - params_in_call = ', '.join(params_in_call) - - return_type = return_declaration = JavaDataTypeToC(native.return_type) - post_call = '' - if re.match(RE_SCOPED_JNI_TYPES, return_type): - post_call = '.Release()' - return_declaration = ('base::android::ScopedJavaLocalRef<' + return_type + - '>') - profiling_entered_native = '' - if self.options.enable_profiling: - profiling_entered_native = ' JNI_LINK_SAVED_FRAME_POINTER;\n' - values = { - 'RETURN': return_type, - 'RETURN_DECLARATION': return_declaration, - 'NAME': native.name, - 'IMPL_METHOD_NAME': self.GetImplementationMethodName(native), - 'PARAMS': _GetParamsInDeclaration(native), - 'PARAMS_IN_STUB': GetParamsInStub(native), - 'PARAMS_IN_CALL': params_in_call, - 'POST_CALL': post_call, - 'STUB_NAME': self.helper.GetStubName(native), - 'PROFILING_ENTERED_NATIVE': profiling_entered_native, - 'TRACE_EVENT': '', - } - - namespace_qual = self.namespace + '::' if self.namespace else '' - if is_method: - optional_error_return = JavaReturnValueToC(native.return_type) - if optional_error_return: - optional_error_return = ', ' + optional_error_return - values.update({ - 'OPTIONAL_ERROR_RETURN': optional_error_return, - 'PARAM0_NAME': native.params[0].name, - 'P0_TYPE': native.p0_type, - }) - if self.options.enable_tracing: - values['TRACE_EVENT'] = self.GetTraceEventForNameTemplate( - namespace_qual + '${P0_TYPE}::${NAME}', values); - template = Template("""\ -JNI_GENERATOR_EXPORT ${RETURN} ${STUB_NAME}( - JNIEnv* env, - ${PARAMS_IN_STUB}) { -${PROFILING_ENTERED_NATIVE}\ -${TRACE_EVENT}\ - ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); - CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN}); - return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL}; -} -""") - else: - if self.options.enable_tracing: - values['TRACE_EVENT'] = self.GetTraceEventForNameTemplate( - namespace_qual + '${IMPL_METHOD_NAME}', values) - template = Template("""\ -static ${RETURN_DECLARATION} ${IMPL_METHOD_NAME}(JNIEnv* env, ${PARAMS}); - -JNI_GENERATOR_EXPORT ${RETURN} ${STUB_NAME}( - JNIEnv* env, - ${PARAMS_IN_STUB}) { -${PROFILING_ENTERED_NATIVE}\ -${TRACE_EVENT}\ - return ${IMPL_METHOD_NAME}(${PARAMS_IN_CALL})${POST_CALL}; -} -""") - - return RemoveIndentedEmptyLines(template.substitute(values)) - - def GetArgument(self, param): - if param.datatype == 'int': - return 'as_jint(' + param.name + ')' - elif re.match(RE_SCOPED_JNI_TYPES, JavaDataTypeToC(param.datatype)): - return param.name + '.obj()' - else: - return param.name - - def GetArgumentsInCall(self, params): - """Return a string of arguments to call from native into Java""" - return [self.GetArgument(p) for p in params] - - def GetCalledByNativeValues(self, called_by_native): - """Fills in necessary values for the CalledByNative methods.""" - java_class_only = called_by_native.java_class_name or self.class_name - java_class = self.fully_qualified_class - if called_by_native.java_class_name: - java_class += '$' + called_by_native.java_class_name - - if called_by_native.static or called_by_native.is_constructor: - first_param_in_declaration = '' - first_param_in_call = ('%s_clazz(env)' % GetBinaryClassName(java_class)) - else: - first_param_in_declaration = ( - ', const base::android::JavaRef& obj') - first_param_in_call = 'obj.obj()' - params_in_declaration = self.GetCalledByNativeParamsInDeclaration( - called_by_native) - if params_in_declaration: - params_in_declaration = ', ' + params_in_declaration - params_in_call = ', '.join(self.GetArgumentsInCall(called_by_native.params)) - if params_in_call: - params_in_call = ', ' + params_in_call - pre_call = '' - post_call = '' - if called_by_native.static_cast: - pre_call = 'static_cast<%s>(' % called_by_native.static_cast - post_call = ')' - check_exception = '' - if not called_by_native.unchecked: - check_exception = 'jni_generator::CheckException(env);' - return_type = JavaDataTypeToC(called_by_native.return_type) - optional_error_return = JavaReturnValueToC(called_by_native.return_type) - if optional_error_return: - optional_error_return = ', ' + optional_error_return - return_declaration = '' - return_clause = '' - if return_type != 'void': - pre_call = ' ' + pre_call - return_declaration = return_type + ' ret =' - if re.match(RE_SCOPED_JNI_TYPES, return_type): - return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>' - return_clause = 'return ' + return_type + '(env, ret);' - else: - return_clause = 'return ret;' - profiling_leaving_native = '' - if self.options.enable_profiling: - profiling_leaving_native = ' JNI_SAVE_FRAME_POINTER;\n' - jni_name = called_by_native.name - jni_return_type = called_by_native.return_type - if called_by_native.is_constructor: - jni_name = '' - jni_return_type = 'void' - if called_by_native.signature: - jni_signature = called_by_native.signature - else: - jni_signature = self.jni_params.Signature( - called_by_native.params, jni_return_type) - java_name_full = java_class.replace('/', '.') + '.' + jni_name - return { - 'JAVA_CLASS_ONLY': java_class_only, - 'JAVA_CLASS': GetBinaryClassName(java_class), - 'RETURN_TYPE': return_type, - 'OPTIONAL_ERROR_RETURN': optional_error_return, - 'RETURN_DECLARATION': return_declaration, - 'RETURN_CLAUSE': return_clause, - 'FIRST_PARAM_IN_DECLARATION': first_param_in_declaration, - 'PARAMS_IN_DECLARATION': params_in_declaration, - 'PRE_CALL': pre_call, - 'POST_CALL': post_call, - 'ENV_CALL': called_by_native.env_call, - 'FIRST_PARAM_IN_CALL': first_param_in_call, - 'PARAMS_IN_CALL': params_in_call, - 'CHECK_EXCEPTION': check_exception, - 'PROFILING_LEAVING_NATIVE': profiling_leaving_native, - 'JNI_NAME': jni_name, - 'JNI_SIGNATURE': jni_signature, - 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, - 'METHOD_ID_TYPE': 'STATIC' if called_by_native.static else 'INSTANCE', - 'JAVA_NAME_FULL': java_name_full, - } - - def GetLazyCalledByNativeMethodStub(self, called_by_native): - """Returns a string.""" - function_signature_template = Template("""\ -static ${RETURN_TYPE} Java_${JAVA_CLASS_ONLY}_${METHOD_ID_VAR_NAME}(\ -JNIEnv* env${FIRST_PARAM_IN_DECLARATION}${PARAMS_IN_DECLARATION})""") - function_header_template = Template("""\ -${FUNCTION_SIGNATURE} {""") - function_header_with_unused_template = Template("""\ -${FUNCTION_SIGNATURE} __attribute__ ((unused)); -${FUNCTION_SIGNATURE} {""") - template = Template(""" -static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0; -${FUNCTION_HEADER} - CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL}, - ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN}); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_${METHOD_ID_TYPE}>( - env, ${JAVA_CLASS}_clazz(env), - "${JNI_NAME}", - ${JNI_SIGNATURE}, - &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}); - -${TRACE_EVENT}\ -${PROFILING_LEAVING_NATIVE}\ - ${RETURN_DECLARATION} - ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL}, - method_id${PARAMS_IN_CALL})${POST_CALL}; - ${CHECK_EXCEPTION} - ${RETURN_CLAUSE} -}""") - values = self.GetCalledByNativeValues(called_by_native) - values['FUNCTION_SIGNATURE'] = ( - function_signature_template.substitute(values)) - if called_by_native.system_class: - values['FUNCTION_HEADER'] = ( - function_header_with_unused_template.substitute(values)) - else: - values['FUNCTION_HEADER'] = function_header_template.substitute(values) - if self.options.enable_tracing: - values['TRACE_EVENT'] = self.GetTraceEventForNameTemplate( - '${JAVA_NAME_FULL}', values) - else: - values['TRACE_EVENT'] = '' - return RemoveIndentedEmptyLines(template.substitute(values)) - - def GetTraceEventForNameTemplate(self, name_template, values): - name = Template(name_template).substitute(values) - return ' TRACE_EVENT0("jni", "%s");' % name - - -def WrapOutput(output): - ret = [] - for line in output.splitlines(): - # Do not wrap preprocessor directives or comments. - if len(line) < _WRAP_LINE_LENGTH or line[0] == '#' or line.startswith('//'): - ret.append(line) - else: - # Assumes that the line is not already indented as a continuation line, - # which is not always true (oh well). - first_line_indent = (len(line) - len(line.lstrip())) - wrapper = _WRAPPERS_BY_INDENT[first_line_indent] - ret.extend(wrapper.wrap(line)) - ret += [''] - return '\n'.join(ret) - - -def ExtractJarInputFile(jar_file, input_file, out_dir): - """Extracts input file from jar and returns the filename. - - The input file is extracted to the same directory that the generated jni - headers will be placed in. This is passed as an argument to script. - - Args: - jar_file: the jar file containing the input files to extract. - input_files: the list of files to extract from the jar file. - out_dir: the name of the directories to extract to. - - Returns: - the name of extracted input file. - """ - jar_file = zipfile.ZipFile(jar_file) - - out_dir = os.path.join(out_dir, os.path.dirname(input_file)) - try: - os.makedirs(out_dir) - except OSError as e: - if e.errno != errno.EEXIST: - raise - extracted_file_name = os.path.join(out_dir, os.path.basename(input_file)) - with open(extracted_file_name, 'w') as outfile: - outfile.write(jar_file.read(input_file)) - - return extracted_file_name - - -def GenerateJNIHeader(input_file, output_file, options): - try: - if os.path.splitext(input_file)[1] == '.class': - jni_from_javap = JNIFromJavaP.CreateFromClass(input_file, options) - content = jni_from_javap.GetContent() - else: - jni_from_java_source = JNIFromJavaSource.CreateFromFile( - input_file, options) - content = jni_from_java_source.GetContent() - except ParseError, e: - print e - sys.exit(1) - if output_file: - WriteOutput(output_file, content) - else: - print content - - -def WriteOutput(output_file, content): - if os.path.exists(output_file): - with open(output_file) as f: - existing_content = f.read() - if existing_content == content: - return - with open(output_file, 'w') as f: - f.write(content) - - -def GetScriptName(): - script_components = os.path.abspath(sys.argv[0]).split(os.path.sep) - base_index = 0 - for idx, value in enumerate(script_components): - if value == 'base' or value == 'third_party': - base_index = idx - break - return os.sep.join(script_components[base_index:]) - - -def main(argv): - usage = """usage: %prog [OPTIONS] -This script will parse the given java source code extracting the native -declarations and print the header file to stdout (or a file). -See SampleForTests.java for more details. - """ - option_parser = optparse.OptionParser(usage=usage) - build_utils.AddDepfileOption(option_parser) - - option_parser.add_option('-j', '--jar_file', dest='jar_file', - help='Extract the list of input files from' - ' a specified jar file.' - ' Uses javap to extract the methods from a' - ' pre-compiled class. --input should point' - ' to pre-compiled Java .class files.') - option_parser.add_option('-n', dest='namespace', - help='Uses as a namespace in the generated header ' - 'instead of the javap class name, or when there is ' - 'no JNINamespace annotation in the java source.') - option_parser.add_option('--input_file', - help='Single input file name. The output file name ' - 'will be derived from it. Must be used with ' - '--output_dir.') - option_parser.add_option('--output_dir', - help='The output directory. Must be used with ' - '--input') - option_parser.add_option('--script_name', default=GetScriptName(), - help='The name of this script in the generated ' - 'header.') - option_parser.add_option('--includes', - help='The comma-separated list of header files to ' - 'include in the generated header.') - option_parser.add_option('--ptr_type', default='int', - type='choice', choices=['int', 'long'], - help='The type used to represent native pointers in ' - 'Java code. For 32-bit, use int; ' - 'for 64-bit, use long.') - option_parser.add_option('--cpp', default='cpp', - help='The path to cpp command.') - option_parser.add_option('--javap', default='javap', - help='The path to javap command.') - option_parser.add_option('--enable_profiling', action='store_true', - help='Add additional profiling instrumentation.') - option_parser.add_option('--enable_tracing', action='store_true', - help='Add TRACE_EVENTs to generated functions.') - options, args = option_parser.parse_args(argv) - if options.jar_file: - input_file = ExtractJarInputFile(options.jar_file, options.input_file, - options.output_dir) - elif options.input_file: - input_file = options.input_file - else: - option_parser.print_help() - print '\nError: Must specify --jar_file or --input_file.' - return 1 - output_file = None - if options.output_dir: - root_name = os.path.splitext(os.path.basename(input_file))[0] - output_file = os.path.join(options.output_dir, root_name) + '_jni.h' - GenerateJNIHeader(input_file, output_file, options) - - if options.depfile: - build_utils.WriteDepfile(options.depfile, output_file) - - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/android/jni_generator/jni_generator_helper.h b/android/jni_generator/jni_generator_helper.h deleted file mode 100644 index 3347fee1f..000000000 --- a/android/jni_generator/jni_generator_helper.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_JNI_GENERATOR_JNI_GENERATOR_HELPER_H_ -#define BASE_ANDROID_JNI_GENERATOR_JNI_GENERATOR_HELPER_H_ - -#include - -#include "base/android/jni_android.h" -#include "base/android/jni_int_wrapper.h" -#include "base/android/scoped_java_ref.h" -#include "base/logging.h" -#include "base/trace_event/trace_event.h" -#include "build/build_config.h" - -// Project-specific macros used by the header files generated by -// jni_generator.py. Different projects can then specify their own -// implementation for this file. -#define CHECK_NATIVE_PTR(env, jcaller, native_ptr, method_name, ...) \ - DCHECK(native_ptr) << method_name; - -#define CHECK_CLAZZ(env, jcaller, clazz, ...) DCHECK(clazz); - -#if defined(ARCH_CPU_X86) -// Dalvik JIT generated code doesn't guarantee 16-byte stack alignment on -// x86 - use force_align_arg_pointer to realign the stack at the JNI -// boundary. crbug.com/655248 -#define JNI_GENERATOR_EXPORT \ - extern "C" __attribute__((visibility("default"), force_align_arg_pointer)) -#else -#define JNI_GENERATOR_EXPORT extern "C" __attribute__((visibility("default"))) -#endif - -// Used to export JNI registration functions. -#if defined(COMPONENT_BUILD) -#define JNI_REGISTRATION_EXPORT __attribute__((visibility("default"))) -#else -#define JNI_REGISTRATION_EXPORT -#endif - -namespace jni_generator { - -inline void HandleRegistrationError(JNIEnv* env, - jclass clazz, - const char* filename) { - LOG(ERROR) << "RegisterNatives failed in " << filename; -} - -inline void CheckException(JNIEnv* env) { - base::android::CheckException(env); -} - -} // namespace jni_generator - -#endif // BASE_ANDROID_JNI_GENERATOR_JNI_GENERATOR_HELPER_H_ diff --git a/android/jni_generator/jni_generator_tests.py b/android/jni_generator/jni_generator_tests.py deleted file mode 100755 index 12812a5c5..000000000 --- a/android/jni_generator/jni_generator_tests.py +++ /dev/null @@ -1,1188 +0,0 @@ -#!/usr/bin/env python -# 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. - -"""Tests for jni_generator.py. - -This test suite contains various tests for the JNI generator. -It exercises the low-level parser all the way up to the -code generator and ensures the output matches a golden -file. -""" - -import difflib -import inspect -import optparse -import os -import sys -import unittest -import jni_generator -import jni_registration_generator -from jni_generator import CalledByNative -from jni_generator import IsMainDexJavaClass -from jni_generator import NativeMethod -from jni_generator import Param - - -SCRIPT_NAME = 'base/android/jni_generator/jni_generator.py' -INCLUDES = ( - 'base/android/jni_generator/jni_generator_helper.h' -) - -# Set this environment variable in order to regenerate the golden text -# files. -REBASELINE_ENV = 'REBASELINE' - -class TestOptions(object): - """The mock options object which is passed to the jni_generator.py script.""" - - def __init__(self): - self.namespace = None - self.script_name = SCRIPT_NAME - self.includes = INCLUDES - self.ptr_type = 'long' - self.cpp = 'cpp' - self.javap = 'javap' - self.native_exports_optional = True - self.enable_profiling = False - self.enable_tracing = False - -class TestGenerator(unittest.TestCase): - def assertObjEquals(self, first, second): - dict_first = first.__dict__ - dict_second = second.__dict__ - self.assertEquals(dict_first.keys(), dict_second.keys()) - for key, value in dict_first.iteritems(): - if (type(value) is list and len(value) and - isinstance(type(value[0]), object)): - self.assertListEquals(value, second.__getattribute__(key)) - else: - actual = second.__getattribute__(key) - self.assertEquals(value, actual, - 'Key ' + key + ': ' + str(value) + '!=' + str(actual)) - - def assertListEquals(self, first, second): - self.assertEquals(len(first), len(second)) - for i in xrange(len(first)): - if isinstance(first[i], object): - self.assertObjEquals(first[i], second[i]) - else: - self.assertEquals(first[i], second[i]) - - def assertTextEquals(self, golden_text, generated_text): - if not self.compareText(golden_text, generated_text): - self.fail('Golden text mismatch.') - - def compareText(self, golden_text, generated_text): - def FilterText(text): - return [ - l.strip() for l in text.split('\n') - if not l.startswith('// Copyright') - ] - stripped_golden = FilterText(golden_text) - stripped_generated = FilterText(generated_text) - if stripped_golden == stripped_generated: - return True - print self.id() - for line in difflib.context_diff(stripped_golden, stripped_generated): - print line - print '\n\nGenerated' - print '=' * 80 - print generated_text - print '=' * 80 - print 'Run with:' - print 'REBASELINE=1', sys.argv[0] - print 'to regenerate the data files.' - - def _ReadGoldenFile(self, golden_file): - if not os.path.exists(golden_file): - return None - with file(golden_file, 'r') as f: - return f.read() - - def assertGoldenTextEquals(self, generated_text, suffix=''): - script_dir = os.path.dirname(sys.argv[0]) - # This is the caller test method. - caller = inspect.stack()[1][3] - self.assertTrue(caller.startswith('test'), - 'assertGoldenTextEquals can only be called from a ' - 'test* method, not %s' % caller) - golden_file = os.path.join(script_dir, '%s%s.golden' % (caller, suffix)) - golden_text = self._ReadGoldenFile(golden_file) - if os.environ.get(REBASELINE_ENV): - if golden_text != generated_text: - with file(golden_file, 'w') as f: - f.write(generated_text) - return - self.assertTextEquals(golden_text, generated_text) - - def testInspectCaller(self): - def willRaise(): - # This function can only be called from a test* method. - self.assertGoldenTextEquals('') - self.assertRaises(AssertionError, willRaise) - - def testNatives(self): - test_data = """" - import android.graphics.Bitmap; - import android.view.View; - - interface OnFrameAvailableListener {} - private native int nativeInit(); - private native void nativeDestroy(int nativeChromeBrowserProvider); - private native long nativeAddBookmark( - int nativeChromeBrowserProvider, - String url, String title, boolean isFolder, long parentId); - private static native String nativeGetDomainAndRegistry(String url); - private static native void nativeCreateHistoricalTabFromState( - byte[] state, int tab_index); - private native byte[] nativeGetStateAsByteArray(View view); - private static native String[] nativeGetAutofillProfileGUIDs(); - private native void nativeSetRecognitionResults( - int sessionId, String[] results); - private native long nativeAddBookmarkFromAPI( - int nativeChromeBrowserProvider, - String url, Long created, Boolean isBookmark, - Long date, byte[] favicon, String title, Integer visits); - native int nativeFindAll(String find); - private static native OnFrameAvailableListener nativeGetInnerClass(); - private native Bitmap nativeQueryBitmap( - int nativeChromeBrowserProvider, - String[] projection, String selection, - String[] selectionArgs, String sortOrder); - private native void nativeGotOrientation( - int nativeDataFetcherImplAndroid, - double alpha, double beta, double gamma); - private static native Throwable nativeMessWithJavaException(Throwable e); - """ - jni_params = jni_generator.JniParams( - 'org/chromium/example/jni_generator/SampleForTests') - jni_params.ExtractImportsAndInnerClasses(test_data) - natives = jni_generator.ExtractNatives(test_data, 'int') - golden_natives = [ - NativeMethod(return_type='int', static=False, - name='Init', - params=[], - java_class_name=None, - type='function'), - NativeMethod(return_type='void', static=False, name='Destroy', - params=[Param(datatype='int', - name='nativeChromeBrowserProvider')], - java_class_name=None, - type='method', - p0_type='ChromeBrowserProvider'), - NativeMethod(return_type='long', static=False, name='AddBookmark', - params=[Param(datatype='int', - name='nativeChromeBrowserProvider'), - Param(datatype='String', - name='url'), - Param(datatype='String', - name='title'), - Param(datatype='boolean', - name='isFolder'), - Param(datatype='long', - name='parentId')], - java_class_name=None, - type='method', - p0_type='ChromeBrowserProvider'), - NativeMethod(return_type='String', static=True, - name='GetDomainAndRegistry', - params=[Param(datatype='String', - name='url')], - java_class_name=None, - type='function'), - NativeMethod(return_type='void', static=True, - name='CreateHistoricalTabFromState', - params=[Param(datatype='byte[]', - name='state'), - Param(datatype='int', - name='tab_index')], - java_class_name=None, - type='function'), - NativeMethod(return_type='byte[]', static=False, - name='GetStateAsByteArray', - params=[Param(datatype='View', name='view')], - java_class_name=None, - type='function'), - NativeMethod(return_type='String[]', static=True, - name='GetAutofillProfileGUIDs', params=[], - java_class_name=None, - type='function'), - NativeMethod(return_type='void', static=False, - name='SetRecognitionResults', - params=[Param(datatype='int', name='sessionId'), - Param(datatype='String[]', name='results')], - java_class_name=None, - type='function'), - NativeMethod(return_type='long', static=False, - name='AddBookmarkFromAPI', - params=[Param(datatype='int', - name='nativeChromeBrowserProvider'), - Param(datatype='String', - name='url'), - Param(datatype='Long', - name='created'), - Param(datatype='Boolean', - name='isBookmark'), - Param(datatype='Long', - name='date'), - Param(datatype='byte[]', - name='favicon'), - Param(datatype='String', - name='title'), - Param(datatype='Integer', - name='visits')], - java_class_name=None, - type='method', - p0_type='ChromeBrowserProvider'), - NativeMethod(return_type='int', static=False, - name='FindAll', - params=[Param(datatype='String', - name='find')], - java_class_name=None, - type='function'), - NativeMethod(return_type='OnFrameAvailableListener', static=True, - name='GetInnerClass', - params=[], - java_class_name=None, - type='function'), - NativeMethod(return_type='Bitmap', - static=False, - name='QueryBitmap', - params=[Param(datatype='int', - name='nativeChromeBrowserProvider'), - Param(datatype='String[]', - name='projection'), - Param(datatype='String', - name='selection'), - Param(datatype='String[]', - name='selectionArgs'), - Param(datatype='String', - name='sortOrder'), - ], - java_class_name=None, - type='method', - p0_type='ChromeBrowserProvider'), - NativeMethod(return_type='void', static=False, - name='GotOrientation', - params=[Param(datatype='int', - name='nativeDataFetcherImplAndroid'), - Param(datatype='double', - name='alpha'), - Param(datatype='double', - name='beta'), - Param(datatype='double', - name='gamma'), - ], - java_class_name=None, - type='method', - p0_type='content::DataFetcherImplAndroid'), - NativeMethod(return_type='Throwable', static=True, - name='MessWithJavaException', - params=[Param(datatype='Throwable', name='e')], - java_class_name=None, - type='function') - ] - self.assertListEquals(golden_natives, natives) - h1 = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', - natives, [], [], jni_params, - TestOptions()) - self.assertGoldenTextEquals(h1.GetContent()) - h2 = jni_registration_generator.HeaderGenerator( - '', 'org/chromium/TestJni', natives, jni_params, True) - content = h2.Generate() - for k in jni_registration_generator.MERGEABLE_KEYS: - content[k] = content.get(k, '') - - self.assertGoldenTextEquals( - jni_registration_generator.CreateFromDict(content), - suffix='Registrations') - - - def testInnerClassNatives(self): - test_data = """ - class MyInnerClass { - @NativeCall("MyInnerClass") - private native int nativeInit(); - } - """ - natives = jni_generator.ExtractNatives(test_data, 'int') - golden_natives = [ - NativeMethod(return_type='int', static=False, - name='Init', params=[], - java_class_name='MyInnerClass', - type='function') - ] - self.assertListEquals(golden_natives, natives) - jni_params = jni_generator.JniParams('') - h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', - natives, [], [], jni_params, - TestOptions()) - self.assertGoldenTextEquals(h.GetContent()) - - def testInnerClassNativesMultiple(self): - test_data = """ - class MyInnerClass { - @NativeCall("MyInnerClass") - private native int nativeInit(); - } - class MyOtherInnerClass { - @NativeCall("MyOtherInnerClass") - private native int nativeInit(); - } - """ - natives = jni_generator.ExtractNatives(test_data, 'int') - golden_natives = [ - NativeMethod(return_type='int', static=False, - name='Init', params=[], - java_class_name='MyInnerClass', - type='function'), - NativeMethod(return_type='int', static=False, - name='Init', params=[], - java_class_name='MyOtherInnerClass', - type='function') - ] - self.assertListEquals(golden_natives, natives) - jni_params = jni_generator.JniParams('') - h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', - natives, [], [], jni_params, - TestOptions()) - self.assertGoldenTextEquals(h.GetContent()) - - def testInnerClassNativesBothInnerAndOuter(self): - test_data = """ - class MyOuterClass { - private native int nativeInit(); - class MyOtherInnerClass { - @NativeCall("MyOtherInnerClass") - private native int nativeInit(); - } - } - """ - natives = jni_generator.ExtractNatives(test_data, 'int') - golden_natives = [ - NativeMethod(return_type='int', static=False, - name='Init', params=[], - java_class_name=None, - type='function'), - NativeMethod(return_type='int', static=False, - name='Init', params=[], - java_class_name='MyOtherInnerClass', - type='function') - ] - self.assertListEquals(golden_natives, natives) - jni_params = jni_generator.JniParams('') - h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', - natives, [], [], jni_params, - TestOptions()) - self.assertGoldenTextEquals(h.GetContent()) - - h2 = jni_registration_generator.HeaderGenerator( - '', 'org/chromium/TestJni', natives, jni_params, True) - content = h2.Generate() - for k in jni_registration_generator.MERGEABLE_KEYS: - content[k] = content.get(k, '') - - self.assertGoldenTextEquals( - jni_registration_generator.CreateFromDict(content), - suffix='Registrations') - - def testCalledByNatives(self): - test_data = """" - import android.graphics.Bitmap; - import android.view.View; - import java.io.InputStream; - import java.util.List; - - class InnerClass {} - - @CalledByNative - @SomeOtherA - @SomeOtherB - public InnerClass showConfirmInfoBar(int nativeInfoBar, - String buttonOk, String buttonCancel, String title, Bitmap icon) { - InfoBar infobar = new ConfirmInfoBar(nativeInfoBar, mContext, - buttonOk, buttonCancel, - title, icon); - return infobar; - } - @CalledByNative - InnerClass showAutoLoginInfoBar(int nativeInfoBar, - String realm, String account, String args) { - AutoLoginInfoBar infobar = new AutoLoginInfoBar(nativeInfoBar, mContext, - realm, account, args); - if (infobar.displayedAccountCount() == 0) - infobar = null; - return infobar; - } - @CalledByNative("InfoBar") - void dismiss(); - @SuppressWarnings("unused") - @CalledByNative - private static boolean shouldShowAutoLogin(View view, - String realm, String account, String args) { - AccountManagerContainer accountManagerContainer = - new AccountManagerContainer((Activity)contentView.getContext(), - realm, account, args); - String[] logins = accountManagerContainer.getAccountLogins(null); - return logins.length != 0; - } - @CalledByNative - static InputStream openUrl(String url) { - return null; - } - @CalledByNative - private void activateHardwareAcceleration(final boolean activated, - final int iPid, final int iType, - final int iPrimaryID, final int iSecondaryID) { - if (!activated) { - return - } - } - @CalledByNative - public static @Status int updateStatus(@Status int status) { - return getAndUpdateStatus(status); - } - @CalledByNativeUnchecked - private void uncheckedCall(int iParam); - - @CalledByNative - public byte[] returnByteArray(); - - @CalledByNative - public boolean[] returnBooleanArray(); - - @CalledByNative - public char[] returnCharArray(); - - @CalledByNative - public short[] returnShortArray(); - - @CalledByNative - public int[] returnIntArray(); - - @CalledByNative - public long[] returnLongArray(); - - @CalledByNative - public double[] returnDoubleArray(); - - @CalledByNative - public Object[] returnObjectArray(); - - @CalledByNative - public byte[][] returnArrayOfByteArray(); - - @CalledByNative - public Bitmap.CompressFormat getCompressFormat(); - - @CalledByNative - public List getCompressFormatList(); - """ - jni_params = jni_generator.JniParams('org/chromium/Foo') - jni_params.ExtractImportsAndInnerClasses(test_data) - called_by_natives = jni_generator.ExtractCalledByNatives(jni_params, - test_data) - golden_called_by_natives = [ - CalledByNative( - return_type='InnerClass', - system_class=False, - static=False, - name='showConfirmInfoBar', - method_id_var_name='showConfirmInfoBar', - java_class_name='', - params=[Param(datatype='int', name='nativeInfoBar'), - Param(datatype='String', name='buttonOk'), - Param(datatype='String', name='buttonCancel'), - Param(datatype='String', name='title'), - Param(datatype='Bitmap', name='icon')], - env_call=('Object', ''), - unchecked=False, - ), - CalledByNative( - return_type='InnerClass', - system_class=False, - static=False, - name='showAutoLoginInfoBar', - method_id_var_name='showAutoLoginInfoBar', - java_class_name='', - params=[Param(datatype='int', name='nativeInfoBar'), - Param(datatype='String', name='realm'), - Param(datatype='String', name='account'), - Param(datatype='String', name='args')], - env_call=('Object', ''), - unchecked=False, - ), - CalledByNative( - return_type='void', - system_class=False, - static=False, - name='dismiss', - method_id_var_name='dismiss', - java_class_name='InfoBar', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='boolean', - system_class=False, - static=True, - name='shouldShowAutoLogin', - method_id_var_name='shouldShowAutoLogin', - java_class_name='', - params=[Param(datatype='View', name='view'), - Param(datatype='String', name='realm'), - Param(datatype='String', name='account'), - Param(datatype='String', name='args')], - env_call=('Boolean', ''), - unchecked=False, - ), - CalledByNative( - return_type='InputStream', - system_class=False, - static=True, - name='openUrl', - method_id_var_name='openUrl', - java_class_name='', - params=[Param(datatype='String', name='url')], - env_call=('Object', ''), - unchecked=False, - ), - CalledByNative( - return_type='void', - system_class=False, - static=False, - name='activateHardwareAcceleration', - method_id_var_name='activateHardwareAcceleration', - java_class_name='', - params=[Param(datatype='boolean', name='activated'), - Param(datatype='int', name='iPid'), - Param(datatype='int', name='iType'), - Param(datatype='int', name='iPrimaryID'), - Param(datatype='int', name='iSecondaryID'), - ], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='int', - system_class=False, - static=True, - name='updateStatus', - method_id_var_name='updateStatus', - java_class_name='', - params=[Param(datatype='int', name='status')], - env_call=('Integer', ''), - unchecked=False, - ), - CalledByNative( - return_type='void', - system_class=False, - static=False, - name='uncheckedCall', - method_id_var_name='uncheckedCall', - java_class_name='', - params=[Param(datatype='int', name='iParam')], - env_call=('Void', ''), - unchecked=True, - ), - CalledByNative( - return_type='byte[]', - system_class=False, - static=False, - name='returnByteArray', - method_id_var_name='returnByteArray', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='boolean[]', - system_class=False, - static=False, - name='returnBooleanArray', - method_id_var_name='returnBooleanArray', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='char[]', - system_class=False, - static=False, - name='returnCharArray', - method_id_var_name='returnCharArray', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='short[]', - system_class=False, - static=False, - name='returnShortArray', - method_id_var_name='returnShortArray', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='int[]', - system_class=False, - static=False, - name='returnIntArray', - method_id_var_name='returnIntArray', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='long[]', - system_class=False, - static=False, - name='returnLongArray', - method_id_var_name='returnLongArray', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='double[]', - system_class=False, - static=False, - name='returnDoubleArray', - method_id_var_name='returnDoubleArray', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='Object[]', - system_class=False, - static=False, - name='returnObjectArray', - method_id_var_name='returnObjectArray', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='byte[][]', - system_class=False, - static=False, - name='returnArrayOfByteArray', - method_id_var_name='returnArrayOfByteArray', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='Bitmap.CompressFormat', - system_class=False, - static=False, - name='getCompressFormat', - method_id_var_name='getCompressFormat', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - CalledByNative( - return_type='List', - system_class=False, - static=False, - name='getCompressFormatList', - method_id_var_name='getCompressFormatList', - java_class_name='', - params=[], - env_call=('Void', ''), - unchecked=False, - ), - ] - self.assertListEquals(golden_called_by_natives, called_by_natives) - h = jni_generator.InlHeaderFileGenerator( - '', 'org/chromium/TestJni', [], called_by_natives, [], jni_params, - TestOptions()) - self.assertGoldenTextEquals(h.GetContent()) - - def testCalledByNativeParseError(self): - try: - jni_params = jni_generator.JniParams('') - jni_generator.ExtractCalledByNatives(jni_params, """ -@CalledByNative -public static int foo(); // This one is fine - -@CalledByNative -scooby doo -""") - self.fail('Expected a ParseError') - except jni_generator.ParseError, e: - self.assertEquals(('@CalledByNative', 'scooby doo'), e.context_lines) - - def testFullyQualifiedClassName(self): - contents = """ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.content.browser; - -import org.chromium.base.BuildInfo; -""" - self.assertEquals('org/chromium/content/browser/Foo', - jni_generator.ExtractFullyQualifiedJavaClassName( - 'org/chromium/content/browser/Foo.java', contents)) - self.assertEquals('org/chromium/content/browser/Foo', - jni_generator.ExtractFullyQualifiedJavaClassName( - 'frameworks/Foo.java', contents)) - self.assertRaises(SyntaxError, - jni_generator.ExtractFullyQualifiedJavaClassName, - 'com/foo/Bar', 'no PACKAGE line') - - def testMethodNameMangling(self): - jni_params = jni_generator.JniParams('') - self.assertEquals('closeV', - jni_generator.GetMangledMethodName(jni_params, 'close', [], 'void')) - self.assertEquals('readI_AB_I_I', - jni_generator.GetMangledMethodName(jni_params, 'read', - [Param(name='p1', - datatype='byte[]'), - Param(name='p2', - datatype='int'), - Param(name='p3', - datatype='int'),], - 'int')) - self.assertEquals('openJIIS_JLS', - jni_generator.GetMangledMethodName(jni_params, 'open', - [Param(name='p1', - datatype='java/lang/String'),], - 'java/io/InputStream')) - - def testFromJavaPGenerics(self): - contents = """ -public abstract class java.util.HashSet extends java.util.AbstractSet - implements java.util.Set, java.lang.Cloneable, java.io.Serializable { - public void dummy(); - Signature: ()V - public java.lang.Class getClass(); - Signature: ()Ljava/lang/Class<*>; -} -""" - jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'), - TestOptions()) - self.assertEquals(2, len(jni_from_javap.called_by_natives)) - self.assertGoldenTextEquals(jni_from_javap.GetContent()) - - def testSnippnetJavap6_7_8(self): - content_javap6 = """ -public class java.util.HashSet { -public boolean add(java.lang.Object); - Signature: (Ljava/lang/Object;)Z -} -""" - - content_javap7 = """ -public class java.util.HashSet { -public boolean add(E); - Signature: (Ljava/lang/Object;)Z -} -""" - - content_javap8 = """ -public class java.util.HashSet { - public boolean add(E); - descriptor: (Ljava/lang/Object;)Z -} -""" - - jni_from_javap6 = jni_generator.JNIFromJavaP(content_javap6.split('\n'), - TestOptions()) - jni_from_javap7 = jni_generator.JNIFromJavaP(content_javap7.split('\n'), - TestOptions()) - jni_from_javap8 = jni_generator.JNIFromJavaP(content_javap8.split('\n'), - TestOptions()) - self.assertTrue(jni_from_javap6.GetContent()) - self.assertTrue(jni_from_javap7.GetContent()) - self.assertTrue(jni_from_javap8.GetContent()) - # Ensure the javap7 is correctly parsed and uses the Signature field rather - # than the "E" parameter. - self.assertTextEquals(jni_from_javap6.GetContent(), - jni_from_javap7.GetContent()) - # Ensure the javap8 is correctly parsed and uses the descriptor field. - self.assertTextEquals(jni_from_javap7.GetContent(), - jni_from_javap8.GetContent()) - - def testFromJavaP(self): - contents = self._ReadGoldenFile(os.path.join(os.path.dirname(sys.argv[0]), - 'testInputStream.javap')) - jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'), - TestOptions()) - self.assertEquals(10, len(jni_from_javap.called_by_natives)) - self.assertGoldenTextEquals(jni_from_javap.GetContent()) - - def testConstantsFromJavaP(self): - for f in ['testMotionEvent.javap', 'testMotionEvent.javap7']: - contents = self._ReadGoldenFile(os.path.join(os.path.dirname(sys.argv[0]), - f)) - jni_from_javap = jni_generator.JNIFromJavaP(contents.split('\n'), - TestOptions()) - self.assertEquals(86, len(jni_from_javap.called_by_natives)) - self.assertGoldenTextEquals(jni_from_javap.GetContent()) - - def testREForNatives(self): - # We should not match "native SyncSetupFlow" inside the comment. - test_data = """ - /** - * Invoked when the setup process is complete so we can disconnect from the - * native-side SyncSetupFlowHandler. - */ - public void destroy() { - Log.v(TAG, "Destroying native SyncSetupFlow"); - if (mNativeSyncSetupFlow != 0) { - nativeSyncSetupEnded(mNativeSyncSetupFlow); - mNativeSyncSetupFlow = 0; - } - } - private native void nativeSyncSetupEnded( - int nativeAndroidSyncSetupFlowHandler); - """ - jni_from_java = jni_generator.JNIFromJavaSource( - test_data, 'foo/bar', TestOptions()) - - def testRaisesOnNonJNIMethod(self): - test_data = """ - class MyInnerClass { - private int Foo(int p0) { - } - } - """ - self.assertRaises(SyntaxError, - jni_generator.JNIFromJavaSource, - test_data, 'foo/bar', TestOptions()) - - def testJniSelfDocumentingExample(self): - script_dir = os.path.dirname(sys.argv[0]) - content = file(os.path.join(script_dir, - 'java/src/org/chromium/example/jni_generator/SampleForTests.java') - ).read() - golden_file = os.path.join(script_dir, 'SampleForTests_jni.golden') - golden_content = file(golden_file).read() - jni_from_java = jni_generator.JNIFromJavaSource( - content, 'org/chromium/example/jni_generator/SampleForTests', - TestOptions()) - generated_text = jni_from_java.GetContent() - if not self.compareText(golden_content, generated_text): - if os.environ.get(REBASELINE_ENV): - with file(golden_file, 'w') as f: - f.write(generated_text) - return - self.fail('testJniSelfDocumentingExample') - - def testNoWrappingPreprocessorLines(self): - test_data = """ - package com.google.lookhowextremelylongiam.snarf.icankeepthisupallday; - - class ReallyLongClassNamesAreAllTheRage { - private static native int nativeTest(); - } - """ - jni_from_java = jni_generator.JNIFromJavaSource( - test_data, ('com/google/lookhowextremelylongiam/snarf/' - 'icankeepthisupallday/ReallyLongClassNamesAreAllTheRage'), - TestOptions()) - jni_lines = jni_from_java.GetContent().split('\n') - line = filter(lambda line: line.lstrip().startswith('#ifndef'), - jni_lines)[0] - self.assertTrue(len(line) > 80, - ('Expected #ifndef line to be > 80 chars: ', line)) - - def testImports(self): - import_header = """ -// 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. - -package org.chromium.content.app; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.graphics.SurfaceTexture; -import android.os.Bundle; -import android.os.IBinder; -import android.os.ParcelFileDescriptor; -import android.os.Process; -import android.os.RemoteException; -import android.util.Log; -import android.view.Surface; - -import java.util.ArrayList; - -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.content.app.ContentMain; -import org.chromium.content.browser.SandboxedProcessConnection; -import org.chromium.content.common.ISandboxedProcessCallback; -import org.chromium.content.common.ISandboxedProcessService; -import org.chromium.content.common.WillNotRaise.AnException; -import org.chromium.content.common.WillRaise.AnException; - -import static org.chromium.Bar.Zoo; - -class Foo { - public static class BookmarkNode implements Parcelable { - } - public interface PasswordListObserver { - } -} - """ - jni_params = jni_generator.JniParams('org/chromium/content/app/Foo') - jni_params.ExtractImportsAndInnerClasses(import_header) - self.assertTrue('Lorg/chromium/content/common/ISandboxedProcessService' in - jni_params._imports) - self.assertTrue('Lorg/chromium/Bar/Zoo' in - jni_params._imports) - self.assertTrue('Lorg/chromium/content/app/Foo$BookmarkNode' in - jni_params._inner_classes) - self.assertTrue('Lorg/chromium/content/app/Foo$PasswordListObserver' in - jni_params._inner_classes) - self.assertEquals('Lorg/chromium/content/app/ContentMain$Inner;', - jni_params.JavaToJni('ContentMain.Inner')) - self.assertRaises(SyntaxError, - jni_params.JavaToJni, 'AnException') - - def testJniParamsJavaToJni(self): - jni_params = jni_generator.JniParams('') - self.assertTextEquals('I', jni_params.JavaToJni('int')) - self.assertTextEquals('[B', jni_params.JavaToJni('byte[]')) - self.assertTextEquals( - '[Ljava/nio/ByteBuffer;', jni_params.JavaToJni('java/nio/ByteBuffer[]')) - - def testNativesLong(self): - test_options = TestOptions() - test_options.ptr_type = 'long' - test_data = """" - private native void nativeDestroy(long nativeChromeBrowserProvider); - """ - jni_params = jni_generator.JniParams('') - jni_params.ExtractImportsAndInnerClasses(test_data) - natives = jni_generator.ExtractNatives(test_data, test_options.ptr_type) - golden_natives = [ - NativeMethod(return_type='void', static=False, name='Destroy', - params=[Param(datatype='long', - name='nativeChromeBrowserProvider')], - java_class_name=None, - type='method', - p0_type='ChromeBrowserProvider', - ptr_type=test_options.ptr_type), - ] - self.assertListEquals(golden_natives, natives) - h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', - natives, [], [], jni_params, - test_options) - self.assertGoldenTextEquals(h.GetContent()) - - def testMainDexAnnotation(self): - mainDexEntries = [ - '@MainDex public class Test {', - '@MainDex public class Test{', - """@MainDex - public class Test { - """, - """@MainDex public class Test - { - """, - '@MainDex /* This class is a test */ public class Test {', - '@MainDex public class Test implements java.io.Serializable {', - '@MainDex public class Test implements java.io.Serializable, Bidule {', - '@MainDex public class Test extends BaseTest {', - """@MainDex - public class Test extends BaseTest implements Bidule { - """, - """@MainDex - public class Test extends BaseTest implements Bidule, Machin, Chose { - """, - """@MainDex - public class Test implements Testable { - """, - '@MainDex public class Test implements Testable {', - '@a.B @MainDex @C public class Test extends Testable {', - """public class Test extends Testable { - @MainDex void func() {} - """, - ] - for entry in mainDexEntries: - self.assertEquals(True, IsMainDexJavaClass(entry), entry) - - def testNoMainDexAnnotation(self): - noMainDexEntries = [ - 'public class Test {', - '@NotMainDex public class Test {', - '// @MainDex public class Test {', - '/* @MainDex */ public class Test {', - 'public class Test implements java.io.Serializable {', - '@MainDexNot public class Test {', - 'public class Test extends BaseTest {' - ] - for entry in noMainDexEntries: - self.assertEquals(False, IsMainDexJavaClass(entry)) - - def testNativeExportsOnlyOption(self): - test_data = """ - package org.chromium.example.jni_generator; - - /** The pointer to the native Test. */ - long nativeTest; - - class Test { - private static native int nativeStaticMethod(long nativeTest, int arg1); - private native int nativeMethod(long nativeTest, int arg1); - @CalledByNative - private void testMethodWithParam(int iParam); - @CalledByNative - private String testMethodWithParamAndReturn(int iParam); - @CalledByNative - private static int testStaticMethodWithParam(int iParam); - @CalledByNative - private static double testMethodWithNoParam(); - @CalledByNative - private static String testStaticMethodWithNoParam(); - - class MyInnerClass { - @NativeCall("MyInnerClass") - private native int nativeInit(); - } - class MyOtherInnerClass { - @NativeCall("MyOtherInnerClass") - private native int nativeInit(); - } - } - """ - options = TestOptions() - options.native_exports_optional = False - jni_from_java = jni_generator.JNIFromJavaSource( - test_data, 'org/chromium/example/jni_generator/SampleForTests', options) - self.assertGoldenTextEquals(jni_from_java.GetContent()) - - def testOuterInnerRaises(self): - test_data = """ - package org.chromium.media; - - @CalledByNative - static int getCaptureFormatWidth(VideoCapture.CaptureFormat format) { - return format.getWidth(); - } - """ - def willRaise(): - jni_generator.JNIFromJavaSource( - test_data, - 'org/chromium/media/VideoCaptureFactory', - TestOptions()) - self.assertRaises(SyntaxError, willRaise) - - def testSingleJNIAdditionalImport(self): - test_data = """ - package org.chromium.foo; - - @JNIAdditionalImport(Bar.class) - class Foo { - - @CalledByNative - private static void calledByNative(Bar.Callback callback) { - } - - private static native void nativeDoSomething(Bar.Callback callback); - } - """ - jni_from_java = jni_generator.JNIFromJavaSource(test_data, - 'org/chromium/foo/Foo', - TestOptions()) - self.assertGoldenTextEquals(jni_from_java.GetContent()) - - def testMultipleJNIAdditionalImport(self): - test_data = """ - package org.chromium.foo; - - @JNIAdditionalImport({Bar1.class, Bar2.class}) - class Foo { - - @CalledByNative - private static void calledByNative(Bar1.Callback callback1, - Bar2.Callback callback2) { - } - - private static native void nativeDoSomething(Bar1.Callback callback1, - Bar2.Callback callback2); - } - """ - jni_from_java = jni_generator.JNIFromJavaSource(test_data, - 'org/chromium/foo/Foo', - TestOptions()) - self.assertGoldenTextEquals(jni_from_java.GetContent()) - - def testTracing(self): - test_data = """ - package org.chromium.foo; - - @JNINamespace("org::chromium_foo") - class Foo { - - @CalledByNative - Foo(); - - @CalledByNative - void callbackFromNative(); - - native void nativeInstanceMethod(long nativeInstance); - - static native void nativeStaticMethod(); - } - """ - options_with_tracing = TestOptions() - options_with_tracing.enable_tracing = True - jni_from_java = jni_generator.JNIFromJavaSource(test_data, - 'org/chromium/foo/Foo', - options_with_tracing) - self.assertGoldenTextEquals(jni_from_java.GetContent()) - - -def TouchStamp(stamp_path): - dir_name = os.path.dirname(stamp_path) - if not os.path.isdir(dir_name): - os.makedirs(dir_name) - - with open(stamp_path, 'a'): - os.utime(stamp_path, None) - - -def main(argv): - parser = optparse.OptionParser() - parser.add_option('--stamp', help='Path to touch on success.') - parser.add_option('--verbose', action="store_true", - help='Whether to output details.') - options, _ = parser.parse_args(argv[1:]) - - test_result = unittest.main( - argv=argv[0:1], - exit=False, - verbosity=(2 if options.verbose else 1)) - - if test_result.result.wasSuccessful() and options.stamp: - TouchStamp(options.stamp) - - return not test_result.result.wasSuccessful() - - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/android/jni_generator/jni_registration_generator.py b/android/jni_generator/jni_registration_generator.py deleted file mode 100755 index dec56ee66..000000000 --- a/android/jni_generator/jni_registration_generator.py +++ /dev/null @@ -1,340 +0,0 @@ -#!/usr/bin/env python -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Generate JNI registration entry points - -Creates a header file with two static functions: RegisterMainDexNatives() and -RegisterNonMainDexNatives(). Together, these will use manual JNI registration -to register all native methods that exist within an application.""" - -import argparse -import jni_generator -import multiprocessing -import string -import sys -from util import build_utils - - -# All but FULL_CLASS_NAME, which is used only for sorting. -MERGEABLE_KEYS = [ - 'CLASS_PATH_DECLARATIONS', - 'FORWARD_DECLARATIONS', - 'JNI_NATIVE_METHOD', - 'JNI_NATIVE_METHOD_ARRAY', - 'REGISTER_MAIN_DEX_NATIVES', - 'REGISTER_NON_MAIN_DEX_NATIVES', -] - - -def GenerateJNIHeader(java_file_paths, output_file, args): - """Generate a header file including two registration functions. - - Forward declares all JNI registration functions created by jni_generator.py. - Calls the functions in RegisterMainDexNatives() if they are main dex. And - calls them in RegisterNonMainDexNatives() if they are non-main dex. - - Args: - java_file_paths: A list of java file paths. - output_file: A relative path to output file. - args: All input arguments. - """ - # Without multiprocessing, script takes ~13 seconds for chrome_public_apk - # on a z620. With multiprocessing, takes ~2 seconds. - pool = multiprocessing.Pool() - paths = (p for p in java_file_paths if p not in args.no_register_java) - results = [d for d in pool.imap_unordered(_DictForPath, paths) if d] - pool.close() - - # Sort to make output deterministic. - results.sort(key=lambda d: d['FULL_CLASS_NAME']) - - combined_dict = {} - for key in MERGEABLE_KEYS: - combined_dict[key] = ''.join(d.get(key, '') for d in results) - - header_content = CreateFromDict(combined_dict) - if output_file: - jni_generator.WriteOutput(output_file, header_content) - else: - print header_content - - -def _DictForPath(path): - with open(path) as f: - contents = jni_generator.RemoveComments(f.read()) - natives = jni_generator.ExtractNatives(contents, 'long') - if len(natives) == 0: - return None - namespace = jni_generator.ExtractJNINamespace(contents) - fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName( - path, contents) - jni_params = jni_generator.JniParams(fully_qualified_class) - jni_params.ExtractImportsAndInnerClasses(contents) - main_dex = jni_generator.IsMainDexJavaClass(contents) - header_generator = HeaderGenerator( - namespace, fully_qualified_class, natives, jni_params, main_dex) - return header_generator.Generate() - - -def CreateFromDict(registration_dict): - """Returns the content of the header file.""" - - template = string.Template("""\ -// Copyright 2017 The Chromium 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 autogenerated by -// base/android/jni_generator/jni_registration_generator.py -// Please do not change its content. - -#ifndef HEADER_GUARD -#define HEADER_GUARD - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" -#include "base/android/jni_int_wrapper.h" - - -// Step 1: Forward declarations (classes). -${CLASS_PATH_DECLARATIONS} - -// Step 2: Forward declarations (methods). - -${FORWARD_DECLARATIONS} - -// Step 3: Method declarations. - -${JNI_NATIVE_METHOD_ARRAY} -${JNI_NATIVE_METHOD} -// Step 4: Main dex and non-main dex registration functions. - -bool RegisterMainDexNatives(JNIEnv* env) { -${REGISTER_MAIN_DEX_NATIVES} - return true; -} - -bool RegisterNonMainDexNatives(JNIEnv* env) { -${REGISTER_NON_MAIN_DEX_NATIVES} - return true; -} - -#endif // HEADER_GUARD -""") - if len(registration_dict['FORWARD_DECLARATIONS']) == 0: - return '' - - return template.substitute(registration_dict) - - -class HeaderGenerator(object): - """Generates an inline header file for JNI registration.""" - - def __init__(self, namespace, fully_qualified_class, natives, jni_params, - main_dex): - self.namespace = namespace - self.natives = natives - self.fully_qualified_class = fully_qualified_class - self.jni_params = jni_params - self.class_name = self.fully_qualified_class.split('/')[-1] - self.main_dex = main_dex - self.helper = jni_generator.HeaderFileGeneratorHelper( - self.class_name, fully_qualified_class) - self.registration_dict = None - - def Generate(self): - self.registration_dict = {'FULL_CLASS_NAME': self.fully_qualified_class} - self._AddClassPathDeclarations() - self._AddForwardDeclaration() - self._AddJNINativeMethodsArrays() - self._AddRegisterNativesCalls() - self._AddRegisterNativesFunctions() - return self.registration_dict - - def _SetDictValue(self, key, value): - self.registration_dict[key] = jni_generator.WrapOutput(value) - - def _AddClassPathDeclarations(self): - classes = self.helper.GetUniqueClasses(self.natives) - self._SetDictValue('CLASS_PATH_DECLARATIONS', - self.helper.GetClassPathLines(classes, declare_only=True)) - - def _AddForwardDeclaration(self): - """Add the content of the forward declaration to the dictionary.""" - template = string.Template("""\ -JNI_GENERATOR_EXPORT ${RETURN} ${STUB_NAME}( - JNIEnv* env, - ${PARAMS_IN_STUB}); -""") - forward_declaration = '' - for native in self.natives: - value = { - 'RETURN': jni_generator.JavaDataTypeToC(native.return_type), - 'STUB_NAME': self.helper.GetStubName(native), - 'PARAMS_IN_STUB': jni_generator.GetParamsInStub(native), - } - forward_declaration += template.substitute(value) - self._SetDictValue('FORWARD_DECLARATIONS', forward_declaration) - - def _AddRegisterNativesCalls(self): - """Add the body of the RegisterNativesImpl method to the dictionary.""" - template = string.Template("""\ - if (!${REGISTER_NAME}(env)) - return false; -""") - value = { - 'REGISTER_NAME': - jni_generator.GetRegistrationFunctionName( - self.fully_qualified_class) - } - register_body = template.substitute(value) - if self.main_dex: - self._SetDictValue('REGISTER_MAIN_DEX_NATIVES', register_body) - else: - self._SetDictValue('REGISTER_NON_MAIN_DEX_NATIVES', register_body) - - def _AddJNINativeMethodsArrays(self): - """Returns the implementation of the array of native methods.""" - template = string.Template("""\ -static const JNINativeMethod kMethods_${JAVA_CLASS}[] = { -${KMETHODS} -}; - -""") - open_namespace = '' - close_namespace = '' - if self.namespace: - parts = self.namespace.split('::') - all_namespaces = ['namespace %s {' % ns for ns in parts] - open_namespace = '\n'.join(all_namespaces) + '\n' - all_namespaces = ['} // namespace %s' % ns for ns in parts] - all_namespaces.reverse() - close_namespace = '\n'.join(all_namespaces) + '\n\n' - - body = self._SubstituteNativeMethods(template) - self._SetDictValue('JNI_NATIVE_METHOD_ARRAY', - ''.join((open_namespace, body, close_namespace))) - - def _GetKMethodsString(self, clazz): - ret = [] - for native in self.natives: - if (native.java_class_name == clazz or - (not native.java_class_name and clazz == self.class_name)): - ret += [self._GetKMethodArrayEntry(native)] - return '\n'.join(ret) - - def _GetKMethodArrayEntry(self, native): - template = string.Template(' { "native${NAME}", ${JNI_SIGNATURE}, ' + - 'reinterpret_cast(${STUB_NAME}) },') - values = { - 'NAME': native.name, - 'JNI_SIGNATURE': self.jni_params.Signature( - native.params, native.return_type), - 'STUB_NAME': self.helper.GetStubName(native) - } - return template.substitute(values) - - def _SubstituteNativeMethods(self, template): - """Substitutes NAMESPACE, JAVA_CLASS and KMETHODS in the provided - template.""" - ret = [] - all_classes = self.helper.GetUniqueClasses(self.natives) - all_classes[self.class_name] = self.fully_qualified_class - for clazz, full_clazz in all_classes.iteritems(): - kmethods = self._GetKMethodsString(clazz) - namespace_str = '' - if self.namespace: - namespace_str = self.namespace + '::' - if kmethods: - values = {'NAMESPACE': namespace_str, - 'JAVA_CLASS': jni_generator.GetBinaryClassName(full_clazz), - 'KMETHODS': kmethods} - ret += [template.substitute(values)] - if not ret: return '' - return '\n'.join(ret) - - def GetJNINativeMethodsString(self): - """Returns the implementation of the array of native methods.""" - template = string.Template("""\ -static const JNINativeMethod kMethods_${JAVA_CLASS}[] = { -${KMETHODS} - -}; -""") - return self._SubstituteNativeMethods(template) - - def _AddRegisterNativesFunctions(self): - """Returns the code for RegisterNatives.""" - natives = self._GetRegisterNativesImplString() - if not natives: - return '' - template = string.Template("""\ -JNI_REGISTRATION_EXPORT bool ${REGISTER_NAME}(JNIEnv* env) { -${NATIVES}\ - return true; -} - -""") - values = { - 'REGISTER_NAME': jni_generator.GetRegistrationFunctionName( - self.fully_qualified_class), - 'NATIVES': natives - } - self._SetDictValue('JNI_NATIVE_METHOD', template.substitute(values)) - - def _GetRegisterNativesImplString(self): - """Returns the shared implementation for RegisterNatives.""" - template = string.Template("""\ - const int kMethods_${JAVA_CLASS}Size = - arraysize(${NAMESPACE}kMethods_${JAVA_CLASS}); - if (env->RegisterNatives( - ${JAVA_CLASS}_clazz(env), - ${NAMESPACE}kMethods_${JAVA_CLASS}, - kMethods_${JAVA_CLASS}Size) < 0) { - jni_generator::HandleRegistrationError(env, - ${JAVA_CLASS}_clazz(env), - __FILE__); - return false; - } - -""") - return self._SubstituteNativeMethods(template) - - -def main(argv): - arg_parser = argparse.ArgumentParser() - build_utils.AddDepfileOption(arg_parser) - - arg_parser.add_argument('--sources_files', - help='A list of .sources files which contain Java ' - 'file paths. Must be used with --output.') - arg_parser.add_argument('--output', - help='The output file path.') - arg_parser.add_argument('--no_register_java', - help='A list of Java files which should be ignored ' - 'by the parser.') - args = arg_parser.parse_args(build_utils.ExpandFileArgs(argv[1:])) - args.sources_files = build_utils.ParseGnList(args.sources_files) - - if not args.sources_files: - print '\nError: Must specify --sources_files.' - return 1 - - java_file_paths = [] - for f in args.sources_files: - # java_file_paths stores each Java file path as a string. - java_file_paths += build_utils.ReadSourcesList(f) - output_file = args.output - GenerateJNIHeader(java_file_paths, output_file, args) - - if args.depfile: - build_utils.WriteDepfile(args.depfile, output_file, - args.sources_files + java_file_paths) - - -if __name__ == '__main__': - sys.exit(main(sys.argv)) diff --git a/android/jni_generator/sample_entry_point.cc b/android/jni_generator/sample_entry_point.cc deleted file mode 100644 index 86f7e4808..000000000 --- a/android/jni_generator/sample_entry_point.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2017 The Chromium 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/jni_android.h" -#include "base/android/jni_generator/sample_jni_registration.h" -#include "base/android/jni_utils.h" - -// This is called by the VM when the shared library is first loaded. -JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { - // By default, all JNI methods are registered. However, since render processes - // don't need very much Java code, we enable selective JNI registration on the - // Java side and only register a subset of JNI methods. - base::android::InitVM(vm); - JNIEnv* env = base::android::AttachCurrentThread(); - - if (!base::android::IsSelectiveJniRegistrationEnabled(env)) { - if (!RegisterNonMainDexNatives(env)) { - return -1; - } - } - - if (!RegisterMainDexNatives(env)) { - return -1; - } - return JNI_VERSION_1_4; -} diff --git a/android/jni_generator/sample_for_tests.cc b/android/jni_generator/sample_for_tests.cc deleted file mode 100644 index 890103eea..000000000 --- a/android/jni_generator/sample_for_tests.cc +++ /dev/null @@ -1,154 +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 - -#include "base/android/jni_generator/sample_for_tests.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/android/scoped_java_ref.h" -// Generated file for JNI bindings from C++ to Java @CalledByNative methods. -// Only to be included in one .cc file. -// Name is based on the java file name: *.java -> jni/*_jni.h -#include "jni/SampleForTests_jni.h" // Generated by JNI. - -using base::android::AttachCurrentThread; -using base::android::ConvertJavaStringToUTF8; -using base::android::ConvertUTF8ToJavaString; -using base::android::ScopedJavaLocalRef; - -namespace base { -namespace android { - -jdouble CPPClass::InnerClass::MethodOtherP0( - JNIEnv* env, - const JavaParamRef& caller) { - return 0.0; -} - -CPPClass::CPPClass() { -} - -CPPClass::~CPPClass() { -} - -// static -void CPPClass::Destroy(JNIEnv* env, const JavaParamRef& caller) { - delete this; -} - -jint CPPClass::Method(JNIEnv* env, const JavaParamRef& caller) { - return 0; -} - -void CPPClass::AddStructB(JNIEnv* env, - const JavaParamRef& caller, - const JavaParamRef& structb) { - long key = Java_InnerStructB_getKey(env, structb); - std::string value = - ConvertJavaStringToUTF8(env, Java_InnerStructB_getValue(env, structb)); - map_[key] = value; -} - -void CPPClass::IterateAndDoSomethingWithStructB( - JNIEnv* env, - const JavaParamRef& caller) { - // Iterate over the elements and do something with them. - for (std::map::const_iterator it = map_.begin(); - it != map_.end(); ++it) { - long key = it->first; - std::string value = it->second; - std::cout << key << value; - } - map_.clear(); -} - -ScopedJavaLocalRef CPPClass::ReturnAString( - JNIEnv* env, - const JavaParamRef& caller) { - return ConvertUTF8ToJavaString(env, "test"); -} - -// Static free functions declared and called directly from java. -static jlong JNI_SampleForTests_Init(JNIEnv* env, - const JavaParamRef& caller, - const JavaParamRef& param) { - return 0; -} - -static jdouble JNI_SampleForTests_GetDoubleFunction( - JNIEnv*, - const JavaParamRef&) { - return 0; -} - -static jfloat JNI_SampleForTests_GetFloatFunction(JNIEnv*, - const JavaParamRef&) { - return 0; -} - -static void JNI_SampleForTests_SetNonPODDatatype(JNIEnv*, - const JavaParamRef&, - const JavaParamRef&) { -} - -static ScopedJavaLocalRef JNI_SampleForTests_GetNonPODDatatype( - JNIEnv*, - const JavaParamRef&) { - return ScopedJavaLocalRef(); -} - -static jint JNI_InnerClass_GetInnerIntFunction(JNIEnv*, - const JavaParamRef&) { - return 0; -} - -} // namespace android -} // namespace base - -int main() { - // On a regular application, you'd call AttachCurrentThread(). This sample is - // not yet linking with all the libraries. - JNIEnv* env = /* AttachCurrentThread() */ NULL; - - // This is how you call a java static method from C++. - bool foo = base::android::Java_SampleForTests_staticJavaMethod(env); - - // This is how you call a java method from C++. Note that you must have - // obtained the jobject somehow. - ScopedJavaLocalRef my_java_object; - int bar = base::android::Java_SampleForTests_javaMethod( - env, my_java_object, 1, 2); - - base::android::Java_SampleForTests_methodWithGenericParams( - env, my_java_object, nullptr, nullptr); - - // This is how you call a java constructor method from C++. - ScopedJavaLocalRef my_created_object = - base::android::Java_SampleForTests_Constructor(env, 1, 2); - - std::cout << foo << bar; - - for (int i = 0; i < 10; ++i) { - // Creates a "struct" that will then be used by the java side. - ScopedJavaLocalRef struct_a = - base::android::Java_InnerStructA_create( - env, 0, 1, ConvertUTF8ToJavaString(env, "test")); - base::android::Java_SampleForTests_addStructA(env, my_java_object, - struct_a); - } - base::android::Java_SampleForTests_iterateAndDoSomething(env, my_java_object); - base::android::Java_SampleForTests_packagePrivateJavaMethod(env, - my_java_object); - base::android::Java_SampleForTests_methodThatThrowsException(env, - my_java_object); - base::android::Java_SampleForTests_javaMethodWithAnnotatedParam( - env, my_java_object, 42); - - base::android::Java_SampleForTests_getInnerInterface(env); - base::android::Java_SampleForTests_getInnerEnum(env); - - return 0; -} diff --git a/android/jni_generator/sample_for_tests.h b/android/jni_generator/sample_for_tests.h deleted file mode 100644 index 6414158a3..000000000 --- a/android/jni_generator/sample_for_tests.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_JNI_GENERATOR_SAMPLE_FOR_TESTS_H_ -#define BASE_ANDROID_JNI_GENERATOR_SAMPLE_FOR_TESTS_H_ - -#include -#include -#include - -#include "base/android/jni_android.h" - -namespace base { -namespace android { - -// This file is used to: -// - document the best practices and guidelines on JNI usage. -// - ensure sample_for_tests_jni.h compiles and the functions declared in it -// as expected. -// -// Methods are called directly from Java. More documentation in -// SampleForTests.java. See BUILD.gn for the build rules necessary for JNI -// to be used in an APK. -// -// For C++ to access Java methods: -// - GN Build must be configured to generate bindings: -// # Add import at top of file: -// if (is_android) { -// import("//build/config/android/rules.gni") # For generate_jni(). -// } -// # ... -// # An example target that will rely on JNI: -// component("foo") { -// # ... normal sources, defines, deps. -// # For each jni generated .java -> .h header file in jni_headers -// # target there will be a single .cc file here that includes it. -// # -// # Add a dep for JNI: -// if (is_android) { -// deps += [ ":foo_jni" ] -// } -// } -// # ... -// # Create target for JNI: -// if (is_android) { -// generate_jni("jni_headers") { -// sources = [ -// "java/src/org/chromium/example/jni_generator/SampleForTests.java", -// ] -// jni_package = "foo" -// } -// android_library("java") { -// java_files = [ -// "java/src/org/chromium/example/jni_generator/SampleForTests.java", -// "java/src/org/chromium/example/jni_generator/NonJniFile.java", -// ] -// } -// } -// The build rules above are generally that that's needed when adding new -// JNI methods/files. For a full GN example, see -// base/android/jni_generator/BUILD.gn -// -// For C++ methods to be exposed to Java: -// - The Java class must be part of an android_apk target that depends on -// a generate_jni_registration target. This generate_jni_registration target -// automatically generates all necessary registration functions. The -// generated header file exposes two functions that should be called when a -// library is first loaded: -// 1) RegisterMainDexNatives() -// - Registers all methods that are used outside the browser process -// 2) RegisterNonMainDexNatives() -// - Registers all methods used in the browser process -// -class CPPClass { - public: - CPPClass(); - ~CPPClass(); - - // Java @CalledByNative methods implicitly available to C++ via the _jni.h - // file included in the .cc file. - - class InnerClass { - public: - jdouble MethodOtherP0(JNIEnv* env, - const base::android::JavaParamRef& caller); - }; - - void Destroy(JNIEnv* env, const base::android::JavaParamRef& caller); - - jint Method(JNIEnv* env, const base::android::JavaParamRef& caller); - - void AddStructB(JNIEnv* env, - const base::android::JavaParamRef& caller, - const base::android::JavaParamRef& structb); - - void IterateAndDoSomethingWithStructB( - JNIEnv* env, - const base::android::JavaParamRef& caller); - - base::android::ScopedJavaLocalRef ReturnAString( - JNIEnv* env, - const base::android::JavaParamRef& caller); - - private: - std::map map_; - - DISALLOW_COPY_AND_ASSIGN(CPPClass); -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_JNI_GENERATOR_SAMPLE_FOR_TESTS_H_ diff --git a/android/jni_generator/testCalledByNatives.golden b/android/jni_generator/testCalledByNatives.golden deleted file mode 100644 index f0673f65f..000000000 --- a/android/jni_generator/testCalledByNatives.golden +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/TestJni - -#ifndef org_chromium_TestJni_JNI -#define org_chromium_TestJni_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni[]; -const char kClassPath_org_chromium_TestJni[] = "org/chromium/TestJni"; - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni_00024InfoBar[]; -const char kClassPath_org_chromium_TestJni_00024InfoBar[] = "org/chromium/TestJni$InfoBar"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_clazz = 0; -#ifndef org_chromium_TestJni_clazz_defined -#define org_chromium_TestJni_clazz_defined -inline jclass org_chromium_TestJni_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni, - &g_org_chromium_TestJni_clazz); -} -#endif -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_00024InfoBar_clazz = 0; -#ifndef org_chromium_TestJni_00024InfoBar_clazz_defined -#define org_chromium_TestJni_00024InfoBar_clazz_defined -inline jclass org_chromium_TestJni_00024InfoBar_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni_00024InfoBar, - &g_org_chromium_TestJni_00024InfoBar_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. - -static base::subtle::AtomicWord g_org_chromium_TestJni_showConfirmInfoBar = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_showConfirmInfoBar(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper nativeInfoBar, - const base::android::JavaRef& buttonOk, - const base::android::JavaRef& buttonCancel, - const base::android::JavaRef& title, - const base::android::JavaRef& icon) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "showConfirmInfoBar", -"(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Bitmap;)Lorg/chromium/Foo$InnerClass;", - &g_org_chromium_TestJni_showConfirmInfoBar); - - jobject ret = - env->CallObjectMethod(obj.obj(), - method_id, as_jint(nativeInfoBar), buttonOk.obj(), buttonCancel.obj(), title.obj(), - icon.obj()); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_showAutoLoginInfoBar = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_showAutoLoginInfoBar(JNIEnv* env, - const base::android::JavaRef& obj, JniIntWrapper nativeInfoBar, - const base::android::JavaRef& realm, - const base::android::JavaRef& account, - const base::android::JavaRef& args) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "showAutoLoginInfoBar", - "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/chromium/Foo$InnerClass;", - &g_org_chromium_TestJni_showAutoLoginInfoBar); - - jobject ret = - env->CallObjectMethod(obj.obj(), - method_id, as_jint(nativeInfoBar), realm.obj(), account.obj(), args.obj()); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_00024InfoBar_dismiss = 0; -static void Java_InfoBar_dismiss(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_00024InfoBar_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_00024InfoBar_clazz(env), - "dismiss", - "()V", - &g_org_chromium_TestJni_00024InfoBar_dismiss); - - env->CallVoidMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_shouldShowAutoLogin = 0; -static jboolean Java_TestJni_shouldShowAutoLogin(JNIEnv* env, const base::android::JavaRef& - view, - const base::android::JavaRef& realm, - const base::android::JavaRef& account, - const base::android::JavaRef& args) { - CHECK_CLAZZ(env, org_chromium_TestJni_clazz(env), - org_chromium_TestJni_clazz(env), false); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_TestJni_clazz(env), - "shouldShowAutoLogin", - "(Landroid/view/View;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", - &g_org_chromium_TestJni_shouldShowAutoLogin); - - jboolean ret = - env->CallStaticBooleanMethod(org_chromium_TestJni_clazz(env), - method_id, view.obj(), realm.obj(), account.obj(), args.obj()); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_openUrl = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_openUrl(JNIEnv* env, const - base::android::JavaRef& url) { - CHECK_CLAZZ(env, org_chromium_TestJni_clazz(env), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_TestJni_clazz(env), - "openUrl", - "(Ljava/lang/String;)Ljava/io/InputStream;", - &g_org_chromium_TestJni_openUrl); - - jobject ret = - env->CallStaticObjectMethod(org_chromium_TestJni_clazz(env), - method_id, url.obj()); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_activateHardwareAcceleration = 0; -static void Java_TestJni_activateHardwareAcceleration(JNIEnv* env, const - base::android::JavaRef& obj, jboolean activated, - JniIntWrapper iPid, - JniIntWrapper iType, - JniIntWrapper iPrimaryID, - JniIntWrapper iSecondaryID) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "activateHardwareAcceleration", - "(ZIIII)V", - &g_org_chromium_TestJni_activateHardwareAcceleration); - - env->CallVoidMethod(obj.obj(), - method_id, activated, as_jint(iPid), as_jint(iType), as_jint(iPrimaryID), - as_jint(iSecondaryID)); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_updateStatus = 0; -static jint Java_TestJni_updateStatus(JNIEnv* env, JniIntWrapper status) { - CHECK_CLAZZ(env, org_chromium_TestJni_clazz(env), - org_chromium_TestJni_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_TestJni_clazz(env), - "updateStatus", - "(I)I", - &g_org_chromium_TestJni_updateStatus); - - jint ret = - env->CallStaticIntMethod(org_chromium_TestJni_clazz(env), - method_id, as_jint(status)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_uncheckedCall = 0; -static void Java_TestJni_uncheckedCall(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper iParam) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "uncheckedCall", - "(I)V", - &g_org_chromium_TestJni_uncheckedCall); - - env->CallVoidMethod(obj.obj(), - method_id, as_jint(iParam)); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_returnByteArray = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_returnByteArray(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "returnByteArray", - "()[B", - &g_org_chromium_TestJni_returnByteArray); - - jbyteArray ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_returnBooleanArray = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_returnBooleanArray(JNIEnv* env, - const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "returnBooleanArray", - "()[Z", - &g_org_chromium_TestJni_returnBooleanArray); - - jbooleanArray ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_returnCharArray = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_returnCharArray(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "returnCharArray", - "()[C", - &g_org_chromium_TestJni_returnCharArray); - - jcharArray ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_returnShortArray = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_returnShortArray(JNIEnv* env, - const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "returnShortArray", - "()[S", - &g_org_chromium_TestJni_returnShortArray); - - jshortArray ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_returnIntArray = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_returnIntArray(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "returnIntArray", - "()[I", - &g_org_chromium_TestJni_returnIntArray); - - jintArray ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_returnLongArray = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_returnLongArray(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "returnLongArray", - "()[J", - &g_org_chromium_TestJni_returnLongArray); - - jlongArray ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_returnDoubleArray = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_returnDoubleArray(JNIEnv* env, - const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "returnDoubleArray", - "()[D", - &g_org_chromium_TestJni_returnDoubleArray); - - jdoubleArray ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_returnObjectArray = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_returnObjectArray(JNIEnv* env, - const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "returnObjectArray", - "()[Ljava/lang/Object;", - &g_org_chromium_TestJni_returnObjectArray); - - jobjectArray ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_returnArrayOfByteArray = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_returnArrayOfByteArray(JNIEnv* - env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "returnArrayOfByteArray", - "()[[B", - &g_org_chromium_TestJni_returnArrayOfByteArray); - - jobjectArray ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_getCompressFormat = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_getCompressFormat(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "getCompressFormat", - "()Landroid/graphics/Bitmap$CompressFormat;", - &g_org_chromium_TestJni_getCompressFormat); - - jobject ret = - env->CallObjectMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_TestJni_getCompressFormatList = 0; -static base::android::ScopedJavaLocalRef Java_TestJni_getCompressFormatList(JNIEnv* env, - const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_TestJni_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_TestJni_clazz(env), - "getCompressFormatList", - "()Ljava/util/List;", - &g_org_chromium_TestJni_getCompressFormatList); - - jobject ret = - env->CallObjectMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -#endif // org_chromium_TestJni_JNI diff --git a/android/jni_generator/testConstantsFromJavaP.golden b/android/jni_generator/testConstantsFromJavaP.golden deleted file mode 100644 index 9cb39b744..000000000 --- a/android/jni_generator/testConstantsFromJavaP.golden +++ /dev/null @@ -1,2059 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// android/view/MotionEvent - -#ifndef android_view_MotionEvent_JNI -#define android_view_MotionEvent_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_android_view_MotionEvent[]; -const char kClassPath_android_view_MotionEvent[] = "android/view/MotionEvent"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_android_view_MotionEvent_clazz = 0; -#ifndef android_view_MotionEvent_clazz_defined -#define android_view_MotionEvent_clazz_defined -inline jclass android_view_MotionEvent_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_android_view_MotionEvent, - &g_android_view_MotionEvent_clazz); -} -#endif - - -// Step 2: Constants (optional). - -namespace JNI_MotionEvent { - -enum Java_MotionEvent_constant_fields { - INVALID_POINTER_ID = -1, - ACTION_MASK = 255, - ACTION_DOWN = 0, - ACTION_UP = 1, - ACTION_MOVE = 2, - ACTION_CANCEL = 3, - ACTION_OUTSIDE = 4, - ACTION_POINTER_DOWN = 5, - ACTION_POINTER_UP = 6, - ACTION_HOVER_MOVE = 7, - ACTION_SCROLL = 8, - ACTION_HOVER_ENTER = 9, - ACTION_HOVER_EXIT = 10, - ACTION_POINTER_INDEX_MASK = 65280, - ACTION_POINTER_INDEX_SHIFT = 8, - ACTION_POINTER_1_DOWN = 5, - ACTION_POINTER_2_DOWN = 261, - ACTION_POINTER_3_DOWN = 517, - ACTION_POINTER_1_UP = 6, - ACTION_POINTER_2_UP = 262, - ACTION_POINTER_3_UP = 518, - ACTION_POINTER_ID_MASK = 65280, - ACTION_POINTER_ID_SHIFT = 8, - FLAG_WINDOW_IS_OBSCURED = 1, - EDGE_TOP = 1, - EDGE_BOTTOM = 2, - EDGE_LEFT = 4, - EDGE_RIGHT = 8, - AXIS_X = 0, - AXIS_Y = 1, - AXIS_PRESSURE = 2, - AXIS_SIZE = 3, - AXIS_TOUCH_MAJOR = 4, - AXIS_TOUCH_MINOR = 5, - AXIS_TOOL_MAJOR = 6, - AXIS_TOOL_MINOR = 7, - AXIS_ORIENTATION = 8, - AXIS_VSCROLL = 9, - AXIS_HSCROLL = 10, - AXIS_Z = 11, - AXIS_RX = 12, - AXIS_RY = 13, - AXIS_RZ = 14, - AXIS_HAT_X = 15, - AXIS_HAT_Y = 16, - AXIS_LTRIGGER = 17, - AXIS_RTRIGGER = 18, - AXIS_THROTTLE = 19, - AXIS_RUDDER = 20, - AXIS_WHEEL = 21, - AXIS_GAS = 22, - AXIS_BRAKE = 23, - AXIS_DISTANCE = 24, - AXIS_TILT = 25, - AXIS_GENERIC_1 = 32, - AXIS_GENERIC_2 = 33, - AXIS_GENERIC_3 = 34, - AXIS_GENERIC_4 = 35, - AXIS_GENERIC_5 = 36, - AXIS_GENERIC_6 = 37, - AXIS_GENERIC_7 = 38, - AXIS_GENERIC_8 = 39, - AXIS_GENERIC_9 = 40, - AXIS_GENERIC_10 = 41, - AXIS_GENERIC_11 = 42, - AXIS_GENERIC_12 = 43, - AXIS_GENERIC_13 = 44, - AXIS_GENERIC_14 = 45, - AXIS_GENERIC_15 = 46, - AXIS_GENERIC_16 = 47, - BUTTON_PRIMARY = 1, - BUTTON_SECONDARY = 2, - BUTTON_TERTIARY = 4, - BUTTON_BACK = 8, - BUTTON_FORWARD = 16, - TOOL_TYPE_UNKNOWN = 0, - TOOL_TYPE_FINGER = 1, - TOOL_TYPE_STYLUS = 2, - TOOL_TYPE_MOUSE = 3, - TOOL_TYPE_ERASER = 4, -}; - - -} // namespace JNI_MotionEvent -// Step 3: Method stubs. -namespace JNI_MotionEvent { - - -static base::subtle::AtomicWord g_android_view_MotionEvent_finalize = 0; -static void Java_MotionEvent_finalize(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static void Java_MotionEvent_finalize(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "finalize", - "()V", - &g_android_view_MotionEvent_finalize); - - env->CallVoidMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord - g_android_view_MotionEvent_obtainAVME_J_J_I_I_LAVMEPP_LAVMEPC_I_I_F_F_I_I_I_I = 0; -static base::android::ScopedJavaLocalRef - Java_MotionEvent_obtainAVME_J_J_I_I_LAVMEPP_LAVMEPC_I_I_F_F_I_I_I_I(JNIEnv* env, jlong p0, - jlong p1, - JniIntWrapper p2, - JniIntWrapper p3, - const base::android::JavaRef& p4, - const base::android::JavaRef& p5, - JniIntWrapper p6, - JniIntWrapper p7, - jfloat p8, - jfloat p9, - JniIntWrapper p10, - JniIntWrapper p11, - JniIntWrapper p12, - JniIntWrapper p13) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef - Java_MotionEvent_obtainAVME_J_J_I_I_LAVMEPP_LAVMEPC_I_I_F_F_I_I_I_I(JNIEnv* env, jlong p0, - jlong p1, - JniIntWrapper p2, - JniIntWrapper p3, - const base::android::JavaRef& p4, - const base::android::JavaRef& p5, - JniIntWrapper p6, - JniIntWrapper p7, - jfloat p8, - jfloat p9, - JniIntWrapper p10, - JniIntWrapper p11, - JniIntWrapper p12, - JniIntWrapper p13) { - CHECK_CLAZZ(env, android_view_MotionEvent_clazz(env), - android_view_MotionEvent_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, android_view_MotionEvent_clazz(env), - "obtain", -"(JJII[Landroid/view/MotionEvent$PointerProperties;[Landroid/view/MotionEvent$PointerCoords;IIFFIIII)Landroid/view/MotionEvent;", - &g_android_view_MotionEvent_obtainAVME_J_J_I_I_LAVMEPP_LAVMEPC_I_I_F_F_I_I_I_I); - - jobject ret = - env->CallStaticObjectMethod(android_view_MotionEvent_clazz(env), - method_id, p0, p1, as_jint(p2), as_jint(p3), p4.obj(), p5.obj(), as_jint(p6), as_jint(p7), - p8, p9, as_jint(p10), as_jint(p11), as_jint(p12), as_jint(p13)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord - g_android_view_MotionEvent_obtainAVME_J_J_I_I_AI_LAVMEPC_I_F_F_I_I_I_I = 0; -static base::android::ScopedJavaLocalRef - Java_MotionEvent_obtainAVME_J_J_I_I_AI_LAVMEPC_I_F_F_I_I_I_I(JNIEnv* env, jlong p0, - jlong p1, - JniIntWrapper p2, - JniIntWrapper p3, - const base::android::JavaRef& p4, - const base::android::JavaRef& p5, - JniIntWrapper p6, - jfloat p7, - jfloat p8, - JniIntWrapper p9, - JniIntWrapper p10, - JniIntWrapper p11, - JniIntWrapper p12) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef - Java_MotionEvent_obtainAVME_J_J_I_I_AI_LAVMEPC_I_F_F_I_I_I_I(JNIEnv* env, jlong p0, - jlong p1, - JniIntWrapper p2, - JniIntWrapper p3, - const base::android::JavaRef& p4, - const base::android::JavaRef& p5, - JniIntWrapper p6, - jfloat p7, - jfloat p8, - JniIntWrapper p9, - JniIntWrapper p10, - JniIntWrapper p11, - JniIntWrapper p12) { - CHECK_CLAZZ(env, android_view_MotionEvent_clazz(env), - android_view_MotionEvent_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, android_view_MotionEvent_clazz(env), - "obtain", - "(JJII[I[Landroid/view/MotionEvent$PointerCoords;IFFIIII)Landroid/view/MotionEvent;", - &g_android_view_MotionEvent_obtainAVME_J_J_I_I_AI_LAVMEPC_I_F_F_I_I_I_I); - - jobject ret = - env->CallStaticObjectMethod(android_view_MotionEvent_clazz(env), - method_id, p0, p1, as_jint(p2), as_jint(p3), p4.obj(), p5.obj(), as_jint(p6), p7, p8, - as_jint(p9), as_jint(p10), as_jint(p11), as_jint(p12)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_obtainAVME_J_J_I_F_F_F_F_I_F_F_I_I = 0; -static base::android::ScopedJavaLocalRef - Java_MotionEvent_obtainAVME_J_J_I_F_F_F_F_I_F_F_I_I(JNIEnv* env, jlong p0, - jlong p1, - JniIntWrapper p2, - jfloat p3, - jfloat p4, - jfloat p5, - jfloat p6, - JniIntWrapper p7, - jfloat p8, - jfloat p9, - JniIntWrapper p10, - JniIntWrapper p11) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef - Java_MotionEvent_obtainAVME_J_J_I_F_F_F_F_I_F_F_I_I(JNIEnv* env, jlong p0, - jlong p1, - JniIntWrapper p2, - jfloat p3, - jfloat p4, - jfloat p5, - jfloat p6, - JniIntWrapper p7, - jfloat p8, - jfloat p9, - JniIntWrapper p10, - JniIntWrapper p11) { - CHECK_CLAZZ(env, android_view_MotionEvent_clazz(env), - android_view_MotionEvent_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, android_view_MotionEvent_clazz(env), - "obtain", - "(JJIFFFFIFFII)Landroid/view/MotionEvent;", - &g_android_view_MotionEvent_obtainAVME_J_J_I_F_F_F_F_I_F_F_I_I); - - jobject ret = - env->CallStaticObjectMethod(android_view_MotionEvent_clazz(env), - method_id, p0, p1, as_jint(p2), p3, p4, p5, p6, as_jint(p7), p8, p9, as_jint(p10), - as_jint(p11)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_obtainAVME_J_J_I_I_F_F_F_F_I_F_F_I_I = 0; -static base::android::ScopedJavaLocalRef - Java_MotionEvent_obtainAVME_J_J_I_I_F_F_F_F_I_F_F_I_I(JNIEnv* env, jlong p0, - jlong p1, - JniIntWrapper p2, - JniIntWrapper p3, - jfloat p4, - jfloat p5, - jfloat p6, - jfloat p7, - JniIntWrapper p8, - jfloat p9, - jfloat p10, - JniIntWrapper p11, - JniIntWrapper p12) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef - Java_MotionEvent_obtainAVME_J_J_I_I_F_F_F_F_I_F_F_I_I(JNIEnv* env, jlong p0, - jlong p1, - JniIntWrapper p2, - JniIntWrapper p3, - jfloat p4, - jfloat p5, - jfloat p6, - jfloat p7, - JniIntWrapper p8, - jfloat p9, - jfloat p10, - JniIntWrapper p11, - JniIntWrapper p12) { - CHECK_CLAZZ(env, android_view_MotionEvent_clazz(env), - android_view_MotionEvent_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, android_view_MotionEvent_clazz(env), - "obtain", - "(JJIIFFFFIFFII)Landroid/view/MotionEvent;", - &g_android_view_MotionEvent_obtainAVME_J_J_I_I_F_F_F_F_I_F_F_I_I); - - jobject ret = - env->CallStaticObjectMethod(android_view_MotionEvent_clazz(env), - method_id, p0, p1, as_jint(p2), as_jint(p3), p4, p5, p6, p7, as_jint(p8), p9, p10, - as_jint(p11), as_jint(p12)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_obtainAVME_J_J_I_F_F_I = 0; -static base::android::ScopedJavaLocalRef Java_MotionEvent_obtainAVME_J_J_I_F_F_I(JNIEnv* - env, jlong p0, - jlong p1, - JniIntWrapper p2, - jfloat p3, - jfloat p4, - JniIntWrapper p5) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef Java_MotionEvent_obtainAVME_J_J_I_F_F_I(JNIEnv* - env, jlong p0, - jlong p1, - JniIntWrapper p2, - jfloat p3, - jfloat p4, - JniIntWrapper p5) { - CHECK_CLAZZ(env, android_view_MotionEvent_clazz(env), - android_view_MotionEvent_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, android_view_MotionEvent_clazz(env), - "obtain", - "(JJIFFI)Landroid/view/MotionEvent;", - &g_android_view_MotionEvent_obtainAVME_J_J_I_F_F_I); - - jobject ret = - env->CallStaticObjectMethod(android_view_MotionEvent_clazz(env), - method_id, p0, p1, as_jint(p2), p3, p4, as_jint(p5)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_obtainAVME_AVME = 0; -static base::android::ScopedJavaLocalRef Java_MotionEvent_obtainAVME_AVME(JNIEnv* env, - const base::android::JavaRef& p0) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef Java_MotionEvent_obtainAVME_AVME(JNIEnv* env, - const base::android::JavaRef& p0) { - CHECK_CLAZZ(env, android_view_MotionEvent_clazz(env), - android_view_MotionEvent_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, android_view_MotionEvent_clazz(env), - "obtain", - "(Landroid/view/MotionEvent;)Landroid/view/MotionEvent;", - &g_android_view_MotionEvent_obtainAVME_AVME); - - jobject ret = - env->CallStaticObjectMethod(android_view_MotionEvent_clazz(env), - method_id, p0.obj()); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_obtainNoHistory = 0; -static base::android::ScopedJavaLocalRef Java_MotionEvent_obtainNoHistory(JNIEnv* env, - const base::android::JavaRef& p0) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef Java_MotionEvent_obtainNoHistory(JNIEnv* env, - const base::android::JavaRef& p0) { - CHECK_CLAZZ(env, android_view_MotionEvent_clazz(env), - android_view_MotionEvent_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, android_view_MotionEvent_clazz(env), - "obtainNoHistory", - "(Landroid/view/MotionEvent;)Landroid/view/MotionEvent;", - &g_android_view_MotionEvent_obtainNoHistory); - - jobject ret = - env->CallStaticObjectMethod(android_view_MotionEvent_clazz(env), - method_id, p0.obj()); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_recycle = 0; -static void Java_MotionEvent_recycle(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static void Java_MotionEvent_recycle(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "recycle", - "()V", - &g_android_view_MotionEvent_recycle); - - env->CallVoidMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getDeviceId = 0; -static jint Java_MotionEvent_getDeviceId(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getDeviceId(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getDeviceId", - "()I", - &g_android_view_MotionEvent_getDeviceId); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getSource = 0; -static jint Java_MotionEvent_getSource(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getSource(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getSource", - "()I", - &g_android_view_MotionEvent_getSource); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_setSource = 0; -static void Java_MotionEvent_setSource(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) __attribute__ ((unused)); -static void Java_MotionEvent_setSource(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "setSource", - "(I)V", - &g_android_view_MotionEvent_setSource); - - env->CallVoidMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getAction = 0; -static jint Java_MotionEvent_getAction(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getAction(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getAction", - "()I", - &g_android_view_MotionEvent_getAction); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getActionMasked = 0; -static jint Java_MotionEvent_getActionMasked(JNIEnv* env, const base::android::JavaRef& - obj) __attribute__ ((unused)); -static jint Java_MotionEvent_getActionMasked(JNIEnv* env, const base::android::JavaRef& - obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getActionMasked", - "()I", - &g_android_view_MotionEvent_getActionMasked); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getActionIndex = 0; -static jint Java_MotionEvent_getActionIndex(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getActionIndex(JNIEnv* env, const base::android::JavaRef& obj) - { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getActionIndex", - "()I", - &g_android_view_MotionEvent_getActionIndex); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getFlags = 0; -static jint Java_MotionEvent_getFlags(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getFlags(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getFlags", - "()I", - &g_android_view_MotionEvent_getFlags); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getDownTime = 0; -static jlong Java_MotionEvent_getDownTime(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jlong Java_MotionEvent_getDownTime(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getDownTime", - "()J", - &g_android_view_MotionEvent_getDownTime); - - jlong ret = - env->CallLongMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getEventTime = 0; -static jlong Java_MotionEvent_getEventTime(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jlong Java_MotionEvent_getEventTime(JNIEnv* env, const base::android::JavaRef& obj) - { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getEventTime", - "()J", - &g_android_view_MotionEvent_getEventTime); - - jlong ret = - env->CallLongMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getXF = 0; -static jfloat Java_MotionEvent_getXF(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getXF(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getX", - "()F", - &g_android_view_MotionEvent_getXF); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getYF = 0; -static jfloat Java_MotionEvent_getYF(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getYF(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getY", - "()F", - &g_android_view_MotionEvent_getYF); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getPressureF = 0; -static jfloat Java_MotionEvent_getPressureF(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getPressureF(JNIEnv* env, const base::android::JavaRef& obj) - { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getPressure", - "()F", - &g_android_view_MotionEvent_getPressureF); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getSizeF = 0; -static jfloat Java_MotionEvent_getSizeF(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getSizeF(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getSize", - "()F", - &g_android_view_MotionEvent_getSizeF); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getTouchMajorF = 0; -static jfloat Java_MotionEvent_getTouchMajorF(JNIEnv* env, const base::android::JavaRef& - obj) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getTouchMajorF(JNIEnv* env, const base::android::JavaRef& - obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getTouchMajor", - "()F", - &g_android_view_MotionEvent_getTouchMajorF); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getTouchMinorF = 0; -static jfloat Java_MotionEvent_getTouchMinorF(JNIEnv* env, const base::android::JavaRef& - obj) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getTouchMinorF(JNIEnv* env, const base::android::JavaRef& - obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getTouchMinor", - "()F", - &g_android_view_MotionEvent_getTouchMinorF); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getToolMajorF = 0; -static jfloat Java_MotionEvent_getToolMajorF(JNIEnv* env, const base::android::JavaRef& - obj) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getToolMajorF(JNIEnv* env, const base::android::JavaRef& - obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getToolMajor", - "()F", - &g_android_view_MotionEvent_getToolMajorF); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getToolMinorF = 0; -static jfloat Java_MotionEvent_getToolMinorF(JNIEnv* env, const base::android::JavaRef& - obj) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getToolMinorF(JNIEnv* env, const base::android::JavaRef& - obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getToolMinor", - "()F", - &g_android_view_MotionEvent_getToolMinorF); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getOrientationF = 0; -static jfloat Java_MotionEvent_getOrientationF(JNIEnv* env, const base::android::JavaRef& - obj) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getOrientationF(JNIEnv* env, const base::android::JavaRef& - obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getOrientation", - "()F", - &g_android_view_MotionEvent_getOrientationF); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getAxisValueF_I = 0; -static jfloat Java_MotionEvent_getAxisValueF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getAxisValueF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getAxisValue", - "(I)F", - &g_android_view_MotionEvent_getAxisValueF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getPointerCount = 0; -static jint Java_MotionEvent_getPointerCount(JNIEnv* env, const base::android::JavaRef& - obj) __attribute__ ((unused)); -static jint Java_MotionEvent_getPointerCount(JNIEnv* env, const base::android::JavaRef& - obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getPointerCount", - "()I", - &g_android_view_MotionEvent_getPointerCount); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getPointerId = 0; -static jint Java_MotionEvent_getPointerId(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jint Java_MotionEvent_getPointerId(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getPointerId", - "(I)I", - &g_android_view_MotionEvent_getPointerId); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getToolType = 0; -static jint Java_MotionEvent_getToolType(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jint Java_MotionEvent_getToolType(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getToolType", - "(I)I", - &g_android_view_MotionEvent_getToolType); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_findPointerIndex = 0; -static jint Java_MotionEvent_findPointerIndex(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jint Java_MotionEvent_findPointerIndex(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "findPointerIndex", - "(I)I", - &g_android_view_MotionEvent_findPointerIndex); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getXF_I = 0; -static jfloat Java_MotionEvent_getXF_I(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getXF_I(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getX", - "(I)F", - &g_android_view_MotionEvent_getXF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getYF_I = 0; -static jfloat Java_MotionEvent_getYF_I(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getYF_I(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getY", - "(I)F", - &g_android_view_MotionEvent_getYF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getPressureF_I = 0; -static jfloat Java_MotionEvent_getPressureF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getPressureF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getPressure", - "(I)F", - &g_android_view_MotionEvent_getPressureF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getSizeF_I = 0; -static jfloat Java_MotionEvent_getSizeF_I(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getSizeF_I(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getSize", - "(I)F", - &g_android_view_MotionEvent_getSizeF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getTouchMajorF_I = 0; -static jfloat Java_MotionEvent_getTouchMajorF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getTouchMajorF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getTouchMajor", - "(I)F", - &g_android_view_MotionEvent_getTouchMajorF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getTouchMinorF_I = 0; -static jfloat Java_MotionEvent_getTouchMinorF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getTouchMinorF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getTouchMinor", - "(I)F", - &g_android_view_MotionEvent_getTouchMinorF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getToolMajorF_I = 0; -static jfloat Java_MotionEvent_getToolMajorF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getToolMajorF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getToolMajor", - "(I)F", - &g_android_view_MotionEvent_getToolMajorF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getToolMinorF_I = 0; -static jfloat Java_MotionEvent_getToolMinorF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getToolMinorF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getToolMinor", - "(I)F", - &g_android_view_MotionEvent_getToolMinorF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getOrientationF_I = 0; -static jfloat Java_MotionEvent_getOrientationF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getOrientationF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getOrientation", - "(I)F", - &g_android_view_MotionEvent_getOrientationF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getAxisValueF_I_I = 0; -static jfloat Java_MotionEvent_getAxisValueF_I_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getAxisValueF_I_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getAxisValue", - "(II)F", - &g_android_view_MotionEvent_getAxisValueF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getPointerCoords = 0; -static void Java_MotionEvent_getPointerCoords(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0, - const base::android::JavaRef& p1) __attribute__ ((unused)); -static void Java_MotionEvent_getPointerCoords(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0, - const base::android::JavaRef& p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getPointerCoords", - "(ILandroid/view/MotionEvent$PointerCoords;)V", - &g_android_view_MotionEvent_getPointerCoords); - - env->CallVoidMethod(obj.obj(), - method_id, as_jint(p0), p1.obj()); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getPointerProperties = 0; -static void Java_MotionEvent_getPointerProperties(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - const base::android::JavaRef& p1) __attribute__ ((unused)); -static void Java_MotionEvent_getPointerProperties(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - const base::android::JavaRef& p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getPointerProperties", - "(ILandroid/view/MotionEvent$PointerProperties;)V", - &g_android_view_MotionEvent_getPointerProperties); - - env->CallVoidMethod(obj.obj(), - method_id, as_jint(p0), p1.obj()); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getMetaState = 0; -static jint Java_MotionEvent_getMetaState(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getMetaState(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getMetaState", - "()I", - &g_android_view_MotionEvent_getMetaState); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getButtonState = 0; -static jint Java_MotionEvent_getButtonState(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getButtonState(JNIEnv* env, const base::android::JavaRef& obj) - { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getButtonState", - "()I", - &g_android_view_MotionEvent_getButtonState); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getRawX = 0; -static jfloat Java_MotionEvent_getRawX(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getRawX(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getRawX", - "()F", - &g_android_view_MotionEvent_getRawX); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getRawY = 0; -static jfloat Java_MotionEvent_getRawY(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getRawY(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getRawY", - "()F", - &g_android_view_MotionEvent_getRawY); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getXPrecision = 0; -static jfloat Java_MotionEvent_getXPrecision(JNIEnv* env, const base::android::JavaRef& - obj) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getXPrecision(JNIEnv* env, const base::android::JavaRef& - obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getXPrecision", - "()F", - &g_android_view_MotionEvent_getXPrecision); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getYPrecision = 0; -static jfloat Java_MotionEvent_getYPrecision(JNIEnv* env, const base::android::JavaRef& - obj) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getYPrecision(JNIEnv* env, const base::android::JavaRef& - obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getYPrecision", - "()F", - &g_android_view_MotionEvent_getYPrecision); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistorySize = 0; -static jint Java_MotionEvent_getHistorySize(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getHistorySize(JNIEnv* env, const base::android::JavaRef& obj) - { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistorySize", - "()I", - &g_android_view_MotionEvent_getHistorySize); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalEventTime = 0; -static jlong Java_MotionEvent_getHistoricalEventTime(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) __attribute__ ((unused)); -static jlong Java_MotionEvent_getHistoricalEventTime(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalEventTime", - "(I)J", - &g_android_view_MotionEvent_getHistoricalEventTime); - - jlong ret = - env->CallLongMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalXF_I = 0; -static jfloat Java_MotionEvent_getHistoricalXF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalXF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalX", - "(I)F", - &g_android_view_MotionEvent_getHistoricalXF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalYF_I = 0; -static jfloat Java_MotionEvent_getHistoricalYF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalYF_I(JNIEnv* env, const base::android::JavaRef& - obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalY", - "(I)F", - &g_android_view_MotionEvent_getHistoricalYF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalPressureF_I = 0; -static jfloat Java_MotionEvent_getHistoricalPressureF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalPressureF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalPressure", - "(I)F", - &g_android_view_MotionEvent_getHistoricalPressureF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalSizeF_I = 0; -static jfloat Java_MotionEvent_getHistoricalSizeF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalSizeF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalSize", - "(I)F", - &g_android_view_MotionEvent_getHistoricalSizeF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalTouchMajorF_I = 0; -static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalTouchMajor", - "(I)F", - &g_android_view_MotionEvent_getHistoricalTouchMajorF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalTouchMinorF_I = 0; -static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalTouchMinor", - "(I)F", - &g_android_view_MotionEvent_getHistoricalTouchMinorF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalToolMajorF_I = 0; -static jfloat Java_MotionEvent_getHistoricalToolMajorF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalToolMajorF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalToolMajor", - "(I)F", - &g_android_view_MotionEvent_getHistoricalToolMajorF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalToolMinorF_I = 0; -static jfloat Java_MotionEvent_getHistoricalToolMinorF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalToolMinorF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalToolMinor", - "(I)F", - &g_android_view_MotionEvent_getHistoricalToolMinorF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalOrientationF_I = 0; -static jfloat Java_MotionEvent_getHistoricalOrientationF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalOrientationF_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalOrientation", - "(I)F", - &g_android_view_MotionEvent_getHistoricalOrientationF_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalAxisValueF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalAxisValue", - "(II)F", - &g_android_view_MotionEvent_getHistoricalAxisValueF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalXF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalXF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalXF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalX", - "(II)F", - &g_android_view_MotionEvent_getHistoricalXF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalYF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalYF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalYF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalY", - "(II)F", - &g_android_view_MotionEvent_getHistoricalYF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalPressureF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalPressureF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalPressureF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalPressure", - "(II)F", - &g_android_view_MotionEvent_getHistoricalPressureF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalSizeF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalSizeF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalSizeF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalSize", - "(II)F", - &g_android_view_MotionEvent_getHistoricalSizeF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalTouchMajorF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalTouchMajor", - "(II)F", - &g_android_view_MotionEvent_getHistoricalTouchMajorF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalTouchMinorF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalTouchMinor", - "(II)F", - &g_android_view_MotionEvent_getHistoricalTouchMinorF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalToolMajorF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalToolMajorF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalToolMajorF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalToolMajor", - "(II)F", - &g_android_view_MotionEvent_getHistoricalToolMajorF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalToolMinorF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalToolMinorF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalToolMinorF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalToolMinor", - "(II)F", - &g_android_view_MotionEvent_getHistoricalToolMinorF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalOrientationF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalOrientationF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalOrientationF_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalOrientation", - "(II)F", - &g_android_view_MotionEvent_getHistoricalOrientationF_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalAxisValueF_I_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1, - JniIntWrapper p2) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I_I(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1, - JniIntWrapper p2) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalAxisValue", - "(III)F", - &g_android_view_MotionEvent_getHistoricalAxisValueF_I_I_I); - - jfloat ret = - env->CallFloatMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1), as_jint(p2)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getHistoricalPointerCoords = 0; -static void Java_MotionEvent_getHistoricalPointerCoords(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1, - const base::android::JavaRef& p2) __attribute__ ((unused)); -static void Java_MotionEvent_getHistoricalPointerCoords(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper p0, - JniIntWrapper p1, - const base::android::JavaRef& p2) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getHistoricalPointerCoords", - "(IILandroid/view/MotionEvent$PointerCoords;)V", - &g_android_view_MotionEvent_getHistoricalPointerCoords); - - env->CallVoidMethod(obj.obj(), - method_id, as_jint(p0), as_jint(p1), p2.obj()); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_getEdgeFlags = 0; -static jint Java_MotionEvent_getEdgeFlags(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getEdgeFlags(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "getEdgeFlags", - "()I", - &g_android_view_MotionEvent_getEdgeFlags); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_setEdgeFlags = 0; -static void Java_MotionEvent_setEdgeFlags(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) __attribute__ ((unused)); -static void Java_MotionEvent_setEdgeFlags(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "setEdgeFlags", - "(I)V", - &g_android_view_MotionEvent_setEdgeFlags); - - env->CallVoidMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_setAction = 0; -static void Java_MotionEvent_setAction(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) __attribute__ ((unused)); -static void Java_MotionEvent_setAction(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "setAction", - "(I)V", - &g_android_view_MotionEvent_setAction); - - env->CallVoidMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_offsetLocation = 0; -static void Java_MotionEvent_offsetLocation(JNIEnv* env, const base::android::JavaRef& obj, - jfloat p0, - jfloat p1) __attribute__ ((unused)); -static void Java_MotionEvent_offsetLocation(JNIEnv* env, const base::android::JavaRef& obj, - jfloat p0, - jfloat p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "offsetLocation", - "(FF)V", - &g_android_view_MotionEvent_offsetLocation); - - env->CallVoidMethod(obj.obj(), - method_id, p0, p1); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_setLocation = 0; -static void Java_MotionEvent_setLocation(JNIEnv* env, const base::android::JavaRef& obj, - jfloat p0, - jfloat p1) __attribute__ ((unused)); -static void Java_MotionEvent_setLocation(JNIEnv* env, const base::android::JavaRef& obj, - jfloat p0, - jfloat p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "setLocation", - "(FF)V", - &g_android_view_MotionEvent_setLocation); - - env->CallVoidMethod(obj.obj(), - method_id, p0, p1); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_transform = 0; -static void Java_MotionEvent_transform(JNIEnv* env, const base::android::JavaRef& obj, - const base::android::JavaRef& p0) __attribute__ ((unused)); -static void Java_MotionEvent_transform(JNIEnv* env, const base::android::JavaRef& obj, - const base::android::JavaRef& p0) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "transform", - "(Landroid/graphics/Matrix;)V", - &g_android_view_MotionEvent_transform); - - env->CallVoidMethod(obj.obj(), - method_id, p0.obj()); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_addBatchV_J_F_F_F_F_I = 0; -static void Java_MotionEvent_addBatchV_J_F_F_F_F_I(JNIEnv* env, const - base::android::JavaRef& obj, jlong p0, - jfloat p1, - jfloat p2, - jfloat p3, - jfloat p4, - JniIntWrapper p5) __attribute__ ((unused)); -static void Java_MotionEvent_addBatchV_J_F_F_F_F_I(JNIEnv* env, const - base::android::JavaRef& obj, jlong p0, - jfloat p1, - jfloat p2, - jfloat p3, - jfloat p4, - JniIntWrapper p5) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "addBatch", - "(JFFFFI)V", - &g_android_view_MotionEvent_addBatchV_J_F_F_F_F_I); - - env->CallVoidMethod(obj.obj(), - method_id, p0, p1, p2, p3, p4, as_jint(p5)); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_addBatchV_J_LAVMEPC_I = 0; -static void Java_MotionEvent_addBatchV_J_LAVMEPC_I(JNIEnv* env, const - base::android::JavaRef& obj, jlong p0, - const base::android::JavaRef& p1, - JniIntWrapper p2) __attribute__ ((unused)); -static void Java_MotionEvent_addBatchV_J_LAVMEPC_I(JNIEnv* env, const - base::android::JavaRef& obj, jlong p0, - const base::android::JavaRef& p1, - JniIntWrapper p2) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "addBatch", - "(J[Landroid/view/MotionEvent$PointerCoords;I)V", - &g_android_view_MotionEvent_addBatchV_J_LAVMEPC_I); - - env->CallVoidMethod(obj.obj(), - method_id, p0, p1.obj(), as_jint(p2)); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_toString = 0; -static base::android::ScopedJavaLocalRef Java_MotionEvent_toString(JNIEnv* env, const - base::android::JavaRef& obj) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef Java_MotionEvent_toString(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "toString", - "()Ljava/lang/String;", - &g_android_view_MotionEvent_toString); - - jstring ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_actionToString = 0; -static base::android::ScopedJavaLocalRef Java_MotionEvent_actionToString(JNIEnv* env, - JniIntWrapper p0) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef Java_MotionEvent_actionToString(JNIEnv* env, - JniIntWrapper p0) { - CHECK_CLAZZ(env, android_view_MotionEvent_clazz(env), - android_view_MotionEvent_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, android_view_MotionEvent_clazz(env), - "actionToString", - "(I)Ljava/lang/String;", - &g_android_view_MotionEvent_actionToString); - - jstring ret = - static_cast(env->CallStaticObjectMethod(android_view_MotionEvent_clazz(env), - method_id, as_jint(p0))); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_axisToString = 0; -static base::android::ScopedJavaLocalRef Java_MotionEvent_axisToString(JNIEnv* env, - JniIntWrapper p0) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef Java_MotionEvent_axisToString(JNIEnv* env, - JniIntWrapper p0) { - CHECK_CLAZZ(env, android_view_MotionEvent_clazz(env), - android_view_MotionEvent_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, android_view_MotionEvent_clazz(env), - "axisToString", - "(I)Ljava/lang/String;", - &g_android_view_MotionEvent_axisToString); - - jstring ret = - static_cast(env->CallStaticObjectMethod(android_view_MotionEvent_clazz(env), - method_id, as_jint(p0))); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_axisFromString = 0; -static jint Java_MotionEvent_axisFromString(JNIEnv* env, const base::android::JavaRef& p0) - __attribute__ ((unused)); -static jint Java_MotionEvent_axisFromString(JNIEnv* env, const base::android::JavaRef& p0) - { - CHECK_CLAZZ(env, android_view_MotionEvent_clazz(env), - android_view_MotionEvent_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, android_view_MotionEvent_clazz(env), - "axisFromString", - "(Ljava/lang/String;)I", - &g_android_view_MotionEvent_axisFromString); - - jint ret = - env->CallStaticIntMethod(android_view_MotionEvent_clazz(env), - method_id, p0.obj()); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_android_view_MotionEvent_writeToParcel = 0; -static void Java_MotionEvent_writeToParcel(JNIEnv* env, const base::android::JavaRef& obj, - const base::android::JavaRef& p0, - JniIntWrapper p1) __attribute__ ((unused)); -static void Java_MotionEvent_writeToParcel(JNIEnv* env, const base::android::JavaRef& obj, - const base::android::JavaRef& p0, - JniIntWrapper p1) { - CHECK_CLAZZ(env, obj.obj(), - android_view_MotionEvent_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, android_view_MotionEvent_clazz(env), - "writeToParcel", - "(Landroid/os/Parcel;I)V", - &g_android_view_MotionEvent_writeToParcel); - - env->CallVoidMethod(obj.obj(), - method_id, p0.obj(), as_jint(p1)); - jni_generator::CheckException(env); -} - -} // namespace JNI_MotionEvent - -#endif // android_view_MotionEvent_JNI diff --git a/android/jni_generator/testFromJavaP.golden b/android/jni_generator/testFromJavaP.golden deleted file mode 100644 index ca5b050fb..000000000 --- a/android/jni_generator/testFromJavaP.golden +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// java/io/InputStream - -#ifndef java_io_InputStream_JNI -#define java_io_InputStream_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_java_io_InputStream[]; -const char kClassPath_java_io_InputStream[] = "java/io/InputStream"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_java_io_InputStream_clazz = 0; -#ifndef java_io_InputStream_clazz_defined -#define java_io_InputStream_clazz_defined -inline jclass java_io_InputStream_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_java_io_InputStream, - &g_java_io_InputStream_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -namespace JNI_InputStream { - - -static base::subtle::AtomicWord g_java_io_InputStream_available = 0; -static jint Java_InputStream_available(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_InputStream_available(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - java_io_InputStream_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_io_InputStream_clazz(env), - "available", - "()I", - &g_java_io_InputStream_available); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_java_io_InputStream_close = 0; -static void Java_InputStream_close(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static void Java_InputStream_close(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - java_io_InputStream_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_io_InputStream_clazz(env), - "close", - "()V", - &g_java_io_InputStream_close); - - env->CallVoidMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_java_io_InputStream_mark = 0; -static void Java_InputStream_mark(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) __attribute__ ((unused)); -static void Java_InputStream_mark(JNIEnv* env, const base::android::JavaRef& obj, - JniIntWrapper p0) { - CHECK_CLAZZ(env, obj.obj(), - java_io_InputStream_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_io_InputStream_clazz(env), - "mark", - "(I)V", - &g_java_io_InputStream_mark); - - env->CallVoidMethod(obj.obj(), - method_id, as_jint(p0)); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_java_io_InputStream_markSupported = 0; -static jboolean Java_InputStream_markSupported(JNIEnv* env, const base::android::JavaRef& - obj) __attribute__ ((unused)); -static jboolean Java_InputStream_markSupported(JNIEnv* env, const base::android::JavaRef& - obj) { - CHECK_CLAZZ(env, obj.obj(), - java_io_InputStream_clazz(env), false); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_io_InputStream_clazz(env), - "markSupported", - "()Z", - &g_java_io_InputStream_markSupported); - - jboolean ret = - env->CallBooleanMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_java_io_InputStream_readI = 0; -static jint Java_InputStream_readI(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static jint Java_InputStream_readI(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - java_io_InputStream_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_io_InputStream_clazz(env), - "read", - "()I", - &g_java_io_InputStream_readI); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_java_io_InputStream_readI_AB = 0; -static jint Java_InputStream_readI_AB(JNIEnv* env, const base::android::JavaRef& obj, const - base::android::JavaRef& p0) __attribute__ ((unused)); -static jint Java_InputStream_readI_AB(JNIEnv* env, const base::android::JavaRef& obj, const - base::android::JavaRef& p0) { - CHECK_CLAZZ(env, obj.obj(), - java_io_InputStream_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_io_InputStream_clazz(env), - "read", - "([B)I", - &g_java_io_InputStream_readI_AB); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id, p0.obj()); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_java_io_InputStream_readI_AB_I_I = 0; -static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, const base::android::JavaRef& obj, - const base::android::JavaRef& p0, - JniIntWrapper p1, - JniIntWrapper p2) __attribute__ ((unused)); -static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, const base::android::JavaRef& obj, - const base::android::JavaRef& p0, - JniIntWrapper p1, - JniIntWrapper p2) { - CHECK_CLAZZ(env, obj.obj(), - java_io_InputStream_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_io_InputStream_clazz(env), - "read", - "([BII)I", - &g_java_io_InputStream_readI_AB_I_I); - - jint ret = - env->CallIntMethod(obj.obj(), - method_id, p0.obj(), as_jint(p1), as_jint(p2)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_java_io_InputStream_reset = 0; -static void Java_InputStream_reset(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static void Java_InputStream_reset(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - java_io_InputStream_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_io_InputStream_clazz(env), - "reset", - "()V", - &g_java_io_InputStream_reset); - - env->CallVoidMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_java_io_InputStream_skip = 0; -static jlong Java_InputStream_skip(JNIEnv* env, const base::android::JavaRef& obj, jlong - p0) __attribute__ ((unused)); -static jlong Java_InputStream_skip(JNIEnv* env, const base::android::JavaRef& obj, jlong - p0) { - CHECK_CLAZZ(env, obj.obj(), - java_io_InputStream_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_io_InputStream_clazz(env), - "skip", - "(J)J", - &g_java_io_InputStream_skip); - - jlong ret = - env->CallLongMethod(obj.obj(), - method_id, p0); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_java_io_InputStream_Constructor = 0; -static base::android::ScopedJavaLocalRef Java_InputStream_Constructor(JNIEnv* env) - __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef Java_InputStream_Constructor(JNIEnv* env) { - CHECK_CLAZZ(env, java_io_InputStream_clazz(env), - java_io_InputStream_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_io_InputStream_clazz(env), - "", - "()V", - &g_java_io_InputStream_Constructor); - - jobject ret = - env->NewObject(java_io_InputStream_clazz(env), - method_id); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -} // namespace JNI_InputStream - -#endif // java_io_InputStream_JNI diff --git a/android/jni_generator/testFromJavaPGenerics.golden b/android/jni_generator/testFromJavaPGenerics.golden deleted file mode 100644 index 25c5e0ea2..000000000 --- a/android/jni_generator/testFromJavaPGenerics.golden +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// java/util/HashSet - -#ifndef java_util_HashSet_JNI -#define java_util_HashSet_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_java_util_HashSet[]; -const char kClassPath_java_util_HashSet[] = "java/util/HashSet"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_java_util_HashSet_clazz = 0; -#ifndef java_util_HashSet_clazz_defined -#define java_util_HashSet_clazz_defined -inline jclass java_util_HashSet_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_java_util_HashSet, &g_java_util_HashSet_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -namespace JNI_HashSet { - - -static base::subtle::AtomicWord g_java_util_HashSet_dummy = 0; -static void Java_HashSet_dummy(JNIEnv* env, const base::android::JavaRef& obj) - __attribute__ ((unused)); -static void Java_HashSet_dummy(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - java_util_HashSet_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_util_HashSet_clazz(env), - "dummy", - "()V", - &g_java_util_HashSet_dummy); - - env->CallVoidMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord g_java_util_HashSet_getClass = 0; -static base::android::ScopedJavaLocalRef Java_HashSet_getClass(JNIEnv* env, const - base::android::JavaRef& obj) __attribute__ ((unused)); -static base::android::ScopedJavaLocalRef Java_HashSet_getClass(JNIEnv* env, const - base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - java_util_HashSet_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, java_util_HashSet_clazz(env), - "getClass", - "()Ljava/lang/Class<*>;", - &g_java_util_HashSet_getClass); - - jclass ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -} // namespace JNI_HashSet - -#endif // java_util_HashSet_JNI diff --git a/android/jni_generator/testInnerClassNatives.golden b/android/jni_generator/testInnerClassNatives.golden deleted file mode 100644 index 2c89de9b8..000000000 --- a/android/jni_generator/testInnerClassNatives.golden +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/TestJni - -#ifndef org_chromium_TestJni_JNI -#define org_chromium_TestJni_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni[]; -const char kClassPath_org_chromium_TestJni[] = "org/chromium/TestJni"; - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni_00024MyInnerClass[]; -const char kClassPath_org_chromium_TestJni_00024MyInnerClass[] = - "org/chromium/TestJni$MyInnerClass"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_clazz = 0; -#ifndef org_chromium_TestJni_clazz_defined -#define org_chromium_TestJni_clazz_defined -inline jclass org_chromium_TestJni_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni, - &g_org_chromium_TestJni_clazz); -} -#endif -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_00024MyInnerClass_clazz = 0; -#ifndef org_chromium_TestJni_00024MyInnerClass_clazz_defined -#define org_chromium_TestJni_00024MyInnerClass_clazz_defined -inline jclass org_chromium_TestJni_00024MyInnerClass_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni_00024MyInnerClass, - &g_org_chromium_TestJni_00024MyInnerClass_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -static jint JNI_MyInnerClass_Init(JNIEnv* env, const base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_00024MyInnerClass_nativeInit( - JNIEnv* env, - jobject jcaller) { - return JNI_MyInnerClass_Init(env, base::android::JavaParamRef(env, jcaller)); -} - - -#endif // org_chromium_TestJni_JNI diff --git a/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden b/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden deleted file mode 100644 index 0623b3782..000000000 --- a/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/TestJni - -#ifndef org_chromium_TestJni_JNI -#define org_chromium_TestJni_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni_00024MyOtherInnerClass[]; -const char kClassPath_org_chromium_TestJni_00024MyOtherInnerClass[] = - "org/chromium/TestJni$MyOtherInnerClass"; - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni[]; -const char kClassPath_org_chromium_TestJni[] = "org/chromium/TestJni"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_00024MyOtherInnerClass_clazz - = 0; -#ifndef org_chromium_TestJni_00024MyOtherInnerClass_clazz_defined -#define org_chromium_TestJni_00024MyOtherInnerClass_clazz_defined -inline jclass org_chromium_TestJni_00024MyOtherInnerClass_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni_00024MyOtherInnerClass, - &g_org_chromium_TestJni_00024MyOtherInnerClass_clazz); -} -#endif -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_clazz = 0; -#ifndef org_chromium_TestJni_clazz_defined -#define org_chromium_TestJni_clazz_defined -inline jclass org_chromium_TestJni_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni, - &g_org_chromium_TestJni_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -static jint JNI_TestJni_Init(JNIEnv* env, const base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_nativeInit( - JNIEnv* env, - jobject jcaller) { - return JNI_TestJni_Init(env, base::android::JavaParamRef(env, jcaller)); -} - -static jint JNI_MyOtherInnerClass_Init(JNIEnv* env, const base::android::JavaParamRef& - jcaller); - -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit( - JNIEnv* env, - jobject jcaller) { - return JNI_MyOtherInnerClass_Init(env, base::android::JavaParamRef(env, jcaller)); -} - - -#endif // org_chromium_TestJni_JNI diff --git a/android/jni_generator/testInnerClassNativesBothInnerAndOuterRegistrations.golden b/android/jni_generator/testInnerClassNativesBothInnerAndOuterRegistrations.golden deleted file mode 100644 index 3f91cf1ff..000000000 --- a/android/jni_generator/testInnerClassNativesBothInnerAndOuterRegistrations.golden +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2017 The Chromium 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 autogenerated by -// base/android/jni_generator/jni_registration_generator.py -// Please do not change its content. - -#ifndef HEADER_GUARD -#define HEADER_GUARD - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" -#include "base/android/jni_int_wrapper.h" - - -// Step 1: Forward declarations (classes). - -extern const char kClassPath_org_chromium_TestJni_00024MyOtherInnerClass[]; - -extern const char kClassPath_org_chromium_TestJni[]; -extern base::subtle::AtomicWord g_org_chromium_TestJni_00024MyOtherInnerClass_clazz; -#ifndef org_chromium_TestJni_00024MyOtherInnerClass_clazz_defined -#define org_chromium_TestJni_00024MyOtherInnerClass_clazz_defined -inline jclass org_chromium_TestJni_00024MyOtherInnerClass_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni_00024MyOtherInnerClass, - &g_org_chromium_TestJni_00024MyOtherInnerClass_clazz); -} -#endif -extern base::subtle::AtomicWord g_org_chromium_TestJni_clazz; -#ifndef org_chromium_TestJni_clazz_defined -#define org_chromium_TestJni_clazz_defined -inline jclass org_chromium_TestJni_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni, - &g_org_chromium_TestJni_clazz); -} -#endif - - -// Step 2: Forward declarations (methods). - -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_nativeInit( - JNIEnv* env, - jobject jcaller); -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit( - JNIEnv* env, - jobject jcaller); - - -// Step 3: Method declarations. - -static const JNINativeMethod kMethods_org_chromium_TestJni_00024MyOtherInnerClass[] = { - { "nativeInit", "()I", - reinterpret_cast(Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit) }, -}; - - -static const JNINativeMethod kMethods_org_chromium_TestJni[] = { - { "nativeInit", "()I", reinterpret_cast(Java_org_chromium_TestJni_nativeInit) }, -}; - - -JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) { - const int kMethods_org_chromium_TestJni_00024MyOtherInnerClassSize = - arraysize(kMethods_org_chromium_TestJni_00024MyOtherInnerClass); - if (env->RegisterNatives( - org_chromium_TestJni_00024MyOtherInnerClass_clazz(env), - kMethods_org_chromium_TestJni_00024MyOtherInnerClass, - kMethods_org_chromium_TestJni_00024MyOtherInnerClassSize) < 0) { - jni_generator::HandleRegistrationError(env, - org_chromium_TestJni_00024MyOtherInnerClass_clazz(env), - __FILE__); - return false; - } - - - const int kMethods_org_chromium_TestJniSize = - arraysize(kMethods_org_chromium_TestJni); - if (env->RegisterNatives( - org_chromium_TestJni_clazz(env), - kMethods_org_chromium_TestJni, - kMethods_org_chromium_TestJniSize) < 0) { - jni_generator::HandleRegistrationError(env, - org_chromium_TestJni_clazz(env), - __FILE__); - return false; - } - - return true; -} - - -// Step 4: Main dex and non-main dex registration functions. - -bool RegisterMainDexNatives(JNIEnv* env) { - if (!RegisterNative_org_chromium_TestJni(env)) - return false; - - return true; -} - -bool RegisterNonMainDexNatives(JNIEnv* env) { - - return true; -} - -#endif // HEADER_GUARD diff --git a/android/jni_generator/testInnerClassNativesMultiple.golden b/android/jni_generator/testInnerClassNativesMultiple.golden deleted file mode 100644 index a7eff72ba..000000000 --- a/android/jni_generator/testInnerClassNativesMultiple.golden +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/TestJni - -#ifndef org_chromium_TestJni_JNI -#define org_chromium_TestJni_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni_00024MyOtherInnerClass[]; -const char kClassPath_org_chromium_TestJni_00024MyOtherInnerClass[] = - "org/chromium/TestJni$MyOtherInnerClass"; - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni[]; -const char kClassPath_org_chromium_TestJni[] = "org/chromium/TestJni"; - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni_00024MyInnerClass[]; -const char kClassPath_org_chromium_TestJni_00024MyInnerClass[] = - "org/chromium/TestJni$MyInnerClass"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_00024MyOtherInnerClass_clazz - = 0; -#ifndef org_chromium_TestJni_00024MyOtherInnerClass_clazz_defined -#define org_chromium_TestJni_00024MyOtherInnerClass_clazz_defined -inline jclass org_chromium_TestJni_00024MyOtherInnerClass_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni_00024MyOtherInnerClass, - &g_org_chromium_TestJni_00024MyOtherInnerClass_clazz); -} -#endif -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_clazz = 0; -#ifndef org_chromium_TestJni_clazz_defined -#define org_chromium_TestJni_clazz_defined -inline jclass org_chromium_TestJni_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni, - &g_org_chromium_TestJni_clazz); -} -#endif -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_00024MyInnerClass_clazz = 0; -#ifndef org_chromium_TestJni_00024MyInnerClass_clazz_defined -#define org_chromium_TestJni_00024MyInnerClass_clazz_defined -inline jclass org_chromium_TestJni_00024MyInnerClass_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni_00024MyInnerClass, - &g_org_chromium_TestJni_00024MyInnerClass_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -static jint JNI_MyInnerClass_Init(JNIEnv* env, const base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_00024MyInnerClass_nativeInit( - JNIEnv* env, - jobject jcaller) { - return JNI_MyInnerClass_Init(env, base::android::JavaParamRef(env, jcaller)); -} - -static jint JNI_MyOtherInnerClass_Init(JNIEnv* env, const base::android::JavaParamRef& - jcaller); - -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit( - JNIEnv* env, - jobject jcaller) { - return JNI_MyOtherInnerClass_Init(env, base::android::JavaParamRef(env, jcaller)); -} - - -#endif // org_chromium_TestJni_JNI diff --git a/android/jni_generator/testInputStream.javap b/android/jni_generator/testInputStream.javap deleted file mode 100644 index 50ab617a3..000000000 --- a/android/jni_generator/testInputStream.javap +++ /dev/null @@ -1,228 +0,0 @@ -Compiled from "InputStream.java" -public abstract class java.io.InputStream extends java.lang.Object implements java.io.Closeable - SourceFile: "InputStream.java" - minor version: 0 - major version: 49 - Constant pool: -const #1 = Method #6.#39; // java/lang/Object."":()V -const #2 = class #40; // java/lang/RuntimeException -const #3 = String #41; // Stub! -const #4 = Method #2.#42; // java/lang/RuntimeException."":(Ljava/lang/String;)V -const #5 = class #43; // java/io/InputStream -const #6 = class #44; // java/lang/Object -const #7 = class #45; // java/io/Closeable -const #8 = Asciz ; -const #9 = Asciz ()V; -const #10 = Asciz Code; -const #11 = Asciz LineNumberTable; -const #12 = Asciz LocalVariableTable; -const #13 = Asciz this; -const #14 = Asciz Ljava/io/InputStream;; -const #15 = Asciz available; -const #16 = Asciz ()I; -const #17 = Asciz Exceptions; -const #18 = class #46; // java/io/IOException -const #19 = Asciz close; -const #20 = Asciz mark; -const #21 = Asciz (I)V; -const #22 = Asciz readlimit; -const #23 = Asciz I; -const #24 = Asciz markSupported; -const #25 = Asciz ()Z; -const #26 = Asciz read; -const #27 = Asciz ([B)I; -const #28 = Asciz buffer; -const #29 = Asciz [B; -const #30 = Asciz ([BII)I; -const #31 = Asciz byteOffset; -const #32 = Asciz byteCount; -const #33 = Asciz reset; -const #34 = Asciz skip; -const #35 = Asciz (J)J; -const #36 = Asciz J; -const #37 = Asciz SourceFile; -const #38 = Asciz InputStream.java; -const #39 = NameAndType #8:#9;// "":()V -const #40 = Asciz java/lang/RuntimeException; -const #41 = Asciz Stub!; -const #42 = NameAndType #8:#47;// "":(Ljava/lang/String;)V -const #43 = Asciz java/io/InputStream; -const #44 = Asciz java/lang/Object; -const #45 = Asciz java/io/Closeable; -const #46 = Asciz java/io/IOException; -const #47 = Asciz (Ljava/lang/String;)V; - -{ -public java.io.InputStream(); - Signature: ()V - Code: - Stack=3, Locals=1, Args_size=1 - 0: aload_0 - 1: invokespecial #1; //Method java/lang/Object."":()V - 4: new #2; //class java/lang/RuntimeException - 7: dup - 8: ldc #3; //String Stub! - 10: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 13: athrow - LineNumberTable: - line 5: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 14 0 this Ljava/io/InputStream; - - -public int available() throws java.io.IOException; - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 6: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Ljava/io/InputStream; - - Exceptions: - throws java.io.IOException -public void close() throws java.io.IOException; - Signature: ()V - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 7: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Ljava/io/InputStream; - - Exceptions: - throws java.io.IOException -public void mark(int); - Signature: (I)V - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 8: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Ljava/io/InputStream; - 0 10 1 readlimit I - - -public boolean markSupported(); - Signature: ()Z - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 9: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Ljava/io/InputStream; - - -public abstract int read() throws java.io.IOException; - Signature: ()I - Exceptions: - throws java.io.IOException -public int read(byte[]) throws java.io.IOException; - Signature: ([B)I - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 11: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Ljava/io/InputStream; - 0 10 1 buffer [B - - Exceptions: - throws java.io.IOException -public int read(byte[], int, int) throws java.io.IOException; - Signature: ([BII)I - Code: - Stack=3, Locals=4, Args_size=4 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 12: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Ljava/io/InputStream; - 0 10 1 buffer [B - 0 10 2 byteOffset I - 0 10 3 byteCount I - - Exceptions: - throws java.io.IOException -public synchronized void reset() throws java.io.IOException; - Signature: ()V - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 13: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Ljava/io/InputStream; - - Exceptions: - throws java.io.IOException -public long skip(long) throws java.io.IOException; - Signature: (J)J - Code: - Stack=3, Locals=3, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 14: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Ljava/io/InputStream; - 0 10 1 byteCount J - - Exceptions: - throws java.io.IOException -} - diff --git a/android/jni_generator/testMotionEvent.javap b/android/jni_generator/testMotionEvent.javap deleted file mode 100644 index 0746943e6..000000000 --- a/android/jni_generator/testMotionEvent.javap +++ /dev/null @@ -1,2295 +0,0 @@ -Compiled from "MotionEvent.java" -public final class android.view.MotionEvent extends android.view.InputEvent implements android.os.Parcelable - SourceFile: "MotionEvent.java" - InnerClass: - public final #10= #9 of #6; //PointerProperties=class android/view/MotionEvent$PointerProperties of class android/view/MotionEvent - public final #13= #12 of #6; //PointerCoords=class android/view/MotionEvent$PointerCoords of class android/view/MotionEvent - public abstract #150= #149 of #8; //Creator=class android/os/Parcelable$Creator of class android/os/Parcelable - minor version: 0 - major version: 49 - Constant pool: -const #1 = Method #7.#293; // android/view/InputEvent."":()V -const #2 = class #294; // java/lang/RuntimeException -const #3 = String #295; // Stub! -const #4 = Method #2.#296; // java/lang/RuntimeException."":(Ljava/lang/String;)V -const #5 = Field #6.#297; // android/view/MotionEvent.CREATOR:Landroid/os/Parcelable$Creator; -const #6 = class #298; // android/view/MotionEvent -const #7 = class #299; // android/view/InputEvent -const #8 = class #300; // android/os/Parcelable -const #9 = class #301; // android/view/MotionEvent$PointerProperties -const #10 = Asciz PointerProperties; -const #11 = Asciz InnerClasses; -const #12 = class #302; // android/view/MotionEvent$PointerCoords -const #13 = Asciz PointerCoords; -const #14 = Asciz INVALID_POINTER_ID; -const #15 = Asciz I; -const #16 = Asciz ConstantValue; -const #17 = int -1; -const #18 = Asciz ACTION_MASK; -const #19 = int 255; -const #20 = Asciz ACTION_DOWN; -const #21 = int 0; -const #22 = Asciz ACTION_UP; -const #23 = int 1; -const #24 = Asciz ACTION_MOVE; -const #25 = int 2; -const #26 = Asciz ACTION_CANCEL; -const #27 = int 3; -const #28 = Asciz ACTION_OUTSIDE; -const #29 = int 4; -const #30 = Asciz ACTION_POINTER_DOWN; -const #31 = int 5; -const #32 = Asciz ACTION_POINTER_UP; -const #33 = int 6; -const #34 = Asciz ACTION_HOVER_MOVE; -const #35 = int 7; -const #36 = Asciz ACTION_SCROLL; -const #37 = int 8; -const #38 = Asciz ACTION_HOVER_ENTER; -const #39 = int 9; -const #40 = Asciz ACTION_HOVER_EXIT; -const #41 = int 10; -const #42 = Asciz ACTION_POINTER_INDEX_MASK; -const #43 = int 65280; -const #44 = Asciz ACTION_POINTER_INDEX_SHIFT; -const #45 = Asciz ACTION_POINTER_1_DOWN; -const #46 = Asciz Deprecated; -const #47 = Asciz RuntimeVisibleAnnotations; -const #48 = Asciz Ljava/lang/Deprecated;; -const #49 = Asciz ACTION_POINTER_2_DOWN; -const #50 = int 261; -const #51 = Asciz ACTION_POINTER_3_DOWN; -const #52 = int 517; -const #53 = Asciz ACTION_POINTER_1_UP; -const #54 = Asciz ACTION_POINTER_2_UP; -const #55 = int 262; -const #56 = Asciz ACTION_POINTER_3_UP; -const #57 = int 518; -const #58 = Asciz ACTION_POINTER_ID_MASK; -const #59 = Asciz ACTION_POINTER_ID_SHIFT; -const #60 = Asciz FLAG_WINDOW_IS_OBSCURED; -const #61 = Asciz EDGE_TOP; -const #62 = Asciz EDGE_BOTTOM; -const #63 = Asciz EDGE_LEFT; -const #64 = Asciz EDGE_RIGHT; -const #65 = Asciz AXIS_X; -const #66 = Asciz AXIS_Y; -const #67 = Asciz AXIS_PRESSURE; -const #68 = Asciz AXIS_SIZE; -const #69 = Asciz AXIS_TOUCH_MAJOR; -const #70 = Asciz AXIS_TOUCH_MINOR; -const #71 = Asciz AXIS_TOOL_MAJOR; -const #72 = Asciz AXIS_TOOL_MINOR; -const #73 = Asciz AXIS_ORIENTATION; -const #74 = Asciz AXIS_VSCROLL; -const #75 = Asciz AXIS_HSCROLL; -const #76 = Asciz AXIS_Z; -const #77 = int 11; -const #78 = Asciz AXIS_RX; -const #79 = int 12; -const #80 = Asciz AXIS_RY; -const #81 = int 13; -const #82 = Asciz AXIS_RZ; -const #83 = int 14; -const #84 = Asciz AXIS_HAT_X; -const #85 = int 15; -const #86 = Asciz AXIS_HAT_Y; -const #87 = int 16; -const #88 = Asciz AXIS_LTRIGGER; -const #89 = int 17; -const #90 = Asciz AXIS_RTRIGGER; -const #91 = int 18; -const #92 = Asciz AXIS_THROTTLE; -const #93 = int 19; -const #94 = Asciz AXIS_RUDDER; -const #95 = int 20; -const #96 = Asciz AXIS_WHEEL; -const #97 = int 21; -const #98 = Asciz AXIS_GAS; -const #99 = int 22; -const #100 = Asciz AXIS_BRAKE; -const #101 = int 23; -const #102 = Asciz AXIS_DISTANCE; -const #103 = int 24; -const #104 = Asciz AXIS_TILT; -const #105 = int 25; -const #106 = Asciz AXIS_GENERIC_1; -const #107 = int 32; -const #108 = Asciz AXIS_GENERIC_2; -const #109 = int 33; -const #110 = Asciz AXIS_GENERIC_3; -const #111 = int 34; -const #112 = Asciz AXIS_GENERIC_4; -const #113 = int 35; -const #114 = Asciz AXIS_GENERIC_5; -const #115 = int 36; -const #116 = Asciz AXIS_GENERIC_6; -const #117 = int 37; -const #118 = Asciz AXIS_GENERIC_7; -const #119 = int 38; -const #120 = Asciz AXIS_GENERIC_8; -const #121 = int 39; -const #122 = Asciz AXIS_GENERIC_9; -const #123 = int 40; -const #124 = Asciz AXIS_GENERIC_10; -const #125 = int 41; -const #126 = Asciz AXIS_GENERIC_11; -const #127 = int 42; -const #128 = Asciz AXIS_GENERIC_12; -const #129 = int 43; -const #130 = Asciz AXIS_GENERIC_13; -const #131 = int 44; -const #132 = Asciz AXIS_GENERIC_14; -const #133 = int 45; -const #134 = Asciz AXIS_GENERIC_15; -const #135 = int 46; -const #136 = Asciz AXIS_GENERIC_16; -const #137 = int 47; -const #138 = Asciz BUTTON_PRIMARY; -const #139 = Asciz BUTTON_SECONDARY; -const #140 = Asciz BUTTON_TERTIARY; -const #141 = Asciz BUTTON_BACK; -const #142 = Asciz BUTTON_FORWARD; -const #143 = Asciz TOOL_TYPE_UNKNOWN; -const #144 = Asciz TOOL_TYPE_FINGER; -const #145 = Asciz TOOL_TYPE_STYLUS; -const #146 = Asciz TOOL_TYPE_MOUSE; -const #147 = Asciz TOOL_TYPE_ERASER; -const #148 = Asciz CREATOR; -const #149 = class #303; // android/os/Parcelable$Creator -const #150 = Asciz Creator; -const #151 = Asciz Landroid/os/Parcelable$Creator;; -const #152 = Asciz Signature; -const #153 = Asciz Landroid/os/Parcelable$Creator;; -const #154 = Asciz ; -const #155 = Asciz ()V; -const #156 = Asciz Code; -const #157 = Asciz LineNumberTable; -const #158 = Asciz LocalVariableTable; -const #159 = Asciz this; -const #160 = Asciz Landroid/view/MotionEvent;; -const #161 = Asciz finalize; -const #162 = Asciz Exceptions; -const #163 = class #304; // java/lang/Throwable -const #164 = Asciz obtain; -const #165 = Asciz (JJII[Landroid/view/MotionEvent$PointerProperties;[Landroid/view/MotionEvent$PointerCoords;IIFFIIII)Landroid/view/MotionEvent;; -const #166 = Asciz downTime; -const #167 = Asciz J; -const #168 = Asciz eventTime; -const #169 = Asciz action; -const #170 = Asciz pointerCount; -const #171 = Asciz pointerProperties; -const #172 = Asciz [Landroid/view/MotionEvent$PointerProperties;; -const #173 = Asciz pointerCoords; -const #174 = Asciz [Landroid/view/MotionEvent$PointerCoords;; -const #175 = Asciz metaState; -const #176 = Asciz buttonState; -const #177 = Asciz xPrecision; -const #178 = Asciz F; -const #179 = Asciz yPrecision; -const #180 = Asciz deviceId; -const #181 = Asciz edgeFlags; -const #182 = Asciz source; -const #183 = Asciz flags; -const #184 = Asciz (JJII[I[Landroid/view/MotionEvent$PointerCoords;IFFIIII)Landroid/view/MotionEvent;; -const #185 = Asciz pointerIds; -const #186 = Asciz [I; -const #187 = Asciz (JJIFFFFIFFII)Landroid/view/MotionEvent;; -const #188 = Asciz x; -const #189 = Asciz y; -const #190 = Asciz pressure; -const #191 = Asciz size; -const #192 = Asciz (JJIIFFFFIFFII)Landroid/view/MotionEvent;; -const #193 = Asciz (JJIFFI)Landroid/view/MotionEvent;; -const #194 = Asciz (Landroid/view/MotionEvent;)Landroid/view/MotionEvent;; -const #195 = Asciz other; -const #196 = Asciz obtainNoHistory; -const #197 = Asciz recycle; -const #198 = Asciz getDeviceId; -const #199 = Asciz ()I; -const #200 = Asciz getSource; -const #201 = Asciz setSource; -const #202 = Asciz (I)V; -const #203 = Asciz getAction; -const #204 = Asciz getActionMasked; -const #205 = Asciz getActionIndex; -const #206 = Asciz getFlags; -const #207 = Asciz getDownTime; -const #208 = Asciz ()J; -const #209 = Asciz getEventTime; -const #210 = Asciz getX; -const #211 = Asciz ()F; -const #212 = Asciz getY; -const #213 = Asciz getPressure; -const #214 = Asciz getSize; -const #215 = Asciz getTouchMajor; -const #216 = Asciz getTouchMinor; -const #217 = Asciz getToolMajor; -const #218 = Asciz getToolMinor; -const #219 = Asciz getOrientation; -const #220 = Asciz getAxisValue; -const #221 = Asciz (I)F; -const #222 = Asciz axis; -const #223 = Asciz getPointerCount; -const #224 = Asciz getPointerId; -const #225 = Asciz (I)I; -const #226 = Asciz pointerIndex; -const #227 = Asciz getToolType; -const #228 = Asciz findPointerIndex; -const #229 = Asciz pointerId; -const #230 = Asciz (II)F; -const #231 = Asciz getPointerCoords; -const #232 = Asciz (ILandroid/view/MotionEvent$PointerCoords;)V; -const #233 = Asciz outPointerCoords; -const #234 = Asciz Landroid/view/MotionEvent$PointerCoords;; -const #235 = Asciz getPointerProperties; -const #236 = Asciz (ILandroid/view/MotionEvent$PointerProperties;)V; -const #237 = Asciz outPointerProperties; -const #238 = Asciz Landroid/view/MotionEvent$PointerProperties;; -const #239 = Asciz getMetaState; -const #240 = Asciz getButtonState; -const #241 = Asciz getRawX; -const #242 = Asciz getRawY; -const #243 = Asciz getXPrecision; -const #244 = Asciz getYPrecision; -const #245 = Asciz getHistorySize; -const #246 = Asciz getHistoricalEventTime; -const #247 = Asciz (I)J; -const #248 = Asciz pos; -const #249 = Asciz getHistoricalX; -const #250 = Asciz getHistoricalY; -const #251 = Asciz getHistoricalPressure; -const #252 = Asciz getHistoricalSize; -const #253 = Asciz getHistoricalTouchMajor; -const #254 = Asciz getHistoricalTouchMinor; -const #255 = Asciz getHistoricalToolMajor; -const #256 = Asciz getHistoricalToolMinor; -const #257 = Asciz getHistoricalOrientation; -const #258 = Asciz getHistoricalAxisValue; -const #259 = Asciz (III)F; -const #260 = Asciz getHistoricalPointerCoords; -const #261 = Asciz (IILandroid/view/MotionEvent$PointerCoords;)V; -const #262 = Asciz getEdgeFlags; -const #263 = Asciz setEdgeFlags; -const #264 = Asciz setAction; -const #265 = Asciz offsetLocation; -const #266 = Asciz (FF)V; -const #267 = Asciz deltaX; -const #268 = Asciz deltaY; -const #269 = Asciz setLocation; -const #270 = Asciz transform; -const #271 = Asciz (Landroid/graphics/Matrix;)V; -const #272 = Asciz matrix; -const #273 = Asciz Landroid/graphics/Matrix;; -const #274 = Asciz addBatch; -const #275 = Asciz (JFFFFI)V; -const #276 = Asciz (J[Landroid/view/MotionEvent$PointerCoords;I)V; -const #277 = Asciz toString; -const #278 = Asciz ()Ljava/lang/String;; -const #279 = Asciz actionToString; -const #280 = Asciz (I)Ljava/lang/String;; -const #281 = Asciz axisToString; -const #282 = Asciz axisFromString; -const #283 = Asciz (Ljava/lang/String;)I; -const #284 = Asciz symbolicName; -const #285 = Asciz Ljava/lang/String;; -const #286 = Asciz writeToParcel; -const #287 = Asciz (Landroid/os/Parcel;I)V; -const #288 = Asciz out; -const #289 = Asciz Landroid/os/Parcel;; -const #290 = Asciz ; -const #291 = Asciz SourceFile; -const #292 = Asciz MotionEvent.java; -const #293 = NameAndType #154:#155;// "":()V -const #294 = Asciz java/lang/RuntimeException; -const #295 = Asciz Stub!; -const #296 = NameAndType #154:#305;// "":(Ljava/lang/String;)V -const #297 = NameAndType #148:#151;// CREATOR:Landroid/os/Parcelable$Creator; -const #298 = Asciz android/view/MotionEvent; -const #299 = Asciz android/view/InputEvent; -const #300 = Asciz android/os/Parcelable; -const #301 = Asciz android/view/MotionEvent$PointerProperties; -const #302 = Asciz android/view/MotionEvent$PointerCoords; -const #303 = Asciz android/os/Parcelable$Creator; -const #304 = Asciz java/lang/Throwable; -const #305 = Asciz (Ljava/lang/String;)V; - -{ -public static final int INVALID_POINTER_ID; - Signature: I - Constant value: int -1 - -public static final int ACTION_MASK; - Signature: I - Constant value: int 255 - -public static final int ACTION_DOWN; - Signature: I - Constant value: int 0 - -public static final int ACTION_UP; - Signature: I - Constant value: int 1 - -public static final int ACTION_MOVE; - Signature: I - Constant value: int 2 - -public static final int ACTION_CANCEL; - Signature: I - Constant value: int 3 - -public static final int ACTION_OUTSIDE; - Signature: I - Constant value: int 4 - -public static final int ACTION_POINTER_DOWN; - Signature: I - Constant value: int 5 - -public static final int ACTION_POINTER_UP; - Signature: I - Constant value: int 6 - -public static final int ACTION_HOVER_MOVE; - Signature: I - Constant value: int 7 - -public static final int ACTION_SCROLL; - Signature: I - Constant value: int 8 - -public static final int ACTION_HOVER_ENTER; - Signature: I - Constant value: int 9 - -public static final int ACTION_HOVER_EXIT; - Signature: I - Constant value: int 10 - -public static final int ACTION_POINTER_INDEX_MASK; - Signature: I - Constant value: int 65280 - -public static final int ACTION_POINTER_INDEX_SHIFT; - Signature: I - Constant value: int 8 - -public static final int ACTION_POINTER_1_DOWN; - Signature: I - Constant value: int 5Deprecated: true - RuntimeVisibleAnnotations: length = 0x6 - 00 01 00 30 00 00 - - -public static final int ACTION_POINTER_2_DOWN; - Signature: I - Constant value: int 261Deprecated: true - RuntimeVisibleAnnotations: length = 0x6 - 00 01 00 30 00 00 - - -public static final int ACTION_POINTER_3_DOWN; - Signature: I - Constant value: int 517Deprecated: true - RuntimeVisibleAnnotations: length = 0x6 - 00 01 00 30 00 00 - - -public static final int ACTION_POINTER_1_UP; - Signature: I - Constant value: int 6Deprecated: true - RuntimeVisibleAnnotations: length = 0x6 - 00 01 00 30 00 00 - - -public static final int ACTION_POINTER_2_UP; - Signature: I - Constant value: int 262Deprecated: true - RuntimeVisibleAnnotations: length = 0x6 - 00 01 00 30 00 00 - - -public static final int ACTION_POINTER_3_UP; - Signature: I - Constant value: int 518Deprecated: true - RuntimeVisibleAnnotations: length = 0x6 - 00 01 00 30 00 00 - - -public static final int ACTION_POINTER_ID_MASK; - Signature: I - Constant value: int 65280Deprecated: true - RuntimeVisibleAnnotations: length = 0x6 - 00 01 00 30 00 00 - - -public static final int ACTION_POINTER_ID_SHIFT; - Signature: I - Constant value: int 8Deprecated: true - RuntimeVisibleAnnotations: length = 0x6 - 00 01 00 30 00 00 - - -public static final int FLAG_WINDOW_IS_OBSCURED; - Signature: I - Constant value: int 1 - -public static final int EDGE_TOP; - Signature: I - Constant value: int 1 - -public static final int EDGE_BOTTOM; - Signature: I - Constant value: int 2 - -public static final int EDGE_LEFT; - Signature: I - Constant value: int 4 - -public static final int EDGE_RIGHT; - Signature: I - Constant value: int 8 - -public static final int AXIS_X; - Signature: I - Constant value: int 0 - -public static final int AXIS_Y; - Signature: I - Constant value: int 1 - -public static final int AXIS_PRESSURE; - Signature: I - Constant value: int 2 - -public static final int AXIS_SIZE; - Signature: I - Constant value: int 3 - -public static final int AXIS_TOUCH_MAJOR; - Signature: I - Constant value: int 4 - -public static final int AXIS_TOUCH_MINOR; - Signature: I - Constant value: int 5 - -public static final int AXIS_TOOL_MAJOR; - Signature: I - Constant value: int 6 - -public static final int AXIS_TOOL_MINOR; - Signature: I - Constant value: int 7 - -public static final int AXIS_ORIENTATION; - Signature: I - Constant value: int 8 - -public static final int AXIS_VSCROLL; - Signature: I - Constant value: int 9 - -public static final int AXIS_HSCROLL; - Signature: I - Constant value: int 10 - -public static final int AXIS_Z; - Signature: I - Constant value: int 11 - -public static final int AXIS_RX; - Signature: I - Constant value: int 12 - -public static final int AXIS_RY; - Signature: I - Constant value: int 13 - -public static final int AXIS_RZ; - Signature: I - Constant value: int 14 - -public static final int AXIS_HAT_X; - Signature: I - Constant value: int 15 - -public static final int AXIS_HAT_Y; - Signature: I - Constant value: int 16 - -public static final int AXIS_LTRIGGER; - Signature: I - Constant value: int 17 - -public static final int AXIS_RTRIGGER; - Signature: I - Constant value: int 18 - -public static final int AXIS_THROTTLE; - Signature: I - Constant value: int 19 - -public static final int AXIS_RUDDER; - Signature: I - Constant value: int 20 - -public static final int AXIS_WHEEL; - Signature: I - Constant value: int 21 - -public static final int AXIS_GAS; - Signature: I - Constant value: int 22 - -public static final int AXIS_BRAKE; - Signature: I - Constant value: int 23 - -public static final int AXIS_DISTANCE; - Signature: I - Constant value: int 24 - -public static final int AXIS_TILT; - Signature: I - Constant value: int 25 - -public static final int AXIS_GENERIC_1; - Signature: I - Constant value: int 32 - -public static final int AXIS_GENERIC_2; - Signature: I - Constant value: int 33 - -public static final int AXIS_GENERIC_3; - Signature: I - Constant value: int 34 - -public static final int AXIS_GENERIC_4; - Signature: I - Constant value: int 35 - -public static final int AXIS_GENERIC_5; - Signature: I - Constant value: int 36 - -public static final int AXIS_GENERIC_6; - Signature: I - Constant value: int 37 - -public static final int AXIS_GENERIC_7; - Signature: I - Constant value: int 38 - -public static final int AXIS_GENERIC_8; - Signature: I - Constant value: int 39 - -public static final int AXIS_GENERIC_9; - Signature: I - Constant value: int 40 - -public static final int AXIS_GENERIC_10; - Signature: I - Constant value: int 41 - -public static final int AXIS_GENERIC_11; - Signature: I - Constant value: int 42 - -public static final int AXIS_GENERIC_12; - Signature: I - Constant value: int 43 - -public static final int AXIS_GENERIC_13; - Signature: I - Constant value: int 44 - -public static final int AXIS_GENERIC_14; - Signature: I - Constant value: int 45 - -public static final int AXIS_GENERIC_15; - Signature: I - Constant value: int 46 - -public static final int AXIS_GENERIC_16; - Signature: I - Constant value: int 47 - -public static final int BUTTON_PRIMARY; - Signature: I - Constant value: int 1 - -public static final int BUTTON_SECONDARY; - Signature: I - Constant value: int 2 - -public static final int BUTTON_TERTIARY; - Signature: I - Constant value: int 4 - -public static final int BUTTON_BACK; - Signature: I - Constant value: int 8 - -public static final int BUTTON_FORWARD; - Signature: I - Constant value: int 16 - -public static final int TOOL_TYPE_UNKNOWN; - Signature: I - Constant value: int 0 - -public static final int TOOL_TYPE_FINGER; - Signature: I - Constant value: int 1 - -public static final int TOOL_TYPE_STYLUS; - Signature: I - Constant value: int 2 - -public static final int TOOL_TYPE_MOUSE; - Signature: I - Constant value: int 3 - -public static final int TOOL_TYPE_ERASER; - Signature: I - Constant value: int 4 - -public static final android.os.Parcelable$Creator CREATOR; - Signature: Landroid/os/Parcelable$Creator; - Signature: length = 0x2 - 00 FFFFFF99 - - -android.view.MotionEvent(); - Signature: ()V - Code: - Stack=3, Locals=1, Args_size=1 - 0: aload_0 - 1: invokespecial #1; //Method android/view/InputEvent."":()V - 4: new #2; //class java/lang/RuntimeException - 7: dup - 8: ldc #3; //String Stub! - 10: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 13: athrow - LineNumberTable: - line 35: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 14 0 this Landroid/view/MotionEvent; - - -protected void finalize() throws java.lang.Throwable; - Signature: ()V - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 36: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - Exceptions: - throws java.lang.Throwable -public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent$PointerProperties[], android.view.MotionEvent$PointerCoords[], int, int, float, float, int, int, int, int); - Signature: (JJII[Landroid/view/MotionEvent$PointerProperties;[Landroid/view/MotionEvent$PointerCoords;IIFFIIII)Landroid/view/MotionEvent; - Code: - Stack=3, Locals=16, Args_size=14 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 37: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 downTime J - 0 10 2 eventTime J - 0 10 4 action I - 0 10 5 pointerCount I - 0 10 6 pointerProperties [Landroid/view/MotionEvent$PointerProperties; - 0 10 7 pointerCoords [Landroid/view/MotionEvent$PointerCoords; - 0 10 8 metaState I - 0 10 9 buttonState I - 0 10 10 xPrecision F - 0 10 11 yPrecision F - 0 10 12 deviceId I - 0 10 13 edgeFlags I - 0 10 14 source I - 0 10 15 flags I - - -public static android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent$PointerCoords[], int, float, float, int, int, int, int); - Signature: (JJII[I[Landroid/view/MotionEvent$PointerCoords;IFFIIII)Landroid/view/MotionEvent; - Code: - Stack=3, Locals=15, Args_size=13 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 39: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 downTime J - 0 10 2 eventTime J - 0 10 4 action I - 0 10 5 pointerCount I - 0 10 6 pointerIds [I - 0 10 7 pointerCoords [Landroid/view/MotionEvent$PointerCoords; - 0 10 8 metaState I - 0 10 9 xPrecision F - 0 10 10 yPrecision F - 0 10 11 deviceId I - 0 10 12 edgeFlags I - 0 10 13 source I - 0 10 14 flags I - - Deprecated: true - RuntimeVisibleAnnotations: length = 0x6 - 00 01 00 30 00 00 - -public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int); - Signature: (JJIFFFFIFFII)Landroid/view/MotionEvent; - Code: - Stack=3, Locals=14, Args_size=12 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 40: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 downTime J - 0 10 2 eventTime J - 0 10 4 action I - 0 10 5 x F - 0 10 6 y F - 0 10 7 pressure F - 0 10 8 size F - 0 10 9 metaState I - 0 10 10 xPrecision F - 0 10 11 yPrecision F - 0 10 12 deviceId I - 0 10 13 edgeFlags I - - -public static android.view.MotionEvent obtain(long, long, int, int, float, float, float, float, int, float, float, int, int); - Signature: (JJIIFFFFIFFII)Landroid/view/MotionEvent; - Code: - Stack=3, Locals=15, Args_size=13 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 42: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 downTime J - 0 10 2 eventTime J - 0 10 4 action I - 0 10 5 pointerCount I - 0 10 6 x F - 0 10 7 y F - 0 10 8 pressure F - 0 10 9 size F - 0 10 10 metaState I - 0 10 11 xPrecision F - 0 10 12 yPrecision F - 0 10 13 deviceId I - 0 10 14 edgeFlags I - - Deprecated: true - RuntimeVisibleAnnotations: length = 0x6 - 00 01 00 30 00 00 - -public static android.view.MotionEvent obtain(long, long, int, float, float, int); - Signature: (JJIFFI)Landroid/view/MotionEvent; - Code: - Stack=3, Locals=8, Args_size=6 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 43: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 downTime J - 0 10 2 eventTime J - 0 10 4 action I - 0 10 5 x F - 0 10 6 y F - 0 10 7 metaState I - - -public static android.view.MotionEvent obtain(android.view.MotionEvent); - Signature: (Landroid/view/MotionEvent;)Landroid/view/MotionEvent; - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 44: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 other Landroid/view/MotionEvent; - - -public static android.view.MotionEvent obtainNoHistory(android.view.MotionEvent); - Signature: (Landroid/view/MotionEvent;)Landroid/view/MotionEvent; - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 45: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 other Landroid/view/MotionEvent; - - -public final void recycle(); - Signature: ()V - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 46: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final int getDeviceId(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 47: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final int getSource(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 48: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final void setSource(int); - Signature: (I)V - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 49: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 source I - - -public final int getAction(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 50: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final int getActionMasked(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 51: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final int getActionIndex(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 52: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final int getFlags(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 53: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final long getDownTime(); - Signature: ()J - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 54: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final long getEventTime(); - Signature: ()J - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 55: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getX(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 56: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getY(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 57: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getPressure(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 58: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getSize(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 59: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getTouchMajor(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 60: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getTouchMinor(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 61: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getToolMajor(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 62: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getToolMinor(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 63: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getOrientation(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 64: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getAxisValue(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 65: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 axis I - - -public final int getPointerCount(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 66: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final int getPointerId(int); - Signature: (I)I - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 67: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final int getToolType(int); - Signature: (I)I - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 68: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final int findPointerIndex(int); - Signature: (I)I - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 69: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerId I - - -public final float getX(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 70: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final float getY(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 71: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final float getPressure(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 72: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final float getSize(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 73: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final float getTouchMajor(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 74: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final float getTouchMinor(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 75: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final float getToolMajor(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 76: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final float getToolMinor(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 77: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final float getOrientation(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 78: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - -public final float getAxisValue(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 79: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 axis I - 0 10 2 pointerIndex I - - -public final void getPointerCoords(int, android.view.MotionEvent$PointerCoords); - Signature: (ILandroid/view/MotionEvent$PointerCoords;)V - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 80: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 outPointerCoords Landroid/view/MotionEvent$PointerCoords; - - -public final void getPointerProperties(int, android.view.MotionEvent$PointerProperties); - Signature: (ILandroid/view/MotionEvent$PointerProperties;)V - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 81: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 outPointerProperties Landroid/view/MotionEvent$PointerProperties; - - -public final int getMetaState(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 82: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final int getButtonState(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 83: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getRawX(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 84: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getRawY(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 85: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getXPrecision(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 86: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final float getYPrecision(); - Signature: ()F - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 87: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final int getHistorySize(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 88: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final long getHistoricalEventTime(int); - Signature: (I)J - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 89: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - -public final float getHistoricalX(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 90: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - -public final float getHistoricalY(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 91: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - -public final float getHistoricalPressure(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 92: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - -public final float getHistoricalSize(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 93: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - -public final float getHistoricalTouchMajor(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 94: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - -public final float getHistoricalTouchMinor(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 95: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - -public final float getHistoricalToolMajor(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 96: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - -public final float getHistoricalToolMinor(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 97: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - -public final float getHistoricalOrientation(int); - Signature: (I)F - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 98: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - -public final float getHistoricalAxisValue(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 99: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 axis I - 0 10 2 pos I - - -public final float getHistoricalX(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 100: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - -public final float getHistoricalY(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 101: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - -public final float getHistoricalPressure(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 102: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - -public final float getHistoricalSize(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 103: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - -public final float getHistoricalTouchMajor(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 104: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - -public final float getHistoricalTouchMinor(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 105: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - -public final float getHistoricalToolMajor(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 106: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - -public final float getHistoricalToolMinor(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 107: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - -public final float getHistoricalOrientation(int, int); - Signature: (II)F - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 108: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - -public final float getHistoricalAxisValue(int, int, int); - Signature: (III)F - Code: - Stack=3, Locals=4, Args_size=4 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 109: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 axis I - 0 10 2 pointerIndex I - 0 10 3 pos I - - -public final void getHistoricalPointerCoords(int, int, android.view.MotionEvent$PointerCoords); - Signature: (IILandroid/view/MotionEvent$PointerCoords;)V - Code: - Stack=3, Locals=4, Args_size=4 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 110: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - 0 10 3 outPointerCoords Landroid/view/MotionEvent$PointerCoords; - - -public final int getEdgeFlags(); - Signature: ()I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 111: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public final void setEdgeFlags(int); - Signature: (I)V - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 112: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 flags I - - -public final void setAction(int); - Signature: (I)V - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 113: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 action I - - -public final void offsetLocation(float, float); - Signature: (FF)V - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 114: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 deltaX F - 0 10 2 deltaY F - - -public final void setLocation(float, float); - Signature: (FF)V - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 115: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 x F - 0 10 2 y F - - -public final void transform(android.graphics.Matrix); - Signature: (Landroid/graphics/Matrix;)V - Code: - Stack=3, Locals=2, Args_size=2 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 116: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 matrix Landroid/graphics/Matrix; - - -public final void addBatch(long, float, float, float, float, int); - Signature: (JFFFFI)V - Code: - Stack=3, Locals=8, Args_size=7 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 117: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 eventTime J - 0 10 3 x F - 0 10 4 y F - 0 10 5 pressure F - 0 10 6 size F - 0 10 7 metaState I - - -public final void addBatch(long, android.view.MotionEvent$PointerCoords[], int); - Signature: (J[Landroid/view/MotionEvent$PointerCoords;I)V - Code: - Stack=3, Locals=5, Args_size=4 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 118: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 eventTime J - 0 10 3 pointerCoords [Landroid/view/MotionEvent$PointerCoords; - 0 10 4 metaState I - - -public java.lang.String toString(); - Signature: ()Ljava/lang/String; - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 119: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - -public static java.lang.String actionToString(int); - Signature: (I)Ljava/lang/String; - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 120: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 action I - - -public static java.lang.String axisToString(int); - Signature: (I)Ljava/lang/String; - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 121: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 axis I - - -public static int axisFromString(java.lang.String); - Signature: (Ljava/lang/String;)I - Code: - Stack=3, Locals=1, Args_size=1 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 122: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 symbolicName Ljava/lang/String; - - -public void writeToParcel(android.os.Parcel, int); - Signature: (Landroid/os/Parcel;I)V - Code: - Stack=3, Locals=3, Args_size=3 - 0: new #2; //class java/lang/RuntimeException - 3: dup - 4: ldc #3; //String Stub! - 6: invokespecial #4; //Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 123: 0 - - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 out Landroid/os/Parcel; - 0 10 2 flags I - - -static {}; - Signature: ()V - Code: - Stack=1, Locals=0, Args_size=0 - 0: aconst_null - 1: putstatic #5; //Field CREATOR:Landroid/os/Parcelable$Creator; - 4: return - LineNumberTable: - line 213: 0 - - -} - diff --git a/android/jni_generator/testMotionEvent.javap7 b/android/jni_generator/testMotionEvent.javap7 deleted file mode 100644 index f4f544436..000000000 --- a/android/jni_generator/testMotionEvent.javap7 +++ /dev/null @@ -1,2370 +0,0 @@ -Classfile out_android/Debug/gen/content/jni/android/view/MotionEvent.class - Last modified Feb 27, 2014; size 13369 bytes - MD5 checksum 3718d77a994cb8aceb7b35c5df3c4dd1 - Compiled from "MotionEvent.java" -public final class android.view.MotionEvent extends android.view.InputEvent implements android.os.Parcelable - SourceFile: "MotionEvent.java" - InnerClasses: - public static final #10= #9 of #6; //PointerProperties=class android/view/MotionEvent$PointerProperties of class android/view/MotionEvent - public static final #13= #12 of #6; //PointerCoords=class android/view/MotionEvent$PointerCoords of class android/view/MotionEvent - public static #150= #149 of #8; //Creator=class android/os/Parcelable$Creator of class android/os/Parcelable - minor version: 0 - major version: 49 - flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER -Constant pool: - #1 = Methodref #7.#293 // android/view/InputEvent."":()V - #2 = Class #294 // java/lang/RuntimeException - #3 = String #295 // Stub! - #4 = Methodref #2.#296 // java/lang/RuntimeException."":(Ljava/lang/String;)V - #5 = Fieldref #6.#297 // android/view/MotionEvent.CREATOR:Landroid/os/Parcelable$Creator; - #6 = Class #298 // android/view/MotionEvent - #7 = Class #299 // android/view/InputEvent - #8 = Class #300 // android/os/Parcelable - #9 = Class #301 // android/view/MotionEvent$PointerProperties - #10 = Utf8 PointerProperties - #11 = Utf8 InnerClasses - #12 = Class #302 // android/view/MotionEvent$PointerCoords - #13 = Utf8 PointerCoords - #14 = Utf8 INVALID_POINTER_ID - #15 = Utf8 I - #16 = Utf8 ConstantValue - #17 = Integer -1 - #18 = Utf8 ACTION_MASK - #19 = Integer 255 - #20 = Utf8 ACTION_DOWN - #21 = Integer 0 - #22 = Utf8 ACTION_UP - #23 = Integer 1 - #24 = Utf8 ACTION_MOVE - #25 = Integer 2 - #26 = Utf8 ACTION_CANCEL - #27 = Integer 3 - #28 = Utf8 ACTION_OUTSIDE - #29 = Integer 4 - #30 = Utf8 ACTION_POINTER_DOWN - #31 = Integer 5 - #32 = Utf8 ACTION_POINTER_UP - #33 = Integer 6 - #34 = Utf8 ACTION_HOVER_MOVE - #35 = Integer 7 - #36 = Utf8 ACTION_SCROLL - #37 = Integer 8 - #38 = Utf8 ACTION_HOVER_ENTER - #39 = Integer 9 - #40 = Utf8 ACTION_HOVER_EXIT - #41 = Integer 10 - #42 = Utf8 ACTION_POINTER_INDEX_MASK - #43 = Integer 65280 - #44 = Utf8 ACTION_POINTER_INDEX_SHIFT - #45 = Utf8 ACTION_POINTER_1_DOWN - #46 = Utf8 Deprecated - #47 = Utf8 RuntimeVisibleAnnotations - #48 = Utf8 Ljava/lang/Deprecated; - #49 = Utf8 ACTION_POINTER_2_DOWN - #50 = Integer 261 - #51 = Utf8 ACTION_POINTER_3_DOWN - #52 = Integer 517 - #53 = Utf8 ACTION_POINTER_1_UP - #54 = Utf8 ACTION_POINTER_2_UP - #55 = Integer 262 - #56 = Utf8 ACTION_POINTER_3_UP - #57 = Integer 518 - #58 = Utf8 ACTION_POINTER_ID_MASK - #59 = Utf8 ACTION_POINTER_ID_SHIFT - #60 = Utf8 FLAG_WINDOW_IS_OBSCURED - #61 = Utf8 EDGE_TOP - #62 = Utf8 EDGE_BOTTOM - #63 = Utf8 EDGE_LEFT - #64 = Utf8 EDGE_RIGHT - #65 = Utf8 AXIS_X - #66 = Utf8 AXIS_Y - #67 = Utf8 AXIS_PRESSURE - #68 = Utf8 AXIS_SIZE - #69 = Utf8 AXIS_TOUCH_MAJOR - #70 = Utf8 AXIS_TOUCH_MINOR - #71 = Utf8 AXIS_TOOL_MAJOR - #72 = Utf8 AXIS_TOOL_MINOR - #73 = Utf8 AXIS_ORIENTATION - #74 = Utf8 AXIS_VSCROLL - #75 = Utf8 AXIS_HSCROLL - #76 = Utf8 AXIS_Z - #77 = Integer 11 - #78 = Utf8 AXIS_RX - #79 = Integer 12 - #80 = Utf8 AXIS_RY - #81 = Integer 13 - #82 = Utf8 AXIS_RZ - #83 = Integer 14 - #84 = Utf8 AXIS_HAT_X - #85 = Integer 15 - #86 = Utf8 AXIS_HAT_Y - #87 = Integer 16 - #88 = Utf8 AXIS_LTRIGGER - #89 = Integer 17 - #90 = Utf8 AXIS_RTRIGGER - #91 = Integer 18 - #92 = Utf8 AXIS_THROTTLE - #93 = Integer 19 - #94 = Utf8 AXIS_RUDDER - #95 = Integer 20 - #96 = Utf8 AXIS_WHEEL - #97 = Integer 21 - #98 = Utf8 AXIS_GAS - #99 = Integer 22 - #100 = Utf8 AXIS_BRAKE - #101 = Integer 23 - #102 = Utf8 AXIS_DISTANCE - #103 = Integer 24 - #104 = Utf8 AXIS_TILT - #105 = Integer 25 - #106 = Utf8 AXIS_GENERIC_1 - #107 = Integer 32 - #108 = Utf8 AXIS_GENERIC_2 - #109 = Integer 33 - #110 = Utf8 AXIS_GENERIC_3 - #111 = Integer 34 - #112 = Utf8 AXIS_GENERIC_4 - #113 = Integer 35 - #114 = Utf8 AXIS_GENERIC_5 - #115 = Integer 36 - #116 = Utf8 AXIS_GENERIC_6 - #117 = Integer 37 - #118 = Utf8 AXIS_GENERIC_7 - #119 = Integer 38 - #120 = Utf8 AXIS_GENERIC_8 - #121 = Integer 39 - #122 = Utf8 AXIS_GENERIC_9 - #123 = Integer 40 - #124 = Utf8 AXIS_GENERIC_10 - #125 = Integer 41 - #126 = Utf8 AXIS_GENERIC_11 - #127 = Integer 42 - #128 = Utf8 AXIS_GENERIC_12 - #129 = Integer 43 - #130 = Utf8 AXIS_GENERIC_13 - #131 = Integer 44 - #132 = Utf8 AXIS_GENERIC_14 - #133 = Integer 45 - #134 = Utf8 AXIS_GENERIC_15 - #135 = Integer 46 - #136 = Utf8 AXIS_GENERIC_16 - #137 = Integer 47 - #138 = Utf8 BUTTON_PRIMARY - #139 = Utf8 BUTTON_SECONDARY - #140 = Utf8 BUTTON_TERTIARY - #141 = Utf8 BUTTON_BACK - #142 = Utf8 BUTTON_FORWARD - #143 = Utf8 TOOL_TYPE_UNKNOWN - #144 = Utf8 TOOL_TYPE_FINGER - #145 = Utf8 TOOL_TYPE_STYLUS - #146 = Utf8 TOOL_TYPE_MOUSE - #147 = Utf8 TOOL_TYPE_ERASER - #148 = Utf8 CREATOR - #149 = Class #303 // android/os/Parcelable$Creator - #150 = Utf8 Creator - #151 = Utf8 Landroid/os/Parcelable$Creator; - #152 = Utf8 Signature - #153 = Utf8 Landroid/os/Parcelable$Creator; - #154 = Utf8 - #155 = Utf8 ()V - #156 = Utf8 Code - #157 = Utf8 LineNumberTable - #158 = Utf8 LocalVariableTable - #159 = Utf8 this - #160 = Utf8 Landroid/view/MotionEvent; - #161 = Utf8 finalize - #162 = Utf8 Exceptions - #163 = Class #304 // java/lang/Throwable - #164 = Utf8 obtain - #165 = Utf8 (JJII[Landroid/view/MotionEvent$PointerProperties;[Landroid/view/MotionEvent$PointerCoords;IIFFIIII)Landroid/view/MotionEvent; - #166 = Utf8 downTime - #167 = Utf8 J - #168 = Utf8 eventTime - #169 = Utf8 action - #170 = Utf8 pointerCount - #171 = Utf8 pointerProperties - #172 = Utf8 [Landroid/view/MotionEvent$PointerProperties; - #173 = Utf8 pointerCoords - #174 = Utf8 [Landroid/view/MotionEvent$PointerCoords; - #175 = Utf8 metaState - #176 = Utf8 buttonState - #177 = Utf8 xPrecision - #178 = Utf8 F - #179 = Utf8 yPrecision - #180 = Utf8 deviceId - #181 = Utf8 edgeFlags - #182 = Utf8 source - #183 = Utf8 flags - #184 = Utf8 (JJII[I[Landroid/view/MotionEvent$PointerCoords;IFFIIII)Landroid/view/MotionEvent; - #185 = Utf8 pointerIds - #186 = Utf8 [I - #187 = Utf8 (JJIFFFFIFFII)Landroid/view/MotionEvent; - #188 = Utf8 x - #189 = Utf8 y - #190 = Utf8 pressure - #191 = Utf8 size - #192 = Utf8 (JJIIFFFFIFFII)Landroid/view/MotionEvent; - #193 = Utf8 (JJIFFI)Landroid/view/MotionEvent; - #194 = Utf8 (Landroid/view/MotionEvent;)Landroid/view/MotionEvent; - #195 = Utf8 other - #196 = Utf8 obtainNoHistory - #197 = Utf8 recycle - #198 = Utf8 getDeviceId - #199 = Utf8 ()I - #200 = Utf8 getSource - #201 = Utf8 setSource - #202 = Utf8 (I)V - #203 = Utf8 getAction - #204 = Utf8 getActionMasked - #205 = Utf8 getActionIndex - #206 = Utf8 getFlags - #207 = Utf8 getDownTime - #208 = Utf8 ()J - #209 = Utf8 getEventTime - #210 = Utf8 getX - #211 = Utf8 ()F - #212 = Utf8 getY - #213 = Utf8 getPressure - #214 = Utf8 getSize - #215 = Utf8 getTouchMajor - #216 = Utf8 getTouchMinor - #217 = Utf8 getToolMajor - #218 = Utf8 getToolMinor - #219 = Utf8 getOrientation - #220 = Utf8 getAxisValue - #221 = Utf8 (I)F - #222 = Utf8 axis - #223 = Utf8 getPointerCount - #224 = Utf8 getPointerId - #225 = Utf8 (I)I - #226 = Utf8 pointerIndex - #227 = Utf8 getToolType - #228 = Utf8 findPointerIndex - #229 = Utf8 pointerId - #230 = Utf8 (II)F - #231 = Utf8 getPointerCoords - #232 = Utf8 (ILandroid/view/MotionEvent$PointerCoords;)V - #233 = Utf8 outPointerCoords - #234 = Utf8 Landroid/view/MotionEvent$PointerCoords; - #235 = Utf8 getPointerProperties - #236 = Utf8 (ILandroid/view/MotionEvent$PointerProperties;)V - #237 = Utf8 outPointerProperties - #238 = Utf8 Landroid/view/MotionEvent$PointerProperties; - #239 = Utf8 getMetaState - #240 = Utf8 getButtonState - #241 = Utf8 getRawX - #242 = Utf8 getRawY - #243 = Utf8 getXPrecision - #244 = Utf8 getYPrecision - #245 = Utf8 getHistorySize - #246 = Utf8 getHistoricalEventTime - #247 = Utf8 (I)J - #248 = Utf8 pos - #249 = Utf8 getHistoricalX - #250 = Utf8 getHistoricalY - #251 = Utf8 getHistoricalPressure - #252 = Utf8 getHistoricalSize - #253 = Utf8 getHistoricalTouchMajor - #254 = Utf8 getHistoricalTouchMinor - #255 = Utf8 getHistoricalToolMajor - #256 = Utf8 getHistoricalToolMinor - #257 = Utf8 getHistoricalOrientation - #258 = Utf8 getHistoricalAxisValue - #259 = Utf8 (III)F - #260 = Utf8 getHistoricalPointerCoords - #261 = Utf8 (IILandroid/view/MotionEvent$PointerCoords;)V - #262 = Utf8 getEdgeFlags - #263 = Utf8 setEdgeFlags - #264 = Utf8 setAction - #265 = Utf8 offsetLocation - #266 = Utf8 (FF)V - #267 = Utf8 deltaX - #268 = Utf8 deltaY - #269 = Utf8 setLocation - #270 = Utf8 transform - #271 = Utf8 (Landroid/graphics/Matrix;)V - #272 = Utf8 matrix - #273 = Utf8 Landroid/graphics/Matrix; - #274 = Utf8 addBatch - #275 = Utf8 (JFFFFI)V - #276 = Utf8 (J[Landroid/view/MotionEvent$PointerCoords;I)V - #277 = Utf8 toString - #278 = Utf8 ()Ljava/lang/String; - #279 = Utf8 actionToString - #280 = Utf8 (I)Ljava/lang/String; - #281 = Utf8 axisToString - #282 = Utf8 axisFromString - #283 = Utf8 (Ljava/lang/String;)I - #284 = Utf8 symbolicName - #285 = Utf8 Ljava/lang/String; - #286 = Utf8 writeToParcel - #287 = Utf8 (Landroid/os/Parcel;I)V - #288 = Utf8 out - #289 = Utf8 Landroid/os/Parcel; - #290 = Utf8 - #291 = Utf8 SourceFile - #292 = Utf8 MotionEvent.java - #293 = NameAndType #154:#155 // "":()V - #294 = Utf8 java/lang/RuntimeException - #295 = Utf8 Stub! - #296 = NameAndType #154:#305 // "":(Ljava/lang/String;)V - #297 = NameAndType #148:#151 // CREATOR:Landroid/os/Parcelable$Creator; - #298 = Utf8 android/view/MotionEvent - #299 = Utf8 android/view/InputEvent - #300 = Utf8 android/os/Parcelable - #301 = Utf8 android/view/MotionEvent$PointerProperties - #302 = Utf8 android/view/MotionEvent$PointerCoords - #303 = Utf8 android/os/Parcelable$Creator - #304 = Utf8 java/lang/Throwable - #305 = Utf8 (Ljava/lang/String;)V -{ - public static final int INVALID_POINTER_ID; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int -1 - - - public static final int ACTION_MASK; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 255 - - - public static final int ACTION_DOWN; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 0 - - - public static final int ACTION_UP; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 1 - - - public static final int ACTION_MOVE; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 2 - - - public static final int ACTION_CANCEL; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 3 - - - public static final int ACTION_OUTSIDE; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 4 - - - public static final int ACTION_POINTER_DOWN; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 5 - - - public static final int ACTION_POINTER_UP; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 6 - - - public static final int ACTION_HOVER_MOVE; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 7 - - - public static final int ACTION_SCROLL; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 8 - - - public static final int ACTION_HOVER_ENTER; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 9 - - - public static final int ACTION_HOVER_EXIT; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 10 - - - public static final int ACTION_POINTER_INDEX_MASK; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 65280 - - - public static final int ACTION_POINTER_INDEX_SHIFT; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 8 - - - public static final int ACTION_POINTER_1_DOWN; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 5 - Deprecated: true - RuntimeVisibleAnnotations: - 0: #48() - - - public static final int ACTION_POINTER_2_DOWN; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 261 - Deprecated: true - RuntimeVisibleAnnotations: - 0: #48() - - - public static final int ACTION_POINTER_3_DOWN; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 517 - Deprecated: true - RuntimeVisibleAnnotations: - 0: #48() - - - public static final int ACTION_POINTER_1_UP; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 6 - Deprecated: true - RuntimeVisibleAnnotations: - 0: #48() - - - public static final int ACTION_POINTER_2_UP; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 262 - Deprecated: true - RuntimeVisibleAnnotations: - 0: #48() - - - public static final int ACTION_POINTER_3_UP; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 518 - Deprecated: true - RuntimeVisibleAnnotations: - 0: #48() - - - public static final int ACTION_POINTER_ID_MASK; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 65280 - Deprecated: true - RuntimeVisibleAnnotations: - 0: #48() - - - public static final int ACTION_POINTER_ID_SHIFT; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 8 - Deprecated: true - RuntimeVisibleAnnotations: - 0: #48() - - - public static final int FLAG_WINDOW_IS_OBSCURED; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 1 - - - public static final int EDGE_TOP; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 1 - - - public static final int EDGE_BOTTOM; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 2 - - - public static final int EDGE_LEFT; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 4 - - - public static final int EDGE_RIGHT; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 8 - - - public static final int AXIS_X; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 0 - - - public static final int AXIS_Y; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 1 - - - public static final int AXIS_PRESSURE; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 2 - - - public static final int AXIS_SIZE; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 3 - - - public static final int AXIS_TOUCH_MAJOR; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 4 - - - public static final int AXIS_TOUCH_MINOR; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 5 - - - public static final int AXIS_TOOL_MAJOR; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 6 - - - public static final int AXIS_TOOL_MINOR; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 7 - - - public static final int AXIS_ORIENTATION; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 8 - - - public static final int AXIS_VSCROLL; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 9 - - - public static final int AXIS_HSCROLL; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 10 - - - public static final int AXIS_Z; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 11 - - - public static final int AXIS_RX; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 12 - - - public static final int AXIS_RY; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 13 - - - public static final int AXIS_RZ; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 14 - - - public static final int AXIS_HAT_X; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 15 - - - public static final int AXIS_HAT_Y; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 16 - - - public static final int AXIS_LTRIGGER; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 17 - - - public static final int AXIS_RTRIGGER; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 18 - - - public static final int AXIS_THROTTLE; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 19 - - - public static final int AXIS_RUDDER; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 20 - - - public static final int AXIS_WHEEL; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 21 - - - public static final int AXIS_GAS; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 22 - - - public static final int AXIS_BRAKE; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 23 - - - public static final int AXIS_DISTANCE; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 24 - - - public static final int AXIS_TILT; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 25 - - - public static final int AXIS_GENERIC_1; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 32 - - - public static final int AXIS_GENERIC_2; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 33 - - - public static final int AXIS_GENERIC_3; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 34 - - - public static final int AXIS_GENERIC_4; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 35 - - - public static final int AXIS_GENERIC_5; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 36 - - - public static final int AXIS_GENERIC_6; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 37 - - - public static final int AXIS_GENERIC_7; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 38 - - - public static final int AXIS_GENERIC_8; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 39 - - - public static final int AXIS_GENERIC_9; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 40 - - - public static final int AXIS_GENERIC_10; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 41 - - - public static final int AXIS_GENERIC_11; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 42 - - - public static final int AXIS_GENERIC_12; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 43 - - - public static final int AXIS_GENERIC_13; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 44 - - - public static final int AXIS_GENERIC_14; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 45 - - - public static final int AXIS_GENERIC_15; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 46 - - - public static final int AXIS_GENERIC_16; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 47 - - - public static final int BUTTON_PRIMARY; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 1 - - - public static final int BUTTON_SECONDARY; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 2 - - - public static final int BUTTON_TERTIARY; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 4 - - - public static final int BUTTON_BACK; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 8 - - - public static final int BUTTON_FORWARD; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 16 - - - public static final int TOOL_TYPE_UNKNOWN; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 0 - - - public static final int TOOL_TYPE_FINGER; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 1 - - - public static final int TOOL_TYPE_STYLUS; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 2 - - - public static final int TOOL_TYPE_MOUSE; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 3 - - - public static final int TOOL_TYPE_ERASER; - Signature: I - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - ConstantValue: int 4 - - - public static final android.os.Parcelable$Creator CREATOR; - Signature: Landroid/os/Parcelable$Creator; - flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL - Signature: #153 // Landroid/os/Parcelable$Creator; - - - android.view.MotionEvent(); - Signature: ()V - flags: - Code: - stack=3, locals=1, args_size=1 - 0: aload_0 - 1: invokespecial #1 // Method android/view/InputEvent."":()V - 4: new #2 // class java/lang/RuntimeException - 7: dup - 8: ldc #3 // String Stub! - 10: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 13: athrow - LineNumberTable: - line 35: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 14 0 this Landroid/view/MotionEvent; - - protected void finalize() throws java.lang.Throwable; - Signature: ()V - flags: ACC_PROTECTED - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 36: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - Exceptions: - throws java.lang.Throwable - - public static android.view.MotionEvent obtain(long, long, int, int, android.view.MotionEvent$PointerProperties[], android.view.MotionEvent$PointerCoords[], int, int, float, float, int, int, int, int); - Signature: (JJII[Landroid/view/MotionEvent$PointerProperties;[Landroid/view/MotionEvent$PointerCoords;IIFFIIII)Landroid/view/MotionEvent; - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=16, args_size=14 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 37: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 downTime J - 0 10 2 eventTime J - 0 10 4 action I - 0 10 5 pointerCount I - 0 10 6 pointerProperties [Landroid/view/MotionEvent$PointerProperties; - 0 10 7 pointerCoords [Landroid/view/MotionEvent$PointerCoords; - 0 10 8 metaState I - 0 10 9 buttonState I - 0 10 10 xPrecision F - 0 10 11 yPrecision F - 0 10 12 deviceId I - 0 10 13 edgeFlags I - 0 10 14 source I - 0 10 15 flags I - - public static android.view.MotionEvent obtain(long, long, int, int, int[], android.view.MotionEvent$PointerCoords[], int, float, float, int, int, int, int); - Signature: (JJII[I[Landroid/view/MotionEvent$PointerCoords;IFFIIII)Landroid/view/MotionEvent; - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=15, args_size=13 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 39: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 downTime J - 0 10 2 eventTime J - 0 10 4 action I - 0 10 5 pointerCount I - 0 10 6 pointerIds [I - 0 10 7 pointerCoords [Landroid/view/MotionEvent$PointerCoords; - 0 10 8 metaState I - 0 10 9 xPrecision F - 0 10 10 yPrecision F - 0 10 11 deviceId I - 0 10 12 edgeFlags I - 0 10 13 source I - 0 10 14 flags I - Deprecated: true - RuntimeVisibleAnnotations: - 0: #48() - - public static android.view.MotionEvent obtain(long, long, int, float, float, float, float, int, float, float, int, int); - Signature: (JJIFFFFIFFII)Landroid/view/MotionEvent; - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=14, args_size=12 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 40: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 downTime J - 0 10 2 eventTime J - 0 10 4 action I - 0 10 5 x F - 0 10 6 y F - 0 10 7 pressure F - 0 10 8 size F - 0 10 9 metaState I - 0 10 10 xPrecision F - 0 10 11 yPrecision F - 0 10 12 deviceId I - 0 10 13 edgeFlags I - - public static android.view.MotionEvent obtain(long, long, int, int, float, float, float, float, int, float, float, int, int); - Signature: (JJIIFFFFIFFII)Landroid/view/MotionEvent; - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=15, args_size=13 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 42: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 downTime J - 0 10 2 eventTime J - 0 10 4 action I - 0 10 5 pointerCount I - 0 10 6 x F - 0 10 7 y F - 0 10 8 pressure F - 0 10 9 size F - 0 10 10 metaState I - 0 10 11 xPrecision F - 0 10 12 yPrecision F - 0 10 13 deviceId I - 0 10 14 edgeFlags I - Deprecated: true - RuntimeVisibleAnnotations: - 0: #48() - - public static android.view.MotionEvent obtain(long, long, int, float, float, int); - Signature: (JJIFFI)Landroid/view/MotionEvent; - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=8, args_size=6 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 43: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 downTime J - 0 10 2 eventTime J - 0 10 4 action I - 0 10 5 x F - 0 10 6 y F - 0 10 7 metaState I - - public static android.view.MotionEvent obtain(android.view.MotionEvent); - Signature: (Landroid/view/MotionEvent;)Landroid/view/MotionEvent; - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 44: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 other Landroid/view/MotionEvent; - - public static android.view.MotionEvent obtainNoHistory(android.view.MotionEvent); - Signature: (Landroid/view/MotionEvent;)Landroid/view/MotionEvent; - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 45: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 other Landroid/view/MotionEvent; - - public final void recycle(); - Signature: ()V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 46: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final int getDeviceId(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 47: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final int getSource(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 48: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final void setSource(int); - Signature: (I)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 49: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 source I - - public final int getAction(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 50: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final int getActionMasked(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 51: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final int getActionIndex(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 52: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final int getFlags(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 53: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final long getDownTime(); - Signature: ()J - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 54: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final long getEventTime(); - Signature: ()J - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 55: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getX(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 56: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getY(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 57: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getPressure(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 58: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getSize(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 59: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getTouchMajor(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 60: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getTouchMinor(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 61: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getToolMajor(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 62: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getToolMinor(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 63: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getOrientation(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 64: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getAxisValue(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 65: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 axis I - - public final int getPointerCount(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 66: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final int getPointerId(int); - Signature: (I)I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 67: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final int getToolType(int); - Signature: (I)I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 68: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final int findPointerIndex(int); - Signature: (I)I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 69: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerId I - - public final float getX(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 70: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final float getY(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 71: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final float getPressure(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 72: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final float getSize(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 73: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final float getTouchMajor(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 74: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final float getTouchMinor(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 75: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final float getToolMajor(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 76: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final float getToolMinor(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 77: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final float getOrientation(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 78: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - - public final float getAxisValue(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 79: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 axis I - 0 10 2 pointerIndex I - - public final void getPointerCoords(int, android.view.MotionEvent$PointerCoords); - Signature: (ILandroid/view/MotionEvent$PointerCoords;)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 80: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 outPointerCoords Landroid/view/MotionEvent$PointerCoords; - - public final void getPointerProperties(int, android.view.MotionEvent$PointerProperties); - Signature: (ILandroid/view/MotionEvent$PointerProperties;)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 81: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 outPointerProperties Landroid/view/MotionEvent$PointerProperties; - - public final int getMetaState(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 82: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final int getButtonState(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 83: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getRawX(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 84: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getRawY(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 85: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getXPrecision(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 86: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final float getYPrecision(); - Signature: ()F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 87: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final int getHistorySize(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 88: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final long getHistoricalEventTime(int); - Signature: (I)J - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 89: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - public final float getHistoricalX(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 90: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - public final float getHistoricalY(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 91: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - public final float getHistoricalPressure(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 92: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - public final float getHistoricalSize(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 93: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - public final float getHistoricalTouchMajor(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 94: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - public final float getHistoricalTouchMinor(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 95: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - public final float getHistoricalToolMajor(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 96: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - public final float getHistoricalToolMinor(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 97: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - public final float getHistoricalOrientation(int); - Signature: (I)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 98: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pos I - - public final float getHistoricalAxisValue(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 99: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 axis I - 0 10 2 pos I - - public final float getHistoricalX(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 100: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - public final float getHistoricalY(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 101: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - public final float getHistoricalPressure(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 102: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - public final float getHistoricalSize(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 103: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - public final float getHistoricalTouchMajor(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 104: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - public final float getHistoricalTouchMinor(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 105: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - public final float getHistoricalToolMajor(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 106: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - public final float getHistoricalToolMinor(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 107: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - public final float getHistoricalOrientation(int, int); - Signature: (II)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 108: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - - public final float getHistoricalAxisValue(int, int, int); - Signature: (III)F - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=4, args_size=4 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 109: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 axis I - 0 10 2 pointerIndex I - 0 10 3 pos I - - public final void getHistoricalPointerCoords(int, int, android.view.MotionEvent$PointerCoords); - Signature: (IILandroid/view/MotionEvent$PointerCoords;)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=4, args_size=4 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 110: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 pointerIndex I - 0 10 2 pos I - 0 10 3 outPointerCoords Landroid/view/MotionEvent$PointerCoords; - - public final int getEdgeFlags(); - Signature: ()I - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 111: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public final void setEdgeFlags(int); - Signature: (I)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 112: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 flags I - - public final void setAction(int); - Signature: (I)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 113: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 action I - - public final void offsetLocation(float, float); - Signature: (FF)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 114: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 deltaX F - 0 10 2 deltaY F - - public final void setLocation(float, float); - Signature: (FF)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 115: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 x F - 0 10 2 y F - - public final void transform(android.graphics.Matrix); - Signature: (Landroid/graphics/Matrix;)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=2, args_size=2 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 116: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 matrix Landroid/graphics/Matrix; - - public final void addBatch(long, float, float, float, float, int); - Signature: (JFFFFI)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=8, args_size=7 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 117: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 eventTime J - 0 10 3 x F - 0 10 4 y F - 0 10 5 pressure F - 0 10 6 size F - 0 10 7 metaState I - - public final void addBatch(long, android.view.MotionEvent$PointerCoords[], int); - Signature: (J[Landroid/view/MotionEvent$PointerCoords;I)V - flags: ACC_PUBLIC, ACC_FINAL - Code: - stack=3, locals=5, args_size=4 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 118: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 eventTime J - 0 10 3 pointerCoords [Landroid/view/MotionEvent$PointerCoords; - 0 10 4 metaState I - - public java.lang.String toString(); - Signature: ()Ljava/lang/String; - flags: ACC_PUBLIC - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 119: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - - public static java.lang.String actionToString(int); - Signature: (I)Ljava/lang/String; - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 120: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 action I - - public static java.lang.String axisToString(int); - Signature: (I)Ljava/lang/String; - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 121: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 axis I - - public static int axisFromString(java.lang.String); - Signature: (Ljava/lang/String;)I - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=1, args_size=1 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 122: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 symbolicName Ljava/lang/String; - - public void writeToParcel(android.os.Parcel, int); - Signature: (Landroid/os/Parcel;I)V - flags: ACC_PUBLIC - Code: - stack=3, locals=3, args_size=3 - 0: new #2 // class java/lang/RuntimeException - 3: dup - 4: ldc #3 // String Stub! - 6: invokespecial #4 // Method java/lang/RuntimeException."":(Ljava/lang/String;)V - 9: athrow - LineNumberTable: - line 123: 0 - LocalVariableTable: - Start Length Slot Name Signature - 0 10 0 this Landroid/view/MotionEvent; - 0 10 1 out Landroid/os/Parcel; - 0 10 2 flags I - - static {}; - Signature: ()V - flags: ACC_STATIC - Code: - stack=1, locals=0, args_size=0 - 0: aconst_null - 1: putstatic #5 // Field CREATOR:Landroid/os/Parcelable$Creator; - 4: return - LineNumberTable: - line 213: 0 -} diff --git a/android/jni_generator/testMultipleJNIAdditionalImport.golden b/android/jni_generator/testMultipleJNIAdditionalImport.golden deleted file mode 100644 index 40f3d6bee..000000000 --- a/android/jni_generator/testMultipleJNIAdditionalImport.golden +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/foo/Foo - -#ifndef org_chromium_foo_Foo_JNI -#define org_chromium_foo_Foo_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_foo_Foo[]; -const char kClassPath_org_chromium_foo_Foo[] = "org/chromium/foo/Foo"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_foo_Foo_clazz = 0; -#ifndef org_chromium_foo_Foo_clazz_defined -#define org_chromium_foo_Foo_clazz_defined -inline jclass org_chromium_foo_Foo_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_foo_Foo, - &g_org_chromium_foo_Foo_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -static void JNI_Foo_DoSomething(JNIEnv* env, const base::android::JavaParamRef& jcaller, - const base::android::JavaParamRef& callback1, - const base::android::JavaParamRef& callback2); - -JNI_GENERATOR_EXPORT void Java_org_chromium_foo_Foo_nativeDoSomething( - JNIEnv* env, - jclass jcaller, - jobject callback1, - jobject callback2) { - return JNI_Foo_DoSomething(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, callback1), - base::android::JavaParamRef(env, callback2)); -} - - -static base::subtle::AtomicWord g_org_chromium_foo_Foo_calledByNative = 0; -static void Java_Foo_calledByNative(JNIEnv* env, const base::android::JavaRef& callback1, - const base::android::JavaRef& callback2) { - CHECK_CLAZZ(env, org_chromium_foo_Foo_clazz(env), - org_chromium_foo_Foo_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_foo_Foo_clazz(env), - "calledByNative", - "(Lorg/chromium/foo/Bar1$Callback;Lorg/chromium/foo/Bar2$Callback;)V", - &g_org_chromium_foo_Foo_calledByNative); - - env->CallStaticVoidMethod(org_chromium_foo_Foo_clazz(env), - method_id, callback1.obj(), callback2.obj()); - jni_generator::CheckException(env); -} - -#endif // org_chromium_foo_Foo_JNI diff --git a/android/jni_generator/testNativeExportsOnlyOption.golden b/android/jni_generator/testNativeExportsOnlyOption.golden deleted file mode 100644 index bdc8d29e9..000000000 --- a/android/jni_generator/testNativeExportsOnlyOption.golden +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/example/jni_generator/SampleForTests - -#ifndef org_chromium_example_jni_generator_SampleForTests_JNI -#define org_chromium_example_jni_generator_SampleForTests_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char - kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass[]; -const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass[] = - "org/chromium/example/jni_generator/SampleForTests$MyOtherInnerClass"; - -JNI_REGISTRATION_EXPORT extern const char - kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass[]; -const char kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass[] = - "org/chromium/example/jni_generator/SampleForTests$MyInnerClass"; - -JNI_REGISTRATION_EXPORT extern const char - kClassPath_org_chromium_example_jni_1generator_SampleForTests[]; -const char kClassPath_org_chromium_example_jni_1generator_SampleForTests[] = - "org/chromium/example/jni_generator/SampleForTests"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_clazz = 0; -#ifndef org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_clazz_defined -#define org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_clazz_defined -inline jclass - org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, - kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass, - &g_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_clazz); -} -#endif -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_clazz = 0; -#ifndef org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_clazz_defined -#define org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_clazz_defined -inline jclass org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_clazz(JNIEnv* - env) { - return base::android::LazyGetClass(env, - kClassPath_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass, - &g_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_clazz); -} -#endif -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_clazz = 0; -#ifndef org_chromium_example_jni_1generator_SampleForTests_clazz_defined -#define org_chromium_example_jni_1generator_SampleForTests_clazz_defined -inline jclass org_chromium_example_jni_1generator_SampleForTests_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, - kClassPath_org_chromium_example_jni_1generator_SampleForTests, - &g_org_chromium_example_jni_1generator_SampleForTests_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -JNI_GENERATOR_EXPORT jint - Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod( - JNIEnv* env, - jobject jcaller, - jlong nativeTest, - jint arg1) { - Test* native = reinterpret_cast(nativeTest); - CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0); - return native->StaticMethod(env, base::android::JavaParamRef(env, jcaller), arg1); -} - -JNI_GENERATOR_EXPORT jint Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod( - JNIEnv* env, - jobject jcaller, - jlong nativeTest, - jint arg1) { - Test* native = reinterpret_cast(nativeTest); - CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); - return native->Method(env, base::android::JavaParamRef(env, jcaller), arg1); -} - -static jint JNI_MyInnerClass_Init(JNIEnv* env, const base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jint - Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit( - JNIEnv* env, - jobject jcaller) { - return JNI_MyInnerClass_Init(env, base::android::JavaParamRef(env, jcaller)); -} - -static jint JNI_MyOtherInnerClass_Init(JNIEnv* env, const base::android::JavaParamRef& - jcaller); - -JNI_GENERATOR_EXPORT jint - Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit( - JNIEnv* env, - jobject jcaller) { - return JNI_MyOtherInnerClass_Init(env, base::android::JavaParamRef(env, jcaller)); -} - - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithParam = 0; -static void Java_SampleForTests_testMethodWithParam(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper iParam) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "testMethodWithParam", - "(I)V", - &g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithParam); - - env->CallVoidMethod(obj.obj(), - method_id, as_jint(iParam)); - jni_generator::CheckException(env); -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithParamAndReturn = 0; -static base::android::ScopedJavaLocalRef - Java_SampleForTests_testMethodWithParamAndReturn(JNIEnv* env, const - base::android::JavaRef& obj, JniIntWrapper iParam) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "testMethodWithParamAndReturn", - "(I)Ljava/lang/String;", - &g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithParamAndReturn); - - jstring ret = - static_cast(env->CallObjectMethod(obj.obj(), - method_id, as_jint(iParam))); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_testStaticMethodWithParam = 0; -static jint Java_SampleForTests_testStaticMethodWithParam(JNIEnv* env, JniIntWrapper iParam) { - CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - org_chromium_example_jni_1generator_SampleForTests_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "testStaticMethodWithParam", - "(I)I", - &g_org_chromium_example_jni_1generator_SampleForTests_testStaticMethodWithParam); - - jint ret = - env->CallStaticIntMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env), - method_id, as_jint(iParam)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithNoParam = 0; -static jdouble Java_SampleForTests_testMethodWithNoParam(JNIEnv* env) { - CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - org_chromium_example_jni_1generator_SampleForTests_clazz(env), 0); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "testMethodWithNoParam", - "()D", - &g_org_chromium_example_jni_1generator_SampleForTests_testMethodWithNoParam); - - jdouble ret = - env->CallStaticDoubleMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord - g_org_chromium_example_jni_1generator_SampleForTests_testStaticMethodWithNoParam = 0; -static base::android::ScopedJavaLocalRef - Java_SampleForTests_testStaticMethodWithNoParam(JNIEnv* env) { - CHECK_CLAZZ(env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - org_chromium_example_jni_1generator_SampleForTests_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_example_jni_1generator_SampleForTests_clazz(env), - "testStaticMethodWithNoParam", - "()Ljava/lang/String;", - &g_org_chromium_example_jni_1generator_SampleForTests_testStaticMethodWithNoParam); - - jstring ret = -static_cast(env->CallStaticObjectMethod(org_chromium_example_jni_1generator_SampleForTests_clazz(env), - method_id)); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -#endif // org_chromium_example_jni_generator_SampleForTests_JNI diff --git a/android/jni_generator/testNatives.golden b/android/jni_generator/testNatives.golden deleted file mode 100644 index 1178f19a0..000000000 --- a/android/jni_generator/testNatives.golden +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/TestJni - -#ifndef org_chromium_TestJni_JNI -#define org_chromium_TestJni_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni[]; -const char kClassPath_org_chromium_TestJni[] = "org/chromium/TestJni"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_clazz = 0; -#ifndef org_chromium_TestJni_clazz_defined -#define org_chromium_TestJni_clazz_defined -inline jclass org_chromium_TestJni_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni, - &g_org_chromium_TestJni_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -static jint JNI_TestJni_Init(JNIEnv* env, const base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_nativeInit( - JNIEnv* env, - jobject jcaller) { - return JNI_TestJni_Init(env, base::android::JavaParamRef(env, jcaller)); -} - -JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeDestroy( - JNIEnv* env, - jobject jcaller, - jint nativeChromeBrowserProvider) { - ChromeBrowserProvider* native = - reinterpret_cast(nativeChromeBrowserProvider); - CHECK_NATIVE_PTR(env, jcaller, native, "Destroy"); - return native->Destroy(env, base::android::JavaParamRef(env, jcaller)); -} - -JNI_GENERATOR_EXPORT jlong Java_org_chromium_TestJni_nativeAddBookmark( - JNIEnv* env, - jobject jcaller, - jint nativeChromeBrowserProvider, - jstring url, - jstring title, - jboolean isFolder, - jlong parentId) { - ChromeBrowserProvider* native = - reinterpret_cast(nativeChromeBrowserProvider); - CHECK_NATIVE_PTR(env, jcaller, native, "AddBookmark", 0); - return native->AddBookmark(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, url), base::android::JavaParamRef(env, - title), isFolder, parentId); -} - -static base::android::ScopedJavaLocalRef JNI_TestJni_GetDomainAndRegistry(JNIEnv* env, - const base::android::JavaParamRef& jcaller, - const base::android::JavaParamRef& url); - -JNI_GENERATOR_EXPORT jstring Java_org_chromium_TestJni_nativeGetDomainAndRegistry( - JNIEnv* env, - jclass jcaller, - jstring url) { - return JNI_TestJni_GetDomainAndRegistry(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, url)).Release(); -} - -static void JNI_TestJni_CreateHistoricalTabFromState(JNIEnv* env, const - base::android::JavaParamRef& jcaller, - const base::android::JavaParamRef& state, - jint tab_index); - -JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeCreateHistoricalTabFromState( - JNIEnv* env, - jclass jcaller, - jbyteArray state, - jint tab_index) { - return JNI_TestJni_CreateHistoricalTabFromState(env, base::android::JavaParamRef(env, - jcaller), base::android::JavaParamRef(env, state), tab_index); -} - -static base::android::ScopedJavaLocalRef JNI_TestJni_GetStateAsByteArray(JNIEnv* env, - const base::android::JavaParamRef& jcaller, - const base::android::JavaParamRef& view); - -JNI_GENERATOR_EXPORT jbyteArray Java_org_chromium_TestJni_nativeGetStateAsByteArray( - JNIEnv* env, - jobject jcaller, - jobject view) { - return JNI_TestJni_GetStateAsByteArray(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, view)).Release(); -} - -static base::android::ScopedJavaLocalRef JNI_TestJni_GetAutofillProfileGUIDs(JNIEnv* - env, const base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jobjectArray Java_org_chromium_TestJni_nativeGetAutofillProfileGUIDs( - JNIEnv* env, - jclass jcaller) { - return JNI_TestJni_GetAutofillProfileGUIDs(env, base::android::JavaParamRef(env, - jcaller)).Release(); -} - -static void JNI_TestJni_SetRecognitionResults(JNIEnv* env, const - base::android::JavaParamRef& jcaller, - jint sessionId, - const base::android::JavaParamRef& results); - -JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeSetRecognitionResults( - JNIEnv* env, - jobject jcaller, - jint sessionId, - jobjectArray results) { - return JNI_TestJni_SetRecognitionResults(env, base::android::JavaParamRef(env, jcaller), - sessionId, base::android::JavaParamRef(env, results)); -} - -JNI_GENERATOR_EXPORT jlong Java_org_chromium_TestJni_nativeAddBookmarkFromAPI( - JNIEnv* env, - jobject jcaller, - jint nativeChromeBrowserProvider, - jstring url, - jobject created, - jobject isBookmark, - jobject date, - jbyteArray favicon, - jstring title, - jobject visits) { - ChromeBrowserProvider* native = - reinterpret_cast(nativeChromeBrowserProvider); - CHECK_NATIVE_PTR(env, jcaller, native, "AddBookmarkFromAPI", 0); - return native->AddBookmarkFromAPI(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, url), base::android::JavaParamRef(env, - created), base::android::JavaParamRef(env, isBookmark), - base::android::JavaParamRef(env, date), base::android::JavaParamRef(env, - favicon), base::android::JavaParamRef(env, title), - base::android::JavaParamRef(env, visits)); -} - -static jint JNI_TestJni_FindAll(JNIEnv* env, const base::android::JavaParamRef& jcaller, - const base::android::JavaParamRef& find); - -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_nativeFindAll( - JNIEnv* env, - jobject jcaller, - jstring find) { - return JNI_TestJni_FindAll(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, find)); -} - -static base::android::ScopedJavaLocalRef JNI_TestJni_GetInnerClass(JNIEnv* env, const - base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT jobject Java_org_chromium_TestJni_nativeGetInnerClass( - JNIEnv* env, - jclass jcaller) { - return JNI_TestJni_GetInnerClass(env, base::android::JavaParamRef(env, - jcaller)).Release(); -} - -JNI_GENERATOR_EXPORT jobject Java_org_chromium_TestJni_nativeQueryBitmap( - JNIEnv* env, - jobject jcaller, - jint nativeChromeBrowserProvider, - jobjectArray projection, - jstring selection, - jobjectArray selectionArgs, - jstring sortOrder) { - ChromeBrowserProvider* native = - reinterpret_cast(nativeChromeBrowserProvider); - CHECK_NATIVE_PTR(env, jcaller, native, "QueryBitmap", NULL); - return native->QueryBitmap(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, projection), - base::android::JavaParamRef(env, selection), - base::android::JavaParamRef(env, selectionArgs), - base::android::JavaParamRef(env, sortOrder)).Release(); -} - -JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeGotOrientation( - JNIEnv* env, - jobject jcaller, - jint nativeDataFetcherImplAndroid, - jdouble alpha, - jdouble beta, - jdouble gamma) { - DataFetcherImplAndroid* native = - reinterpret_cast(nativeDataFetcherImplAndroid); - CHECK_NATIVE_PTR(env, jcaller, native, "GotOrientation"); - return native->GotOrientation(env, base::android::JavaParamRef(env, jcaller), alpha, - beta, gamma); -} - -static base::android::ScopedJavaLocalRef JNI_TestJni_MessWithJavaException(JNIEnv* env, - const base::android::JavaParamRef& jcaller, - const base::android::JavaParamRef& e); - -JNI_GENERATOR_EXPORT jthrowable Java_org_chromium_TestJni_nativeMessWithJavaException( - JNIEnv* env, - jclass jcaller, - jthrowable e) { - return JNI_TestJni_MessWithJavaException(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, e)).Release(); -} - - -#endif // org_chromium_TestJni_JNI diff --git a/android/jni_generator/testNativesLong.golden b/android/jni_generator/testNativesLong.golden deleted file mode 100644 index b3115eff2..000000000 --- a/android/jni_generator/testNativesLong.golden +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/TestJni - -#ifndef org_chromium_TestJni_JNI -#define org_chromium_TestJni_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni[]; -const char kClassPath_org_chromium_TestJni[] = "org/chromium/TestJni"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_TestJni_clazz = 0; -#ifndef org_chromium_TestJni_clazz_defined -#define org_chromium_TestJni_clazz_defined -inline jclass org_chromium_TestJni_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni, - &g_org_chromium_TestJni_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeDestroy( - JNIEnv* env, - jobject jcaller, - jlong nativeChromeBrowserProvider) { - ChromeBrowserProvider* native = - reinterpret_cast(nativeChromeBrowserProvider); - CHECK_NATIVE_PTR(env, jcaller, native, "Destroy"); - return native->Destroy(env, base::android::JavaParamRef(env, jcaller)); -} - - -#endif // org_chromium_TestJni_JNI diff --git a/android/jni_generator/testNativesRegistrations.golden b/android/jni_generator/testNativesRegistrations.golden deleted file mode 100644 index 1dae786aa..000000000 --- a/android/jni_generator/testNativesRegistrations.golden +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2017 The Chromium 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 autogenerated by -// base/android/jni_generator/jni_registration_generator.py -// Please do not change its content. - -#ifndef HEADER_GUARD -#define HEADER_GUARD - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" -#include "base/android/jni_int_wrapper.h" - - -// Step 1: Forward declarations (classes). - -extern const char kClassPath_org_chromium_TestJni[]; -extern base::subtle::AtomicWord g_org_chromium_TestJni_clazz; -#ifndef org_chromium_TestJni_clazz_defined -#define org_chromium_TestJni_clazz_defined -inline jclass org_chromium_TestJni_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni, - &g_org_chromium_TestJni_clazz); -} -#endif - - -// Step 2: Forward declarations (methods). - -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_nativeInit( - JNIEnv* env, - jobject jcaller); -JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeDestroy( - JNIEnv* env, - jobject jcaller, - jint nativeChromeBrowserProvider); -JNI_GENERATOR_EXPORT jlong Java_org_chromium_TestJni_nativeAddBookmark( - JNIEnv* env, - jobject jcaller, - jint nativeChromeBrowserProvider, - jstring url, - jstring title, - jboolean isFolder, - jlong parentId); -JNI_GENERATOR_EXPORT jstring Java_org_chromium_TestJni_nativeGetDomainAndRegistry( - JNIEnv* env, - jclass jcaller, - jstring url); -JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeCreateHistoricalTabFromState( - JNIEnv* env, - jclass jcaller, - jbyteArray state, - jint tab_index); -JNI_GENERATOR_EXPORT jbyteArray Java_org_chromium_TestJni_nativeGetStateAsByteArray( - JNIEnv* env, - jobject jcaller, - jobject view); -JNI_GENERATOR_EXPORT jobjectArray Java_org_chromium_TestJni_nativeGetAutofillProfileGUIDs( - JNIEnv* env, - jclass jcaller); -JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeSetRecognitionResults( - JNIEnv* env, - jobject jcaller, - jint sessionId, - jobjectArray results); -JNI_GENERATOR_EXPORT jlong Java_org_chromium_TestJni_nativeAddBookmarkFromAPI( - JNIEnv* env, - jobject jcaller, - jint nativeChromeBrowserProvider, - jstring url, - jobject created, - jobject isBookmark, - jobject date, - jbyteArray favicon, - jstring title, - jobject visits); -JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_nativeFindAll( - JNIEnv* env, - jobject jcaller, - jstring find); -JNI_GENERATOR_EXPORT jobject Java_org_chromium_TestJni_nativeGetInnerClass( - JNIEnv* env, - jclass jcaller); -JNI_GENERATOR_EXPORT jobject Java_org_chromium_TestJni_nativeQueryBitmap( - JNIEnv* env, - jobject jcaller, - jint nativeChromeBrowserProvider, - jobjectArray projection, - jstring selection, - jobjectArray selectionArgs, - jstring sortOrder); -JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeGotOrientation( - JNIEnv* env, - jobject jcaller, - jint nativeDataFetcherImplAndroid, - jdouble alpha, - jdouble beta, - jdouble gamma); -JNI_GENERATOR_EXPORT jthrowable Java_org_chromium_TestJni_nativeMessWithJavaException( - JNIEnv* env, - jclass jcaller, - jthrowable e); - - -// Step 3: Method declarations. - -static const JNINativeMethod kMethods_org_chromium_TestJni[] = { - { "nativeInit", "()I", reinterpret_cast(Java_org_chromium_TestJni_nativeInit) }, - { "nativeDestroy", "(I)V", reinterpret_cast(Java_org_chromium_TestJni_nativeDestroy) }, - { "nativeAddBookmark", "(ILjava/lang/String;Ljava/lang/String;ZJ)J", - reinterpret_cast(Java_org_chromium_TestJni_nativeAddBookmark) }, - { "nativeGetDomainAndRegistry", "(Ljava/lang/String;)Ljava/lang/String;", - reinterpret_cast(Java_org_chromium_TestJni_nativeGetDomainAndRegistry) }, - { "nativeCreateHistoricalTabFromState", "([BI)V", - reinterpret_cast(Java_org_chromium_TestJni_nativeCreateHistoricalTabFromState) }, - { "nativeGetStateAsByteArray", "(Landroid/view/View;)[B", - reinterpret_cast(Java_org_chromium_TestJni_nativeGetStateAsByteArray) }, - { "nativeGetAutofillProfileGUIDs", "()[Ljava/lang/String;", - reinterpret_cast(Java_org_chromium_TestJni_nativeGetAutofillProfileGUIDs) }, - { "nativeSetRecognitionResults", "(I[Ljava/lang/String;)V", - reinterpret_cast(Java_org_chromium_TestJni_nativeSetRecognitionResults) }, - { "nativeAddBookmarkFromAPI", - "(ILjava/lang/String;Ljava/lang/Long;Ljava/lang/Boolean;Ljava/lang/Long;[BLjava/lang/String;Ljava/lang/Integer;)J", - reinterpret_cast(Java_org_chromium_TestJni_nativeAddBookmarkFromAPI) }, - { "nativeFindAll", "(Ljava/lang/String;)I", - reinterpret_cast(Java_org_chromium_TestJni_nativeFindAll) }, - { "nativeGetInnerClass", - "()Lorg/chromium/example/jni_generator/SampleForTests$OnFrameAvailableListener;", - reinterpret_cast(Java_org_chromium_TestJni_nativeGetInnerClass) }, - { "nativeQueryBitmap", - "(I[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/graphics/Bitmap;", - reinterpret_cast(Java_org_chromium_TestJni_nativeQueryBitmap) }, - { "nativeGotOrientation", "(IDDD)V", - reinterpret_cast(Java_org_chromium_TestJni_nativeGotOrientation) }, - { "nativeMessWithJavaException", "(Ljava/lang/Throwable;)Ljava/lang/Throwable;", - reinterpret_cast(Java_org_chromium_TestJni_nativeMessWithJavaException) }, -}; - - -JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) { - const int kMethods_org_chromium_TestJniSize = - arraysize(kMethods_org_chromium_TestJni); - if (env->RegisterNatives( - org_chromium_TestJni_clazz(env), - kMethods_org_chromium_TestJni, - kMethods_org_chromium_TestJniSize) < 0) { - jni_generator::HandleRegistrationError(env, - org_chromium_TestJni_clazz(env), - __FILE__); - return false; - } - - return true; -} - - -// Step 4: Main dex and non-main dex registration functions. - -bool RegisterMainDexNatives(JNIEnv* env) { - if (!RegisterNative_org_chromium_TestJni(env)) - return false; - - return true; -} - -bool RegisterNonMainDexNatives(JNIEnv* env) { - - return true; -} - -#endif // HEADER_GUARD diff --git a/android/jni_generator/testSingleJNIAdditionalImport.golden b/android/jni_generator/testSingleJNIAdditionalImport.golden deleted file mode 100644 index 4b3eccdc9..000000000 --- a/android/jni_generator/testSingleJNIAdditionalImport.golden +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/foo/Foo - -#ifndef org_chromium_foo_Foo_JNI -#define org_chromium_foo_Foo_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_foo_Foo[]; -const char kClassPath_org_chromium_foo_Foo[] = "org/chromium/foo/Foo"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_foo_Foo_clazz = 0; -#ifndef org_chromium_foo_Foo_clazz_defined -#define org_chromium_foo_Foo_clazz_defined -inline jclass org_chromium_foo_Foo_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_foo_Foo, - &g_org_chromium_foo_Foo_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -static void JNI_Foo_DoSomething(JNIEnv* env, const base::android::JavaParamRef& jcaller, - const base::android::JavaParamRef& callback); - -JNI_GENERATOR_EXPORT void Java_org_chromium_foo_Foo_nativeDoSomething( - JNIEnv* env, - jclass jcaller, - jobject callback) { - return JNI_Foo_DoSomething(env, base::android::JavaParamRef(env, jcaller), - base::android::JavaParamRef(env, callback)); -} - - -static base::subtle::AtomicWord g_org_chromium_foo_Foo_calledByNative = 0; -static void Java_Foo_calledByNative(JNIEnv* env, const base::android::JavaRef& callback) { - CHECK_CLAZZ(env, org_chromium_foo_Foo_clazz(env), - org_chromium_foo_Foo_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, org_chromium_foo_Foo_clazz(env), - "calledByNative", - "(Lorg/chromium/foo/Bar$Callback;)V", - &g_org_chromium_foo_Foo_calledByNative); - - env->CallStaticVoidMethod(org_chromium_foo_Foo_clazz(env), - method_id, callback.obj()); - jni_generator::CheckException(env); -} - -#endif // org_chromium_foo_Foo_JNI diff --git a/android/jni_generator/testTracing.golden b/android/jni_generator/testTracing.golden deleted file mode 100644 index 97010278b..000000000 --- a/android/jni_generator/testTracing.golden +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/foo/Foo - -#ifndef org_chromium_foo_Foo_JNI -#define org_chromium_foo_Foo_JNI - -#include - -#include "base/android/jni_generator/jni_generator_helper.h" - - -// Step 1: Forward declarations. - -JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_foo_Foo[]; -const char kClassPath_org_chromium_foo_Foo[] = "org/chromium/foo/Foo"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -JNI_REGISTRATION_EXPORT base::subtle::AtomicWord g_org_chromium_foo_Foo_clazz = 0; -#ifndef org_chromium_foo_Foo_clazz_defined -#define org_chromium_foo_Foo_clazz_defined -inline jclass org_chromium_foo_Foo_clazz(JNIEnv* env) { - return base::android::LazyGetClass(env, kClassPath_org_chromium_foo_Foo, - &g_org_chromium_foo_Foo_clazz); -} -#endif - - -// Step 2: Constants (optional). - - -// Step 3: Method stubs. -namespace org { -namespace chromium_foo { - -JNI_GENERATOR_EXPORT void Java_org_chromium_foo_Foo_nativeInstanceMethod( - JNIEnv* env, - jobject jcaller, - jlong nativeInstance) { - TRACE_EVENT0("jni", "org::chromium_foo::Instance::InstanceMethod"); Instance* native = - reinterpret_cast(nativeInstance); - CHECK_NATIVE_PTR(env, jcaller, native, "InstanceMethod"); - return native->InstanceMethod(env, base::android::JavaParamRef(env, jcaller)); -} - -static void JNI_Foo_StaticMethod(JNIEnv* env, const base::android::JavaParamRef& jcaller); - -JNI_GENERATOR_EXPORT void Java_org_chromium_foo_Foo_nativeStaticMethod( - JNIEnv* env, - jclass jcaller) { - TRACE_EVENT0("jni", "org::chromium_foo::JNI_Foo_StaticMethod"); return JNI_Foo_StaticMethod(env, - base::android::JavaParamRef(env, jcaller)); -} - - -static base::subtle::AtomicWord g_org_chromium_foo_Foo_Constructor = 0; -static base::android::ScopedJavaLocalRef Java_Foo_Constructor(JNIEnv* env) { - CHECK_CLAZZ(env, org_chromium_foo_Foo_clazz(env), - org_chromium_foo_Foo_clazz(env), NULL); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_foo_Foo_clazz(env), - "", - "()V", - &g_org_chromium_foo_Foo_Constructor); - - TRACE_EVENT0("jni", "org.chromium.foo.Foo."); jobject ret = - env->NewObject(org_chromium_foo_Foo_clazz(env), - method_id); - jni_generator::CheckException(env); - return base::android::ScopedJavaLocalRef(env, ret); -} - -static base::subtle::AtomicWord g_org_chromium_foo_Foo_callbackFromNative = 0; -static void Java_Foo_callbackFromNative(JNIEnv* env, const base::android::JavaRef& obj) { - CHECK_CLAZZ(env, obj.obj(), - org_chromium_foo_Foo_clazz(env)); - jmethodID method_id = base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, org_chromium_foo_Foo_clazz(env), - "callbackFromNative", - "()V", - &g_org_chromium_foo_Foo_callbackFromNative); - - TRACE_EVENT0("jni", "org.chromium.foo.Foo.callbackFromNative"); - env->CallVoidMethod(obj.obj(), - method_id); - jni_generator::CheckException(env); -} - -} // namespace chromium_foo -} // namespace org - -#endif // org_chromium_foo_Foo_JNI diff --git a/android/jni_int_wrapper.h b/android/jni_int_wrapper.h deleted file mode 100644 index fa0f3d5d5..000000000 --- a/android/jni_int_wrapper.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_JNI_INT_WRAPPER_H_ -#define BASE_ANDROID_JNI_INT_WRAPPER_H_ - -// Wrapper used to receive int when calling Java from native. -// The wrapper disallows automatic conversion of long to int. -// This is to avoid a common anti-pattern where a Java int is used -// to receive a native pointer. Please use a Java long to receive -// native pointers, so that the code works on both 32-bit and 64-bit -// platforms. Note the wrapper allows other lossy conversions into -// jint that could be consider anti-patterns, such as from size_t. - -// Checking is only done in debugging builds. - -#ifdef NDEBUG - -typedef jint JniIntWrapper; - -// This inline is sufficiently trivial that it does not change the -// final code generated by g++. -inline jint as_jint(JniIntWrapper wrapper) { - return wrapper; -} - -#else - -class JniIntWrapper { - public: - JniIntWrapper() : i_(0) {} - JniIntWrapper(int i) : i_(i) {} - JniIntWrapper(const JniIntWrapper& ji) : i_(ji.i_) {} - template JniIntWrapper(const T& t) : i_(t) {} - jint as_jint() const { return i_; } - private: - // If you get an "is private" error at the line below it is because you used - // an implicit conversion to convert a long to an int when calling Java. - // We disallow this, as a common anti-pattern allows converting a native - // pointer (intptr_t) to a Java int. Please use a Java long to represent - // a native pointer. If you want a lossy conversion, please use an - // explicit conversion in your C++ code. Note an error is only seen when - // compiling on a 64-bit platform, as intptr_t is indistinguishable from - // int on 32-bit platforms. - JniIntWrapper(long); - jint i_; -}; - -inline jint as_jint(const JniIntWrapper& wrapper) { - return wrapper.as_jint(); -} - -#endif // NDEBUG - -#endif // BASE_ANDROID_JNI_INT_WRAPPER_H_ diff --git a/android/jni_registrar.cc b/android/jni_registrar.cc deleted file mode 100644 index 8e13e6009..000000000 --- a/android/jni_registrar.cc +++ /dev/null @@ -1,30 +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/android/jni_registrar.h" - -#include "base/logging.h" -#include "base/android/jni_android.h" -#include "base/trace_event/trace_event.h" - -namespace base { -namespace android { - -bool RegisterNativeMethods(JNIEnv* env, - const RegistrationMethod* method, - size_t count) { - TRACE_EVENT0("startup", "base_android::RegisterNativeMethods") - const RegistrationMethod* end = method + count; - while (method != end) { - if (!method->func(env)) { - DLOG(ERROR) << method->name << " failed registration!"; - return false; - } - method++; - } - return true; -} - -} // namespace android -} // namespace base diff --git a/android/jni_registrar.h b/android/jni_registrar.h deleted file mode 100644 index 31a475038..000000000 --- a/android/jni_registrar.h +++ /dev/null @@ -1,28 +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_ANDROID_JNI_REGISTRAR_H_ -#define BASE_ANDROID_JNI_REGISTRAR_H_ - -#include -#include - -#include "base/base_export.h" - -namespace base { -namespace android { - -struct RegistrationMethod; - -// Registers the JNI bindings for the specified |method| definition containing -// |count| elements. Returns whether the registration of the given methods -// succeeded. -BASE_EXPORT bool RegisterNativeMethods(JNIEnv* env, - const RegistrationMethod* method, - size_t count); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_JNI_REGISTRAR_H_ diff --git a/android/jni_string.cc b/android/jni_string.cc deleted file mode 100644 index f28f6498e..000000000 --- a/android/jni_string.cc +++ /dev/null @@ -1,121 +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/android/jni_string.h" - -#include "base/android/jni_android.h" -#include "base/logging.h" -#include "base/strings/utf_string_conversions.h" - -namespace { - -// Internal version that does not use a scoped local pointer. -jstring ConvertUTF16ToJavaStringImpl(JNIEnv* env, - const base::StringPiece16& str) { - jstring result = env->NewString(str.data(), str.length()); - base::android::CheckException(env); - return result; -} - -} // namespace - -namespace base { -namespace android { - -void ConvertJavaStringToUTF8(JNIEnv* env, jstring str, std::string* result) { - DCHECK(str); - if (!str) { - LOG(WARNING) << "ConvertJavaStringToUTF8 called with null string."; - result->clear(); - return; - } - const jsize length = env->GetStringLength(str); - if (!length) { - result->clear(); - CheckException(env); - return; - } - // JNI's GetStringUTFChars() returns strings in Java "modified" UTF8, so - // instead get the String in UTF16 and convert using chromium's conversion - // function that yields plain (non Java-modified) UTF8. - const jchar* chars = env->GetStringChars(str, NULL); - DCHECK(chars); - UTF16ToUTF8(chars, length, result); - env->ReleaseStringChars(str, chars); - CheckException(env); -} - -std::string ConvertJavaStringToUTF8(JNIEnv* env, jstring str) { - std::string result; - ConvertJavaStringToUTF8(env, str, &result); - return result; -} - -std::string ConvertJavaStringToUTF8(const JavaRef& str) { - return ConvertJavaStringToUTF8(AttachCurrentThread(), str.obj()); -} - -std::string ConvertJavaStringToUTF8(JNIEnv* env, const JavaRef& str) { - return ConvertJavaStringToUTF8(env, str.obj()); -} - -ScopedJavaLocalRef ConvertUTF8ToJavaString( - JNIEnv* env, - const base::StringPiece& str) { - // JNI's NewStringUTF expects "modified" UTF8 so instead create the string - // via our own UTF16 conversion utility. - // Further, Dalvik requires the string passed into NewStringUTF() to come from - // a trusted source. We can't guarantee that all UTF8 will be sanitized before - // it gets here, so constructing via UTF16 side-steps this issue. - // (Dalvik stores strings internally as UTF16 anyway, so there shouldn't be - // a significant performance hit by doing it this way). - return ScopedJavaLocalRef(env, ConvertUTF16ToJavaStringImpl( - env, UTF8ToUTF16(str))); -} - -void ConvertJavaStringToUTF16(JNIEnv* env, jstring str, string16* result) { - DCHECK(str); - if (!str) { - LOG(WARNING) << "ConvertJavaStringToUTF16 called with null string."; - result->clear(); - return; - } - const jsize length = env->GetStringLength(str); - if (!length) { - result->clear(); - CheckException(env); - return; - } - const jchar* chars = env->GetStringChars(str, NULL); - DCHECK(chars); - // GetStringChars isn't required to NULL-terminate the strings - // it returns, so the length must be explicitly checked. - result->assign(chars, length); - env->ReleaseStringChars(str, chars); - CheckException(env); -} - -string16 ConvertJavaStringToUTF16(JNIEnv* env, jstring str) { - string16 result; - ConvertJavaStringToUTF16(env, str, &result); - return result; -} - -string16 ConvertJavaStringToUTF16(const JavaRef& str) { - return ConvertJavaStringToUTF16(AttachCurrentThread(), str.obj()); -} - -string16 ConvertJavaStringToUTF16(JNIEnv* env, const JavaRef& str) { - return ConvertJavaStringToUTF16(env, str.obj()); -} - -ScopedJavaLocalRef ConvertUTF16ToJavaString( - JNIEnv* env, - const base::StringPiece16& str) { - return ScopedJavaLocalRef(env, - ConvertUTF16ToJavaStringImpl(env, str)); -} - -} // namespace android -} // namespace base diff --git a/android/jni_string.h b/android/jni_string.h deleted file mode 100644 index 09e85f306..000000000 --- a/android/jni_string.h +++ /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. - -#ifndef BASE_ANDROID_JNI_STRING_H_ -#define BASE_ANDROID_JNI_STRING_H_ - -#include -#include - -#include "base/android/scoped_java_ref.h" -#include "base/base_export.h" -#include "base/strings/string_piece.h" - -namespace base { -namespace android { - -// Convert a Java string to UTF8. Returns a std string. -BASE_EXPORT void ConvertJavaStringToUTF8(JNIEnv* env, - jstring str, - std::string* result); -BASE_EXPORT std::string ConvertJavaStringToUTF8(JNIEnv* env, jstring str); -BASE_EXPORT std::string ConvertJavaStringToUTF8(const JavaRef& str); -BASE_EXPORT std::string ConvertJavaStringToUTF8(JNIEnv* env, - const JavaRef& str); - -// Convert a std string to Java string. -BASE_EXPORT ScopedJavaLocalRef ConvertUTF8ToJavaString( - JNIEnv* env, - const base::StringPiece& str); - -// Convert a Java string to UTF16. Returns a string16. -BASE_EXPORT void ConvertJavaStringToUTF16(JNIEnv* env, - jstring str, - string16* result); -BASE_EXPORT string16 ConvertJavaStringToUTF16(JNIEnv* env, jstring str); -BASE_EXPORT string16 ConvertJavaStringToUTF16(const JavaRef& str); -BASE_EXPORT string16 ConvertJavaStringToUTF16(JNIEnv* env, - const JavaRef& str); - -// Convert a string16 to a Java string. -BASE_EXPORT ScopedJavaLocalRef ConvertUTF16ToJavaString( - JNIEnv* env, - const base::StringPiece16& str); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_JNI_STRING_H_ diff --git a/android/jni_string_unittest.cc b/android/jni_string_unittest.cc deleted file mode 100644 index 3da8b870e..000000000 --- a/android/jni_string_unittest.cc +++ /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. - -#include "base/android/jni_string.h" - -#include "base/android/jni_android.h" -#include "base/android/scoped_java_ref.h" -#include "base/strings/utf_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace android { - -TEST(JniString, BasicConversionsUTF8) { - const std::string kSimpleString = "SimpleTest8"; - JNIEnv* env = AttachCurrentThread(); - std::string result = - ConvertJavaStringToUTF8(ConvertUTF8ToJavaString(env, kSimpleString)); - EXPECT_EQ(kSimpleString, result); -} - -TEST(JniString, BasicConversionsUTF16) { - const string16 kSimpleString = UTF8ToUTF16("SimpleTest16"); - JNIEnv* env = AttachCurrentThread(); - string16 result = - ConvertJavaStringToUTF16(ConvertUTF16ToJavaString(env, kSimpleString)); - EXPECT_EQ(kSimpleString, result); -} - -TEST(JniString, EmptyConversionUTF8) { - const std::string kEmptyString = ""; - JNIEnv* env = AttachCurrentThread(); - std::string result = - ConvertJavaStringToUTF8(ConvertUTF8ToJavaString(env, kEmptyString)); - EXPECT_EQ(kEmptyString, result); -} - -TEST(JniString, EmptyConversionUTF16) { - const string16 kEmptyString = UTF8ToUTF16(""); - JNIEnv* env = AttachCurrentThread(); - string16 result = - ConvertJavaStringToUTF16(ConvertUTF16ToJavaString(env, kEmptyString)); - EXPECT_EQ(kEmptyString, result); -} - -} // namespace android -} // namespace base diff --git a/android/jni_utils.cc b/android/jni_utils.cc deleted file mode 100644 index c5e370c9f..000000000 --- a/android/jni_utils.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/jni_utils.h" - -#include "base/android/scoped_java_ref.h" - -#include "jni/JNIUtils_jni.h" - -namespace base { -namespace android { - -ScopedJavaLocalRef GetClassLoader(JNIEnv* env) { - return Java_JNIUtils_getClassLoader(env); -} - -bool IsSelectiveJniRegistrationEnabled(JNIEnv* env) { - return Java_JNIUtils_isSelectiveJniRegistrationEnabled(env); -} - -} // namespace android -} // namespace base - diff --git a/android/jni_utils.h b/android/jni_utils.h deleted file mode 100644 index c626ba460..000000000 --- a/android/jni_utils.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_JNI_UTILS_H_ -#define BASE_ANDROID_JNI_UTILS_H_ - -#include - -#include "base/android/scoped_java_ref.h" - -namespace base { - -namespace android { - -// Gets a ClassLoader instance capable of loading Chromium java classes. -// This should be called either from JNI_OnLoad or from within a method called -// via JNI from Java. -BASE_EXPORT ScopedJavaLocalRef GetClassLoader(JNIEnv* env); - -// Returns true if the current process permits selective JNI registration. -BASE_EXPORT bool IsSelectiveJniRegistrationEnabled(JNIEnv* env); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_JNI_UTILS_H_ - diff --git a/android/jni_weak_ref.cc b/android/jni_weak_ref.cc deleted file mode 100644 index 88efa723e..000000000 --- a/android/jni_weak_ref.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/jni_weak_ref.h" - -#include - -#include "base/android/jni_android.h" -#include "base/logging.h" - -using base::android::AttachCurrentThread; - -JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef() : obj_(nullptr) {} - -JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef( - const JavaObjectWeakGlobalRef& orig) - : obj_(nullptr) { - Assign(orig); -} - -JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef( - JavaObjectWeakGlobalRef&& orig) noexcept - : obj_(orig.obj_) { - orig.obj_ = nullptr; -} - -JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj) - : obj_(env->NewWeakGlobalRef(obj)) { -} - -JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef( - JNIEnv* env, - const base::android::JavaRef& obj) - : obj_(env->NewWeakGlobalRef(obj.obj())) { -} - -JavaObjectWeakGlobalRef::~JavaObjectWeakGlobalRef() { - reset(); -} - -void JavaObjectWeakGlobalRef::operator=(const JavaObjectWeakGlobalRef& rhs) { - Assign(rhs); -} - -void JavaObjectWeakGlobalRef::operator=(JavaObjectWeakGlobalRef&& rhs) { - std::swap(obj_, rhs.obj_); -} - -void JavaObjectWeakGlobalRef::reset() { - if (obj_) { - AttachCurrentThread()->DeleteWeakGlobalRef(obj_); - obj_ = nullptr; - } -} - -base::android::ScopedJavaLocalRef - JavaObjectWeakGlobalRef::get(JNIEnv* env) const { - return GetRealObject(env, obj_); -} - -base::android::ScopedJavaLocalRef GetRealObject( - JNIEnv* env, jweak obj) { - jobject real = nullptr; - if (obj) - real = env->NewLocalRef(obj); - return base::android::ScopedJavaLocalRef(env, real); -} - -void JavaObjectWeakGlobalRef::Assign(const JavaObjectWeakGlobalRef& other) { - if (&other == this) - return; - - JNIEnv* env = AttachCurrentThread(); - if (obj_) - env->DeleteWeakGlobalRef(obj_); - - obj_ = other.obj_ ? env->NewWeakGlobalRef(other.obj_) : nullptr; -} diff --git a/android/jni_weak_ref.h b/android/jni_weak_ref.h deleted file mode 100644 index 43a26b5fe..000000000 --- a/android/jni_weak_ref.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_JNI_WEAK_REF_H_ -#define BASE_ANDROID_JNI_WEAK_REF_H_ - -#include - -#include "base/android/scoped_java_ref.h" -#include "base/base_export.h" - -// Manages WeakGlobalRef lifecycle. -// This class is not thread-safe w.r.t. get() and reset(). Multiple threads may -// safely use get() concurrently, but if the user calls reset() (or of course, -// calls the destructor) they'll need to provide their own synchronization. -class BASE_EXPORT JavaObjectWeakGlobalRef { - public: - JavaObjectWeakGlobalRef(); - JavaObjectWeakGlobalRef(const JavaObjectWeakGlobalRef& orig); - JavaObjectWeakGlobalRef(JavaObjectWeakGlobalRef&& orig) noexcept; - JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj); - JavaObjectWeakGlobalRef(JNIEnv* env, - const base::android::JavaRef& obj); - virtual ~JavaObjectWeakGlobalRef(); - - void operator=(const JavaObjectWeakGlobalRef& rhs); - void operator=(JavaObjectWeakGlobalRef&& rhs); - - base::android::ScopedJavaLocalRef get(JNIEnv* env) const; - - // Returns true if the weak reference has not been initialized to point at - // an object (or ḣas had reset() called). - // Do not call this to test if the object referred to still exists! The weak - // reference remains initialized even if the target object has been collected. - bool is_uninitialized() const { return obj_ == nullptr; } - - void reset(); - - private: - void Assign(const JavaObjectWeakGlobalRef& rhs); - - jweak obj_; -}; - -// Get the real object stored in the weak reference returned as a -// ScopedJavaLocalRef. -BASE_EXPORT base::android::ScopedJavaLocalRef GetRealObject( - JNIEnv* env, jweak obj); - -#endif // BASE_ANDROID_JNI_WEAK_REF_H_ diff --git a/android/junit/src/org/chromium/base/ApplicationStatusTest.java b/android/junit/src/org/chromium/base/ApplicationStatusTest.java deleted file mode 100644 index eb1829539..000000000 --- a/android/junit/src/org/chromium/base/ApplicationStatusTest.java +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.app.Activity; -import android.view.KeyEvent; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.Robolectric; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.Shadows; -import org.robolectric.android.controller.ActivityController; -import org.robolectric.annotation.Config; -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.shadows.ShadowActivity; -import org.robolectric.shadows.multidex.ShadowMultiDex; - -import org.chromium.base.test.BaseRobolectricTestRunner; - -/** Unit tests for {@link ApplicationStatus}. */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE, - shadows = {ApplicationStatusTest.TrackingShadowActivity.class, ShadowMultiDex.class}) -public class ApplicationStatusTest { - /** Shadow that tracks calls to onWindowFocusChanged and dispatchKeyEvent. */ - @Implements(Activity.class) - public static class TrackingShadowActivity extends ShadowActivity { - private int mWindowFocusCalls; - private int mDispatchKeyEventCalls; - private boolean mReturnValueForKeyDispatch; - - @Implementation - public void onWindowFocusChanged(@SuppressWarnings("unused") boolean hasFocus) { - mWindowFocusCalls++; - } - - @Implementation - public boolean dispatchKeyEvent(@SuppressWarnings("unused") KeyEvent event) { - mDispatchKeyEventCalls++; - return mReturnValueForKeyDispatch; - } - } - - @Test - public void testWindowsFocusChanged() throws Exception { - ApplicationStatus.initialize(RuntimeEnvironment.application); - - ApplicationStatus.WindowFocusChangedListener mock = - mock(ApplicationStatus.WindowFocusChangedListener.class); - ApplicationStatus.registerWindowFocusChangedListener(mock); - - ActivityController controller = - Robolectric.buildActivity(Activity.class).create().start().visible(); - TrackingShadowActivity shadow = (TrackingShadowActivity) Shadows.shadowOf(controller.get()); - - controller.get().getWindow().getCallback().onWindowFocusChanged(true); - // Assert that listeners were notified. - verify(mock).onWindowFocusChanged(controller.get(), true); - // Also ensure that the original activity is forwarded the notification. - Assert.assertEquals(1, shadow.mWindowFocusCalls); - } -} diff --git a/android/junit/src/org/chromium/base/DiscardableReferencePoolTest.java b/android/junit/src/org/chromium/base/DiscardableReferencePoolTest.java deleted file mode 100644 index 5eff6c948..000000000 --- a/android/junit/src/org/chromium/base/DiscardableReferencePoolTest.java +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; - -import org.chromium.base.DiscardableReferencePool.DiscardableReference; -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.base.test.util.RetryOnFailure; - -import java.lang.ref.WeakReference; - -/** - * Tests for {@link DiscardableReferencePool}. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class DiscardableReferencePoolTest { - /** - * Tests that draining the pool clears references and allows objects to be garbage collected. - */ - @Test - public void testDrain() { - DiscardableReferencePool pool = new DiscardableReferencePool(); - - Object object = new Object(); - WeakReference weakReference = new WeakReference<>(object); - - DiscardableReference discardableReference = pool.put(object); - Assert.assertEquals(object, discardableReference.get()); - - // Drop reference to the object itself, to allow it to be garbage-collected. - object = null; - - pool.drain(); - - // The discardable reference should be null now. - Assert.assertNull(discardableReference.get()); - - // The object is not (strongly) reachable anymore, so the weak reference may or may not be - // null (it could be if a GC has happened since the pool was drained). - // After an explicit GC call it definitely should be null. - Runtime.getRuntime().gc(); - - Assert.assertNull(weakReference.get()); - } - - /** - * Tests that dropping the (last) discardable reference to an object allows it to be regularly - * garbage collected. - */ - @Test - @RetryOnFailure - public void testReferenceGCd() { - DiscardableReferencePool pool = new DiscardableReferencePool(); - - Object object = new Object(); - WeakReference weakReference = new WeakReference<>(object); - - DiscardableReference discardableReference = pool.put(object); - Assert.assertEquals(object, discardableReference.get()); - - // Drop reference to the object itself and to the discardable reference, allowing the object - // to be garbage-collected. - object = null; - discardableReference = null; - - // The object is not (strongly) reachable anymore, so the weak reference may or may not be - // null (it could be if a GC has happened since the pool was drained). - // After an explicit GC call it definitely should be null. - Runtime.getRuntime().gc(); - - Assert.assertNull(weakReference.get()); - } -} diff --git a/android/junit/src/org/chromium/base/LogTest.java b/android/junit/src/org/chromium/base/LogTest.java deleted file mode 100644 index a3832a0aa..000000000 --- a/android/junit/src/org/chromium/base/LogTest.java +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowLog; - -import org.chromium.base.test.BaseRobolectricTestRunner; - -import java.util.List; - -/** Unit tests for {@link Log}. */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class LogTest { - /** Tests that the computed call origin is the correct one. */ - @Test - public void callOriginTest() { - Log.d("Foo", "Bar"); - - List logs = ShadowLog.getLogs(); - assertEquals("Only one log should be written", 1, logs.size()); - - assertTrue("The origin of the log message (" + logs.get(0).msg + ") looks wrong.", - logs.get(0).msg.matches("\\[LogTest.java:\\d+\\].*")); - } - - @Test - public void normalizeTagTest() { - assertEquals("cr_foo", Log.normalizeTag("cr.foo")); - assertEquals("cr_foo", Log.normalizeTag("cr_foo")); - assertEquals("cr_foo", Log.normalizeTag("foo")); - assertEquals("cr_ab_foo", Log.normalizeTag("ab_foo")); - } - - /** Tests that exceptions provided to the log functions are properly recognized and printed. */ - @Test - public void exceptionLoggingTest() { - Throwable t = new Throwable() { - @Override - public String toString() { - return "MyThrowable"; - } - }; - - Throwable t2 = new Throwable() { - @Override - public String toString() { - return "MyOtherThrowable"; - } - }; - - List logs; - - // The throwable gets printed out - Log.i("Foo", "Bar", t); - logs = ShadowLog.getLogs(); - assertEquals(t, logs.get(logs.size() - 1).throwable); - assertEquals("Bar", logs.get(logs.size() - 1).msg); - - // The throwable can be both added to the message itself and printed out - Log.i("Foo", "Bar %s", t); - logs = ShadowLog.getLogs(); - assertEquals(t, logs.get(logs.size() - 1).throwable); - assertEquals("Bar MyThrowable", logs.get(logs.size() - 1).msg); - - // Non throwable are properly identified - Log.i("Foo", "Bar %s", t, "Baz"); - logs = ShadowLog.getLogs(); - assertNull(logs.get(logs.size() - 1).throwable); - assertEquals("Bar MyThrowable", logs.get(logs.size() - 1).msg); - - // The last throwable is the one used that is going to be printed out - Log.i("Foo", "Bar %s %s", t, t2); - logs = ShadowLog.getLogs(); - assertEquals(t2, logs.get(logs.size() - 1).throwable); - assertEquals("Bar MyThrowable MyOtherThrowable", logs.get(logs.size() - 1).msg); - } -} diff --git a/android/junit/src/org/chromium/base/NonThreadSafeTest.java b/android/junit/src/org/chromium/base/NonThreadSafeTest.java deleted file mode 100644 index 9c57199c9..000000000 --- a/android/junit/src/org/chromium/base/NonThreadSafeTest.java +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.BlockJUnit4ClassRunner; - -import org.chromium.base.test.util.Feature; - -/** - * Tests for NonThreadSafe. - */ -@RunWith(BlockJUnit4ClassRunner.class) -public class NonThreadSafeTest { - /** - * Test for creating and using on the same thread - */ - @Test - @Feature({"Android-AppBase"}) - public void testCreateAndUseOnSameThread() { - NonThreadSafe t = new NonThreadSafe(); - Assert.assertTrue(t.calledOnValidThread()); - } - - /** - * Test if calledOnValidThread returns false if used on another thread. - */ - @Test - @Feature({"Android-AppBase"}) - public void testCreateAndUseOnDifferentThread() { - final NonThreadSafe t = new NonThreadSafe(); - - new Thread(new Runnable() { - @Override - public void run() { - Assert.assertFalse(t.calledOnValidThread()); - } - }).start(); - } - - /** - * Test if detachFromThread reassigns the thread. - */ - @Test - @Feature({"Android-AppBase"}) - public void testDetachFromThread() { - final NonThreadSafe t = new NonThreadSafe(); - Assert.assertTrue(t.calledOnValidThread()); - t.detachFromThread(); - - new Thread(new Runnable() { - @Override - public void run() { - Assert.assertTrue(t.calledOnValidThread()); - Assert.assertTrue(t.calledOnValidThread()); - } - }).start(); - } -} diff --git a/android/junit/src/org/chromium/base/PromiseTest.java b/android/junit/src/org/chromium/base/PromiseTest.java deleted file mode 100644 index 58d39564f..000000000 --- a/android/junit/src/org/chromium/base/PromiseTest.java +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowLooper; - -import org.chromium.base.Promise.UnhandledRejectionException; -import org.chromium.base.test.BaseRobolectricTestRunner; - -/** Unit tests for {@link Promise}. */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class PromiseTest { - // We need a simple mutable reference type for testing. - private static class Value { - private int mValue; - - public int get() { - return mValue; - } - - public void set(int value) { - mValue = value; - } - } - - /** Tests that the callback is called on fulfillment. */ - @Test - public void callback() { - final Value value = new Value(); - - Promise promise = new Promise(); - promise.then(PromiseTest.setValue(value, 1)); - - assertEquals(value.get(), 0); - - promise.fulfill(new Integer(1)); - assertEquals(value.get(), 1); - } - - /** Tests that multiple callbacks are called. */ - @Test - public void multipleCallbacks() { - final Value value = new Value(); - - Promise promise = new Promise(); - Callback callback = new Callback() { - @Override - public void onResult(Integer result) { - value.set(value.get() + 1); - } - }; - promise.then(callback); - promise.then(callback); - - assertEquals(value.get(), 0); - - promise.fulfill(new Integer(0)); - assertEquals(value.get(), 2); - } - - /** Tests that a callback is called immediately when given to a fulfilled Promise. */ - @Test - public void callbackOnFulfilled() { - final Value value = new Value(); - - Promise promise = Promise.fulfilled(new Integer(0)); - assertEquals(value.get(), 0); - - promise.then(PromiseTest.setValue(value, 1)); - - assertEquals(value.get(), 1); - } - - /** Tests that promises can chain synchronous functions correctly. */ - @Test - public void promiseChaining() { - Promise promise = new Promise(); - final Value value = new Value(); - - promise.then(new Promise.Function(){ - @Override - public String apply(Integer arg) { - return arg.toString(); - } - }).then(new Promise.Function(){ - @Override - public String apply(String arg) { - return arg + arg; - } - }).then(new Callback() { - @Override - public void onResult(String result) { - value.set(result.length()); - } - }); - - promise.fulfill(new Integer(123)); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - assertEquals(6, value.get()); - } - - /** Tests that promises can chain asynchronous functions correctly. */ - @Test - public void promiseChainingAsyncFunctions() { - Promise promise = new Promise(); - final Value value = new Value(); - - final Promise innerPromise = new Promise(); - - promise.then(new Promise.AsyncFunction() { - @Override - public Promise apply(Integer arg) { - return innerPromise; - } - }).then(new Callback(){ - @Override - public void onResult(String result) { - value.set(result.length()); - } - }); - - assertEquals(0, value.get()); - - promise.fulfill(5); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - assertEquals(0, value.get()); - - innerPromise.fulfill("abc"); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - assertEquals(3, value.get()); - } - - /** Tests that a Promise that does not use its result does not throw on rejection. */ - @Test - public void rejectPromiseNoCallbacks() { - Promise promise = new Promise(); - - boolean caught = false; - try { - promise.reject(); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - } catch (UnhandledRejectionException e) { - caught = true; - } - assertFalse(caught); - } - - /** Tests that a Promise that uses its result throws on rejection if it has no handler. */ - @Test - public void rejectPromiseNoHandler() { - Promise promise = new Promise(); - promise.then(PromiseTest.identity()).then(PromiseTest.pass()); - - boolean caught = false; - try { - promise.reject(); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - } catch (UnhandledRejectionException e) { - caught = true; - } - assertTrue(caught); - } - - /** Tests that a Promise that handles rejection does not throw on rejection. */ - @Test - public void rejectPromiseHandled() { - Promise promise = new Promise(); - promise.then(PromiseTest.identity()) - .then(PromiseTest.pass(), PromiseTest.pass()); - - boolean caught = false; - try { - promise.reject(); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - } catch (UnhandledRejectionException e) { - caught = true; - } - assertFalse(caught); - } - - /** Tests that rejections carry the exception information. */ - @Test - public void rejectionInformation() { - Promise promise = new Promise(); - promise.then(PromiseTest.pass()); - - String message = "Promise Test"; - try { - promise.reject(new NegativeArraySizeException(message)); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - fail(); - } catch (UnhandledRejectionException e) { - assertTrue(e.getCause() instanceof NegativeArraySizeException); - assertEquals(e.getCause().getMessage(), message); - } - } - - /** Tests that rejections propagate. */ - @Test - public void rejectionChaining() { - final Value value = new Value(); - Promise promise = new Promise(); - - Promise result = - promise.then(PromiseTest.identity()).then(PromiseTest.identity()); - - result.then(PromiseTest.pass(), PromiseTest.setValue(value, 5)); - - promise.reject(new Exception()); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - - assertEquals(value.get(), 5); - assertTrue(result.isRejected()); - } - - /** Tests that Promises get rejected if a Function throws. */ - @Test - public void rejectOnThrow() { - Value value = new Value(); - Promise promise = new Promise(); - promise.then(new Promise.Function() { - @Override - public Integer apply(Integer argument) { - throw new IllegalArgumentException(); - } - }).then(PromiseTest.pass(), PromiseTest.setValue(value, 5)); - - promise.fulfill(0); - - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - assertEquals(value.get(), 5); - } - - /** Tests that Promises get rejected if an AsyncFunction throws. */ - @Test - public void rejectOnAsyncThrow() { - Value value = new Value(); - Promise promise = new Promise(); - - promise.then(new Promise.AsyncFunction() { - @Override - public Promise apply(Integer argument) { - throw new IllegalArgumentException(); - } - }).then(PromiseTest.pass(), PromiseTest.setValue(value, 5)); - - promise.fulfill(0); - - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - assertEquals(value.get(), 5); - } - - /** Tests that Promises get rejected if an AsyncFunction rejects. */ - @Test - public void rejectOnAsyncReject() { - Value value = new Value(); - Promise promise = new Promise(); - final Promise inner = new Promise(); - - promise.then(new Promise.AsyncFunction() { - @Override - public Promise apply(Integer argument) { - return inner; - } - }).then(PromiseTest.pass(), PromiseTest.setValue(value, 5)); - - promise.fulfill(0); - - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - assertEquals(value.get(), 0); - - inner.reject(); - - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - assertEquals(value.get(), 5); - } - - /** Convenience method that returns a Callback that does nothing with its result. */ - private static Callback pass() { - return new Callback() { - @Override - public void onResult(T result) {} - }; - } - - /** Convenience method that returns a Function that just passes through its argument. */ - private static Promise.Function identity() { - return new Promise.Function() { - @Override - public T apply(T argument) { - return argument; - } - }; - } - - /** Convenience method that returns a Callback that sets the given Value on execution. */ - private static Callback setValue(final Value toSet, final int value) { - return new Callback() { - @Override - public void onResult(T result) { - toSet.set(value); - } - }; - } -} diff --git a/android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java b/android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java deleted file mode 100644 index 1249a66ac..000000000 --- a/android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.memory; - -import android.content.ComponentCallbacks2; -import android.os.Looper; -import android.support.test.filters.SmallTest; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowLooper; - -import org.chromium.base.MemoryPressureLevel; -import org.chromium.base.Supplier; -import org.chromium.base.ThreadUtils; -import org.chromium.base.test.BaseRobolectricTestRunner; - -import java.util.concurrent.TimeUnit; - -/** - * Test for MemoryPressureMonitor. - */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class MemoryPressureMonitorTest { - private MemoryPressureMonitor mMonitor; - - private static class TestPressureCallback implements MemoryPressureCallback { - private Integer mReportedPressure; - - public void assertCalledWith(@MemoryPressureLevel int expectedPressure) { - Assert.assertNotNull("Callback was not called", mReportedPressure); - Assert.assertEquals(expectedPressure, (int) mReportedPressure); - } - - public void assertNotCalled() { - Assert.assertNull(mReportedPressure); - } - - public void reset() { - mReportedPressure = null; - } - - @Override - public void onPressure(@MemoryPressureLevel int pressure) { - assertNotCalled(); - mReportedPressure = pressure; - } - } - - private static class TestPressureSupplier implements Supplier { - private @MemoryPressureLevel Integer mPressure; - private boolean mIsCalled; - - public TestPressureSupplier(@MemoryPressureLevel Integer pressure) { - mPressure = pressure; - } - - @Override - public @MemoryPressureLevel Integer get() { - assertNotCalled(); - mIsCalled = true; - return mPressure; - } - - public void assertCalled() { - Assert.assertTrue(mIsCalled); - } - - public void assertNotCalled() { - Assert.assertFalse(mIsCalled); - } - } - - private static final int THROTTLING_INTERVAL_MS = 1000; - - @Before - public void setUp() { - // Explicitly set main thread as UiThread. Other places rely on that. - ThreadUtils.setUiThread(Looper.getMainLooper()); - - // Pause main thread to get control over when tasks are run (see runUiThreadFor()). - ShadowLooper.pauseMainLooper(); - - mMonitor = new MemoryPressureMonitor(THROTTLING_INTERVAL_MS); - mMonitor.setCurrentPressureSupplierForTesting(null); - } - - /** - * Runs all UiThread tasks posted |delayMs| in the future. - * @param delayMs - */ - private void runUiThreadFor(long delayMs) { - ShadowLooper.idleMainLooper(delayMs, TimeUnit.MILLISECONDS); - } - - @Test - @SmallTest - public void testTrimLevelTranslation() { - Integer[][] trimLevelToPressureMap = {// - // Levels >= TRIM_MEMORY_COMPLETE map to CRITICAL. - {ComponentCallbacks2.TRIM_MEMORY_COMPLETE + 1, MemoryPressureLevel.CRITICAL}, - {ComponentCallbacks2.TRIM_MEMORY_COMPLETE, MemoryPressureLevel.CRITICAL}, - - // TRIM_MEMORY_RUNNING_CRITICAL maps to CRITICAL. - {ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL, MemoryPressureLevel.CRITICAL}, - - // Levels < TRIM_MEMORY_COMPLETE && >= TRIM_MEMORY_BACKGROUND map to MODERATE. - {ComponentCallbacks2.TRIM_MEMORY_COMPLETE - 1, MemoryPressureLevel.MODERATE}, - {ComponentCallbacks2.TRIM_MEMORY_BACKGROUND + 1, MemoryPressureLevel.MODERATE}, - {ComponentCallbacks2.TRIM_MEMORY_BACKGROUND, MemoryPressureLevel.MODERATE}, - - // Other levels are not mapped. - {ComponentCallbacks2.TRIM_MEMORY_BACKGROUND - 1, null}, - {ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW, null}, - {ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE, null}, - {ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN, null}}; - for (Integer[] trimLevelAndPressure : trimLevelToPressureMap) { - int trimLevel = trimLevelAndPressure[0]; - Integer expectedPressure = trimLevelAndPressure[1]; - Integer actualPressure = MemoryPressureMonitor.memoryPressureFromTrimLevel(trimLevel); - Assert.assertEquals(expectedPressure, actualPressure); - } - } - - @Test - @SmallTest - public void testThrottleInterval() { - TestPressureCallback callback = new TestPressureCallback(); - mMonitor.setReportingCallbackForTesting(callback); - - // First notification should go through. - mMonitor.notifyPressure(MemoryPressureLevel.NONE); - callback.assertCalledWith(MemoryPressureLevel.NONE); - - callback.reset(); - - // This one should be throttled. - mMonitor.notifyPressure(MemoryPressureLevel.NONE); - callback.assertNotCalled(); - - runUiThreadFor(THROTTLING_INTERVAL_MS - 1); - - // We're still within the throttling interval, so this notification should - // still be throttled. - mMonitor.notifyPressure(MemoryPressureLevel.NONE); - callback.assertNotCalled(); - - runUiThreadFor(1); - - // We're past the throttling interval at this point, so this notification - // should go through. - mMonitor.notifyPressure(MemoryPressureLevel.NONE); - callback.assertCalledWith(MemoryPressureLevel.NONE); - } - - @Test - @SmallTest - public void testChangeNotIgnored() { - TestPressureCallback callback = new TestPressureCallback(); - mMonitor.setReportingCallbackForTesting(callback); - - mMonitor.notifyPressure(MemoryPressureLevel.NONE); - callback.assertCalledWith(MemoryPressureLevel.NONE); - - callback.reset(); - - // Second notification is throttled, but should be reported after the - // throttling interval ends. - mMonitor.notifyPressure(MemoryPressureLevel.MODERATE); - callback.assertNotCalled(); - - runUiThreadFor(THROTTLING_INTERVAL_MS - 1); - - // Shouldn't be reported at this point. - callback.assertNotCalled(); - - runUiThreadFor(1); - - callback.assertCalledWith(MemoryPressureLevel.MODERATE); - } - - @Test - @SmallTest - public void testNoopChangeIgnored() { - TestPressureCallback callback = new TestPressureCallback(); - mMonitor.setReportingCallbackForTesting(callback); - - mMonitor.notifyPressure(MemoryPressureLevel.NONE); - callback.assertCalledWith(MemoryPressureLevel.NONE); - - callback.reset(); - - // Report MODERATE and then NONE, so that the throttling interval finishes with the - // same pressure that started it (i.e. NONE). In this case MODERATE should be ignored. - mMonitor.notifyPressure(MemoryPressureLevel.MODERATE); - mMonitor.notifyPressure(MemoryPressureLevel.NONE); - - runUiThreadFor(THROTTLING_INTERVAL_MS); - - callback.assertNotCalled(); - } - - @Test - @SmallTest - public void testPollingInitiallyDisabled() { - TestPressureSupplier pressureSupplier = - new TestPressureSupplier(MemoryPressureLevel.MODERATE); - mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier); - - mMonitor.notifyPressure(MemoryPressureLevel.CRITICAL); - runUiThreadFor(THROTTLING_INTERVAL_MS); - - // We finished the interval with CRITICAL, but since polling is disabled, we shouldn't - // poll the current pressure. - pressureSupplier.assertNotCalled(); - } - - @Test - @SmallTest - public void testEnablePollingPolls() { - TestPressureCallback callback = new TestPressureCallback(); - mMonitor.setReportingCallbackForTesting(callback); - - TestPressureSupplier pressureSupplier = - new TestPressureSupplier(MemoryPressureLevel.MODERATE); - mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier); - - mMonitor.enablePolling(); - - // When polling is enabled, current pressure should be retrieved and reported. - pressureSupplier.assertCalled(); - callback.assertCalledWith(MemoryPressureLevel.MODERATE); - } - - @Test - @SmallTest - public void testNullSupplierResultIgnored() { - TestPressureCallback callback = new TestPressureCallback(); - mMonitor.setReportingCallbackForTesting(callback); - - TestPressureSupplier pressureSupplier = new TestPressureSupplier(null); - mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier); - - mMonitor.enablePolling(); - - // The pressure supplier should be called, but its null result should be ignored. - pressureSupplier.assertCalled(); - callback.assertNotCalled(); - } - - @Test - @SmallTest - public void testEnablePollingRespectsThrottling() { - TestPressureSupplier pressureSupplier = - new TestPressureSupplier(MemoryPressureLevel.MODERATE); - mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier); - - mMonitor.notifyPressure(MemoryPressureLevel.NONE); - - // The notification above started a throttling interval, so we shouldn't ask for the - // current pressure when polling is enabled. - mMonitor.enablePolling(); - - pressureSupplier.assertNotCalled(); - } - - @Test - @SmallTest - public void testPollingIfCRITICAL() { - TestPressureCallback callback = new TestPressureCallback(); - mMonitor.setReportingCallbackForTesting(callback); - - TestPressureSupplier pressureSupplier = - new TestPressureSupplier(MemoryPressureLevel.MODERATE); - mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier); - - mMonitor.notifyPressure(MemoryPressureLevel.CRITICAL); - callback.reset(); - - mMonitor.enablePolling(); - - runUiThreadFor(THROTTLING_INTERVAL_MS - 1); - - // Pressure should be polled after the interval ends, not before. - pressureSupplier.assertNotCalled(); - - runUiThreadFor(1); - - // We started and finished the throttling interval with CRITICAL pressure, so - // we should poll the current pressure at the end of the interval. - pressureSupplier.assertCalled(); - callback.assertCalledWith(MemoryPressureLevel.MODERATE); - } - - @Test - @SmallTest - public void testNoPollingIfNotCRITICAL() { - TestPressureSupplier pressureSupplier = new TestPressureSupplier(MemoryPressureLevel.NONE); - mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier); - - mMonitor.notifyPressure(MemoryPressureLevel.MODERATE); - - mMonitor.enablePolling(); - - runUiThreadFor(THROTTLING_INTERVAL_MS); - - // We started and finished the throttling interval with non-CRITICAL pressure, - // so no polling should take place. - pressureSupplier.assertNotCalled(); - } - - @Test - @SmallTest - public void testNoPollingIfChangedToCRITICAL() { - TestPressureSupplier pressureSupplier = new TestPressureSupplier(MemoryPressureLevel.NONE); - mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier); - - mMonitor.notifyPressure(MemoryPressureLevel.MODERATE); - mMonitor.notifyPressure(MemoryPressureLevel.CRITICAL); - - mMonitor.enablePolling(); - - runUiThreadFor(THROTTLING_INTERVAL_MS); - - // We finished the throttling interval with CRITITCAL, but started with MODERATE, - // so no polling should take place. - pressureSupplier.assertNotCalled(); - } - - @Test - @SmallTest - public void testDisablePolling() { - TestPressureSupplier pressureSupplier = new TestPressureSupplier(MemoryPressureLevel.NONE); - mMonitor.setCurrentPressureSupplierForTesting(pressureSupplier); - - mMonitor.notifyPressure(MemoryPressureLevel.CRITICAL); - - mMonitor.enablePolling(); - - runUiThreadFor(THROTTLING_INTERVAL_MS - 1); - - // Whether polling is enabled or not should be taken into account only after the interval - // finishes, so disabling it here should have the same affect as if it was never enabled. - mMonitor.disablePolling(); - - runUiThreadFor(1); - - pressureSupplier.assertNotCalled(); - } -} diff --git a/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java b/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java deleted file mode 100644 index 18fe6ce2a..000000000 --- a/android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogram.java +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.metrics.test; - -import android.util.Pair; - -import org.robolectric.annotation.Implementation; -import org.robolectric.annotation.Implements; -import org.robolectric.annotation.Resetter; - -import org.chromium.base.metrics.RecordHistogram; - -import java.util.HashMap; -import java.util.concurrent.TimeUnit; - -/** - * Implementation of RecordHistogram which does not rely on native and still enables testing of - * histogram counts. - */ -@Implements(RecordHistogram.class) -public class ShadowRecordHistogram { - private static HashMap, Integer> sSamples = - new HashMap, Integer>(); - - @Resetter - public static void reset() { - sSamples.clear(); - } - - @Implementation - public static void recordCountHistogram(String name, int sample) { - Pair key = Pair.create(name, sample); - incrementSampleCount(key); - } - - @Implementation - public static void recordLongTimesHistogram100(String name, long duration, TimeUnit timeUnit) { - Pair key = Pair.create(name, (int) timeUnit.toMillis(duration)); - incrementSampleCount(key); - } - - @Implementation - public static int getHistogramValueCountForTesting(String name, int sample) { - Integer i = sSamples.get(Pair.create(name, sample)); - return (i != null) ? i : 0; - } - - private static void incrementSampleCount(Pair key) { - Integer i = sSamples.get(key); - if (i == null) { - i = 0; - } - sSamples.put(key, i + 1); - } -} diff --git a/android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java b/android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java deleted file mode 100644 index 5c5bca912..000000000 --- a/android/junit/src/org/chromium/base/process_launcher/ChildConnectionAllocatorTest.java +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.process_launcher; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.content.ComponentName; -import android.content.Context; -import android.os.Bundle; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowLooper; - -import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.base.test.util.Feature; - -import java.util.HashSet; -import java.util.Set; - -/** Unit tests for the ChildConnectionAllocator class. */ -@Config(manifest = Config.NONE) -@RunWith(BaseRobolectricTestRunner.class) -public class ChildConnectionAllocatorTest { - private static final String TEST_PACKAGE_NAME = "org.chromium.allocator_test"; - - private static final int MAX_CONNECTION_NUMBER = 2; - - private static final int FREE_CONNECTION_TEST_CALLBACK_START_FAILED = 1; - private static final int FREE_CONNECTION_TEST_CALLBACK_PROCESS_DIED = 2; - - @Mock - private ChildProcessConnection.ServiceCallback mServiceCallback; - - static class TestConnectionFactory implements ChildConnectionAllocator.ConnectionFactory { - private ComponentName mLastServiceName; - - private ChildProcessConnection mConnection; - - private ChildProcessConnection.ServiceCallback mConnectionServiceCallback; - - @Override - public ChildProcessConnection createConnection(Context context, ComponentName serviceName, - boolean bindToCaller, boolean bindAsExternalService, Bundle serviceBundle) { - mLastServiceName = serviceName; - if (mConnection == null) { - mConnection = mock(ChildProcessConnection.class); - // Retrieve the ServiceCallback so we can simulate the service process dying. - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) { - mConnectionServiceCallback = - (ChildProcessConnection.ServiceCallback) invocation.getArgument(1); - return null; - } - }) - .when(mConnection) - .start(anyBoolean(), any(ChildProcessConnection.ServiceCallback.class)); - } - return mConnection; - } - - public ComponentName getAndResetLastServiceName() { - ComponentName serviceName = mLastServiceName; - mLastServiceName = null; - return serviceName; - } - - // Use this method to have a callback invoked when the connection is started on the next - // created connection. - public void invokeCallbackOnConnectionStart(final boolean onChildStarted, - final boolean onStartFailed, final boolean onChildProcessDied) { - final ChildProcessConnection connection = mock(ChildProcessConnection.class); - mConnection = connection; - doAnswer(new Answer() { - @Override - public Object answer(InvocationOnMock invocation) { - ChildProcessConnection.ServiceCallback serviceCallback = - (ChildProcessConnection.ServiceCallback) invocation.getArgument(1); - if (onChildStarted) { - serviceCallback.onChildStarted(); - } - if (onStartFailed) { - serviceCallback.onChildStartFailed(connection); - } - if (onChildProcessDied) { - serviceCallback.onChildProcessDied(connection); - } - return null; - } - }) - .when(mConnection) - .start(anyBoolean(), any(ChildProcessConnection.ServiceCallback.class)); - } - - public void simulateServiceStartFailed() { - mConnectionServiceCallback.onChildStartFailed(mConnection); - } - - public void simulateServiceProcessDying() { - mConnectionServiceCallback.onChildProcessDied(mConnection); - } - } - - private final TestConnectionFactory mTestConnectionFactory = new TestConnectionFactory(); - - private ChildConnectionAllocator mAllocator; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mAllocator = ChildConnectionAllocator.createForTest(null, TEST_PACKAGE_NAME, - "AllocatorTest", MAX_CONNECTION_NUMBER, true /* bindToCaller */, - false /* bindAsExternalService */, false /* useStrongBinding */); - mAllocator.setConnectionFactoryForTesting(mTestConnectionFactory); - } - - @Test - @Feature({"ProcessManagement"}) - public void testPlainAllocate() { - assertFalse(mAllocator.anyConnectionAllocated()); - assertEquals(MAX_CONNECTION_NUMBER, mAllocator.getNumberOfServices()); - - ChildProcessConnection connection = - mAllocator.allocate(null /* context */, null /* serviceBundle */, mServiceCallback); - assertNotNull(connection); - - verify(connection, times(1)) - .start(eq(false) /* useStrongBinding */, - any(ChildProcessConnection.ServiceCallback.class)); - assertTrue(mAllocator.anyConnectionAllocated()); - } - - /** Tests that different services are created until we reach the max number specified. */ - @Test - @Feature({"ProcessManagement"}) - public void testAllocateMaxNumber() { - assertTrue(mAllocator.isFreeConnectionAvailable()); - Set serviceNames = new HashSet<>(); - for (int i = 0; i < MAX_CONNECTION_NUMBER; i++) { - ChildProcessConnection connection = mAllocator.allocate( - null /* context */, null /* serviceBundle */, mServiceCallback); - assertNotNull(connection); - ComponentName serviceName = mTestConnectionFactory.getAndResetLastServiceName(); - assertFalse(serviceNames.contains(serviceName)); - serviceNames.add(serviceName); - } - assertFalse(mAllocator.isFreeConnectionAvailable()); - assertNull(mAllocator.allocate( - null /* context */, null /* serviceBundle */, mServiceCallback)); - } - - @Test - @Feature({"ProcessManagement"}) - public void testQueueAllocation() { - Runnable freeConnectionCallback = mock(Runnable.class); - mAllocator = ChildConnectionAllocator.createForTest(freeConnectionCallback, - TEST_PACKAGE_NAME, "AllocatorTest", 1, true /* bindToCaller */, - false /* bindAsExternalService */, false /* useStrongBinding */); - mAllocator.setConnectionFactoryForTesting(mTestConnectionFactory); - // Occupy all slots. - ChildProcessConnection connection = - mAllocator.allocate(null /* context */, null /* serviceBundle */, mServiceCallback); - assertNotNull(connection); - assertFalse(mAllocator.isFreeConnectionAvailable()); - - final ChildProcessConnection newConnection[] = new ChildProcessConnection[2]; - Runnable allocate1 = () -> { - newConnection[0] = mAllocator.allocate( - null /* context */, null /* serviceBundle */, mServiceCallback); - }; - Runnable allocate2 = () -> { - newConnection[1] = mAllocator.allocate( - null /* context */, null /* serviceBundle */, mServiceCallback); - }; - mAllocator.queueAllocation(allocate1); - mAllocator.queueAllocation(allocate2); - verify(freeConnectionCallback, times(1)).run(); - assertNull(newConnection[0]); - - mTestConnectionFactory.simulateServiceProcessDying(); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - assertNotNull(newConnection[0]); - assertNull(newConnection[1]); - - mTestConnectionFactory.simulateServiceProcessDying(); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - assertNotNull(newConnection[1]); - } - - /** - * Tests that the connection is created with the useStrongBinding parameter specified in the - * allocator. - */ - @Test - @Feature({"ProcessManagement"}) - public void testStrongBindingParam() { - for (boolean useStrongBinding : new boolean[] {true, false}) { - ChildConnectionAllocator allocator = ChildConnectionAllocator.createForTest(null, - TEST_PACKAGE_NAME, "AllocatorTest", MAX_CONNECTION_NUMBER, - true /* bindToCaller */, false /* bindAsExternalService */, useStrongBinding); - allocator.setConnectionFactoryForTesting(mTestConnectionFactory); - ChildProcessConnection connection = allocator.allocate( - null /* context */, null /* serviceBundle */, mServiceCallback); - verify(connection, times(0)).start(useStrongBinding, mServiceCallback); - } - } - - /** - * Tests that the various ServiceCallbacks are propagated and posted, so they happen after the - * ChildProcessAllocator,allocate() method has returned. - */ - public void runTestWithConnectionCallbacks( - boolean onChildStarted, boolean onChildStartFailed, boolean onChildProcessDied) { - // We have to pause the Roboletric looper or it'll execute the posted tasks synchronoulsy. - ShadowLooper.pauseMainLooper(); - mTestConnectionFactory.invokeCallbackOnConnectionStart( - onChildStarted, onChildStartFailed, onChildProcessDied); - ChildProcessConnection connection = - mAllocator.allocate(null /* context */, null /* serviceBundle */, mServiceCallback); - assertNotNull(connection); - - // Callbacks are posted. - verify(mServiceCallback, never()).onChildStarted(); - verify(mServiceCallback, never()).onChildStartFailed(any()); - verify(mServiceCallback, never()).onChildProcessDied(any()); - ShadowLooper.unPauseMainLooper(); - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - verify(mServiceCallback, times(onChildStarted ? 1 : 0)).onChildStarted(); - verify(mServiceCallback, times(onChildStartFailed ? 1 : 0)).onChildStartFailed(any()); - verify(mServiceCallback, times(onChildProcessDied ? 1 : 0)).onChildProcessDied(any()); - } - - @Test - @Feature({"ProcessManagement"}) - public void testOnChildStartedCallback() { - runTestWithConnectionCallbacks(true /* onChildStarted */, false /* onChildStartFailed */, - false /* onChildProcessDied */); - } - - @Test - @Feature({"ProcessManagement"}) - public void testOnChildStartFailedCallback() { - runTestWithConnectionCallbacks(false /* onChildStarted */, true /* onChildStartFailed */, - false /* onChildProcessDied */); - } - - @Test - @Feature({"ProcessManagement"}) - public void testOnChildProcessDiedCallback() { - runTestWithConnectionCallbacks(false /* onChildStarted */, false /* onChildStartFailed */, - true /* onChildProcessDied */); - } - - /** - * Tests that the allocator clears the connection when it fails to bind/process dies. - */ - private void testFreeConnection(int callbackType) { - ChildProcessConnection connection = - mAllocator.allocate(null /* context */, null /* serviceBundle */, mServiceCallback); - - assertNotNull(connection); - ComponentName serviceName = mTestConnectionFactory.getAndResetLastServiceName(); - verify(connection, times(1)) - .start(eq(false) /* useStrongBinding */, - any(ChildProcessConnection.ServiceCallback.class)); - assertTrue(mAllocator.anyConnectionAllocated()); - int onChildStartFailedExpectedCount = 0; - int onChildProcessDiedExpectedCount = 0; - switch (callbackType) { - case FREE_CONNECTION_TEST_CALLBACK_START_FAILED: - mTestConnectionFactory.simulateServiceStartFailed(); - onChildStartFailedExpectedCount = 1; - break; - case FREE_CONNECTION_TEST_CALLBACK_PROCESS_DIED: - mTestConnectionFactory.simulateServiceProcessDying(); - onChildProcessDiedExpectedCount = 1; - break; - default: - fail(); - break; - } - ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); - assertFalse(mAllocator.anyConnectionAllocated()); - verify(mServiceCallback, never()).onChildStarted(); - verify(mServiceCallback, times(onChildStartFailedExpectedCount)) - .onChildStartFailed(connection); - verify(mServiceCallback, times(onChildProcessDiedExpectedCount)) - .onChildProcessDied(connection); - - // Allocate a new connection to make sure we are not getting the same connection. - connection = - mAllocator.allocate(null /* context */, null /* serviceBundle */, mServiceCallback); - assertNotNull(connection); - assertNotEquals(mTestConnectionFactory.getAndResetLastServiceName(), serviceName); - } - - @Test - @Feature({"ProcessManagement"}) - public void testFreeConnectionOnChildStartFailed() { - testFreeConnection(FREE_CONNECTION_TEST_CALLBACK_START_FAILED); - } - - @Test - @Feature({"ProcessManagement"}) - public void testFreeConnectionOnChildProcessDied() { - testFreeConnection(FREE_CONNECTION_TEST_CALLBACK_PROCESS_DIED); - } -} diff --git a/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java b/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java deleted file mode 100644 index 95474ea1e..000000000 --- a/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java +++ /dev/null @@ -1,425 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base.process_launcher; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.AdditionalMatchers.or; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.ComponentName; -import android.content.Intent; -import android.os.Binder; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowLooper; - -import org.chromium.base.ChildBindingState; -import org.chromium.base.test.BaseRobolectricTestRunner; - -/** Unit tests for ChildProcessConnection. */ -@RunWith(BaseRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class ChildProcessConnectionTest { - private static class ChildServiceConnectionMock - implements ChildProcessConnection.ChildServiceConnection { - private final Intent mBindIntent; - private final ChildProcessConnection.ChildServiceConnectionDelegate mDelegate; - private boolean mBound; - - public ChildServiceConnectionMock( - Intent bindIntent, ChildProcessConnection.ChildServiceConnectionDelegate delegate) { - mBindIntent = bindIntent; - mDelegate = delegate; - } - - @Override - public boolean bind() { - mBound = true; - return true; - } - - @Override - public void unbind() { - mBound = false; - } - - @Override - public boolean isBound() { - return mBound; - } - - public void notifyServiceConnected(IBinder service) { - mDelegate.onServiceConnected(service); - } - - public void notifyServiceDisconnected() { - mDelegate.onServiceDisconnected(); - } - - public Intent getBindIntent() { - return mBindIntent; - } - }; - - private final ChildProcessConnection.ChildServiceConnectionFactory mServiceConnectionFactory = - new ChildProcessConnection.ChildServiceConnectionFactory() { - @Override - public ChildProcessConnection.ChildServiceConnection createConnection( - Intent bindIntent, int bindFlags, - ChildProcessConnection.ChildServiceConnectionDelegate delegate) { - ChildServiceConnectionMock connection = - spy(new ChildServiceConnectionMock(bindIntent, delegate)); - if (mFirstServiceConnection == null) { - mFirstServiceConnection = connection; - } - return connection; - } - }; - - @Mock - private ChildProcessConnection.ServiceCallback mServiceCallback; - - @Mock - private ChildProcessConnection.ConnectionCallback mConnectionCallback; - - private IChildProcessService mIChildProcessService; - - private Binder mChildProcessServiceBinder; - - private ChildServiceConnectionMock mFirstServiceConnection; - - // Parameters captured from the IChildProcessService.setupConnection() call - private Bundle mConnectionBundle; - private ICallbackInt mConnectionPidCallback; - private IBinder mConnectionIBinderCallback; - - @Before - public void setUp() throws RemoteException { - MockitoAnnotations.initMocks(this); - - mIChildProcessService = mock(IChildProcessService.class); - // Capture the parameters passed to the IChildProcessService.setupConnection() call. - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) { - mConnectionBundle = (Bundle) invocation.getArgument(0); - mConnectionPidCallback = (ICallbackInt) invocation.getArgument(1); - mConnectionIBinderCallback = (IBinder) invocation.getArgument(2); - return null; - } - }) - .when(mIChildProcessService) - .setupConnection( - or(isNull(), any(Bundle.class)), or(isNull(), any()), or(isNull(), any())); - - mChildProcessServiceBinder = new Binder(); - mChildProcessServiceBinder.attachInterface( - mIChildProcessService, IChildProcessService.class.getName()); - } - - private ChildProcessConnection createDefaultTestConnection() { - return createTestConnection(false /* bindToCaller */, false /* bindAsExternalService */, - null /* serviceBundle */); - } - - private ChildProcessConnection createTestConnection( - boolean bindToCaller, boolean bindAsExternalService, Bundle serviceBundle) { - String packageName = "org.chromium.test"; - String serviceName = "TestService"; - return new ChildProcessConnection(null /* context */, - new ComponentName(packageName, serviceName), bindToCaller, bindAsExternalService, - serviceBundle, mServiceConnectionFactory); - } - - @Test - public void testStrongBinding() { - ChildProcessConnection connection = createDefaultTestConnection(); - connection.start(true /* useStrongBinding */, null /* serviceCallback */); - assertTrue(connection.isStrongBindingBound()); - - connection = createDefaultTestConnection(); - connection.start(false /* useStrongBinding */, null /* serviceCallback */); - assertFalse(connection.isStrongBindingBound()); - } - - @Test - public void testServiceBundle() { - Bundle serviceBundle = new Bundle(); - final String intKey = "org.chromium.myInt"; - final int intValue = 34; - final int defaultValue = -1; - serviceBundle.putInt(intKey, intValue); - String stringKey = "org.chromium.myString"; - String stringValue = "thirty four"; - serviceBundle.putString(stringKey, stringValue); - - ChildProcessConnection connection = createTestConnection( - false /* bindToCaller */, false /* bindAsExternalService */, serviceBundle); - // Start the connection without the ChildServiceConnection connecting. - connection.start(false /* useStrongBinding */, null /* serviceCallback */); - assertNotNull(mFirstServiceConnection); - Intent bindIntent = mFirstServiceConnection.getBindIntent(); - assertNotNull(bindIntent); - assertEquals(intValue, bindIntent.getIntExtra(intKey, defaultValue)); - assertEquals(stringValue, bindIntent.getStringExtra(stringKey)); - } - - @Test - public void testServiceStartsSuccessfully() { - ChildProcessConnection connection = createDefaultTestConnection(); - assertNotNull(mFirstServiceConnection); - connection.start(false /* useStrongBinding */, mServiceCallback); - Assert.assertTrue(connection.isModerateBindingBound()); - Assert.assertFalse(connection.didOnServiceConnectedForTesting()); - verify(mServiceCallback, never()).onChildStarted(); - verify(mServiceCallback, never()).onChildStartFailed(any()); - verify(mServiceCallback, never()).onChildProcessDied(any()); - - // The service connects. - mFirstServiceConnection.notifyServiceConnected(null /* iBinder */); - Assert.assertTrue(connection.didOnServiceConnectedForTesting()); - verify(mServiceCallback, times(1)).onChildStarted(); - verify(mServiceCallback, never()).onChildStartFailed(any()); - verify(mServiceCallback, never()).onChildProcessDied(any()); - } - - @Test - public void testServiceStartsAndFailsToBind() { - ChildProcessConnection connection = createDefaultTestConnection(); - assertNotNull(mFirstServiceConnection); - // Note we use doReturn so the actual bind() method is not called (it would with - // when(mFirstServiceConnection.bind()).thenReturn(false). - doReturn(false).when(mFirstServiceConnection).bind(); - connection.start(false /* useStrongBinding */, mServiceCallback); - - Assert.assertFalse(connection.isModerateBindingBound()); - Assert.assertFalse(connection.didOnServiceConnectedForTesting()); - verify(mServiceCallback, never()).onChildStarted(); - verify(mServiceCallback, never()).onChildStartFailed(any()); - verify(mServiceCallback, times(1)).onChildProcessDied(connection); - } - - @Test - public void testServiceStops() { - ChildProcessConnection connection = createDefaultTestConnection(); - assertNotNull(mFirstServiceConnection); - connection.start(false /* useStrongBinding */, mServiceCallback); - mFirstServiceConnection.notifyServiceConnected(null /* iBinder */); - connection.stop(); - verify(mServiceCallback, times(1)).onChildStarted(); - verify(mServiceCallback, never()).onChildStartFailed(any()); - verify(mServiceCallback, times(1)).onChildProcessDied(connection); - } - - @Test - public void testServiceDisconnects() { - ChildProcessConnection connection = createDefaultTestConnection(); - assertNotNull(mFirstServiceConnection); - connection.start(false /* useStrongBinding */, mServiceCallback); - mFirstServiceConnection.notifyServiceConnected(null /* iBinder */); - mFirstServiceConnection.notifyServiceDisconnected(); - verify(mServiceCallback, times(1)).onChildStarted(); - verify(mServiceCallback, never()).onChildStartFailed(any()); - verify(mServiceCallback, times(1)).onChildProcessDied(connection); - } - - @Test - public void testNotBoundToCaller() throws RemoteException { - ChildProcessConnection connection = createTestConnection(false /* bindToCaller */, - false /* bindAsExternalService */, null /* serviceBundle */); - assertNotNull(mFirstServiceConnection); - connection.start(false /* useStrongBinding */, mServiceCallback); - mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder); - // Service is started and bindToCallback is not called. - verify(mServiceCallback, times(1)).onChildStarted(); - verify(mServiceCallback, never()).onChildStartFailed(any()); - verify(mServiceCallback, never()).onChildProcessDied(connection); - verify(mIChildProcessService, never()).bindToCaller(); - } - - @Test - public void testBoundToCallerSuccess() throws RemoteException { - ChildProcessConnection connection = createTestConnection(true /* bindToCaller */, - false /* bindAsExternalService */, null /* serviceBundle */); - assertNotNull(mFirstServiceConnection); - connection.start(false /* useStrongBinding */, mServiceCallback); - when(mIChildProcessService.bindToCaller()).thenReturn(true); - mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder); - // Service is started and bindToCallback is called. - verify(mServiceCallback, times(1)).onChildStarted(); - verify(mServiceCallback, never()).onChildStartFailed(any()); - verify(mServiceCallback, never()).onChildProcessDied(connection); - verify(mIChildProcessService, times(1)).bindToCaller(); - } - - @Test - public void testBoundToCallerFailure() throws RemoteException { - ChildProcessConnection connection = createTestConnection(true /* bindToCaller */, - false /* bindAsExternalService */, null /* serviceBundle */); - assertNotNull(mFirstServiceConnection); - connection.start(false /* useStrongBinding */, mServiceCallback); - // Pretend bindToCaller returns false, i.e. the service is already bound to a different - // service. - when(mIChildProcessService.bindToCaller()).thenReturn(false); - mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder); - // Service fails to start. - verify(mServiceCallback, never()).onChildStarted(); - verify(mServiceCallback, times(1)).onChildStartFailed(any()); - verify(mServiceCallback, never()).onChildProcessDied(connection); - verify(mIChildProcessService, times(1)).bindToCaller(); - } - - @Test - public void testSetupConnectionBeforeServiceConnected() throws RemoteException { - ChildProcessConnection connection = createDefaultTestConnection(); - assertNotNull(mFirstServiceConnection); - connection.start(false /* useStrongBinding */, null /* serviceCallback */); - connection.setupConnection( - null /* connectionBundle */, null /* callback */, mConnectionCallback); - verify(mConnectionCallback, never()).onConnected(any()); - mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder); - ShadowLooper.runUiThreadTasks(); - assertNotNull(mConnectionPidCallback); - mConnectionPidCallback.call(34 /* pid */); - verify(mConnectionCallback, times(1)).onConnected(connection); - } - - @Test - public void testSetupConnectionAfterServiceConnected() throws RemoteException { - ChildProcessConnection connection = createDefaultTestConnection(); - assertNotNull(mFirstServiceConnection); - connection.start(false /* useStrongBinding */, null /* serviceCallback */); - mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder); - connection.setupConnection( - null /* connectionBundle */, null /* callback */, mConnectionCallback); - verify(mConnectionCallback, never()).onConnected(any()); - ShadowLooper.runUiThreadTasks(); - assertNotNull(mConnectionPidCallback); - mConnectionPidCallback.call(34 /* pid */); - verify(mConnectionCallback, times(1)).onConnected(connection); - } - - @Test - public void testKill() throws RemoteException { - ChildProcessConnection connection = createDefaultTestConnection(); - assertNotNull(mFirstServiceConnection); - connection.start(false /* useStrongBinding */, null /* serviceCallback */); - mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder); - connection.setupConnection( - null /* connectionBundle */, null /* callback */, mConnectionCallback); - verify(mConnectionCallback, never()).onConnected(any()); - ShadowLooper.runUiThreadTasks(); - assertNotNull(mConnectionPidCallback); - mConnectionPidCallback.call(34 /* pid */); - verify(mConnectionCallback, times(1)).onConnected(connection); - - // Add strong binding so that connection is oom protected. - connection.removeModerateBinding(); - assertEquals(ChildBindingState.WAIVED, connection.bindingStateCurrentOrWhenDied()); - connection.addModerateBinding(); - assertEquals(ChildBindingState.MODERATE, connection.bindingStateCurrentOrWhenDied()); - connection.addStrongBinding(); - assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrentOrWhenDied()); - - // Kill and verify state. - connection.kill(); - verify(mIChildProcessService).forceKill(); - assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrentOrWhenDied()); - Assert.assertTrue(connection.isKilledByUs()); - } - - @Test - public void testBindingStateCounts() throws RemoteException { - ChildProcessConnection.resetBindingStateCountsForTesting(); - ChildProcessConnection connection0 = createDefaultTestConnection(); - ChildServiceConnectionMock connectionMock0 = mFirstServiceConnection; - mFirstServiceConnection = null; - ChildProcessConnection connection1 = createDefaultTestConnection(); - ChildServiceConnectionMock connectionMock1 = mFirstServiceConnection; - mFirstServiceConnection = null; - ChildProcessConnection connection2 = createDefaultTestConnection(); - ChildServiceConnectionMock connectionMock2 = mFirstServiceConnection; - mFirstServiceConnection = null; - - assertArrayEquals( - connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 0, 0}); - - connection0.start(false /* useStrongBinding */, null /* serviceCallback */); - assertArrayEquals( - connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 0}); - - connection1.start(true /* useStrongBinding */, null /* serviceCallback */); - assertArrayEquals( - connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 1}); - - connection2.start(false /* useStrongBinding */, null /* serviceCallback */); - assertArrayEquals( - connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 2, 1}); - - Binder binder0 = new Binder(); - Binder binder1 = new Binder(); - Binder binder2 = new Binder(); - binder0.attachInterface(mIChildProcessService, IChildProcessService.class.getName()); - binder1.attachInterface(mIChildProcessService, IChildProcessService.class.getName()); - binder2.attachInterface(mIChildProcessService, IChildProcessService.class.getName()); - connectionMock0.notifyServiceConnected(binder0); - connectionMock1.notifyServiceConnected(binder1); - connectionMock2.notifyServiceConnected(binder2); - ShadowLooper.runUiThreadTasks(); - - // Add and remove moderate binding works as expected. - connection2.removeModerateBinding(); - assertArrayEquals( - connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 1, 1, 1}); - connection2.addModerateBinding(); - assertArrayEquals( - connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 2, 1}); - - // Add and remove strong binding works as expected. - connection0.addStrongBinding(); - assertArrayEquals( - connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 2}); - connection0.removeStrongBinding(); - assertArrayEquals( - connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 2, 1}); - - // Stopped connection should no longe update. - connection0.stop(); - assertArrayEquals( - connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 1}); - assertArrayEquals( - connection1.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 1}); - - connection2.removeModerateBinding(); - assertArrayEquals( - connection0.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 0, 1, 1}); - assertArrayEquals( - connection1.bindingStateCountsCurrentOrWhenDied(), new int[] {0, 1, 0, 1}); - } -} diff --git a/android/library_loader/README.md b/android/library_loader/README.md deleted file mode 100644 index 7773b32b0..000000000 --- a/android/library_loader/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# //base/android/library_loader - -Native code is split between this directory and: - * [//third_party/android_crazy_linker](../../../third_party/android_crazy_linker/README.chromium) - -Java code lives at: - * [//base/android/java/src/org/chromium/base/library_loader/](../java/src/org/chromium/base/library_loader/) - -A high-level guide to native code on Android exists at: - * [//docs/android_native_libraries.md](../../../docs/android_native_libraries.md) diff --git a/android/library_loader/anchor_functions.cc b/android/library_loader/anchor_functions.cc deleted file mode 100644 index 0865d9d86..000000000 --- a/android/library_loader/anchor_functions.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2017 The Chromium 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/anchor_functions.h" - -#include "base/logging.h" -#include "build/build_config.h" - -#if BUILDFLAG(SUPPORTS_CODE_ORDERING) - -// These functions are here to delimit the start and end of the ordered part of -// .text. They require a suitably constructed orderfile, with these functions at -// the beginning and end. -// -// These functions are weird: this is due to ICF (Identical Code Folding). -// The linker merges functions that have the same code, which would be the case -// if these functions were empty, or simple. -// Gold's flag --icf=safe will *not* alias functions when their address is used -// in code, but as of November 2017, we use the default setting that -// deduplicates function in this case as well. -// -// Thus these functions are made to be unique, using inline .word in assembly. -// -// Note that code |CheckOrderingSanity()| below will make sure that these -// functions are not aliased, in case the toolchain becomes really clever. -extern "C" { - -// These functions have a well-defined ordering in this file, see the comment -// in |IsOrderingSane()|. -void dummy_function_end_of_ordered_text() { - asm(".word 0x21bad44d"); - asm(".word 0xb815c5b0"); -} - -void dummy_function_start_of_ordered_text() { - asm(".word 0xe4a07375"); - asm(".word 0x66dda6dc"); -} - -// These two symbols are defined by anchor_functions.lds and delimit the start -// and end of .text. -void linker_script_start_of_text(); -void linker_script_end_of_text(); - -} // extern "C" - -namespace base { -namespace android { - -const size_t kStartOfText = - reinterpret_cast(linker_script_start_of_text); -const size_t kEndOfText = reinterpret_cast(linker_script_end_of_text); -const size_t kStartOfOrderedText = - reinterpret_cast(dummy_function_start_of_ordered_text); -const size_t kEndOfOrderedText = - reinterpret_cast(dummy_function_end_of_ordered_text); - -bool IsOrderingSane() { - size_t here = reinterpret_cast(&IsOrderingSane); - // The symbols linker_script_start_of_text and linker_script_end_of_text - // should cover all of .text, and dummy_function_start_of_ordered_text and - // dummy_function_end_of_ordered_text should cover the ordered part of it. - // This check is intended to catch the lack of ordering. - // - // Ordered text can start at the start of text, but should not cover the - // entire range. Most addresses are distinct nonetheless as the symbols are - // different, but linker-defined symbols have zero size and therefore the - // start address could be the same as the address of - // dummy_function_start_of_ordered_text. - return kStartOfText < here && here < kEndOfText && - kStartOfOrderedText < kEndOfOrderedText && - kStartOfText <= kStartOfOrderedText && kEndOfOrderedText < kEndOfText; -} - -} // namespace android -} // namespace base - -#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING) diff --git a/android/library_loader/anchor_functions.h b/android/library_loader/anchor_functions.h deleted file mode 100644 index 9894583a5..000000000 --- a/android/library_loader/anchor_functions.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2017 The Chromium 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_ANCHOR_FUNCTIONS_H_ -#define BASE_ANDROID_LIBRARY_LOADER_ANCHOR_FUNCTIONS_H_ - -#include -#include "base/android/library_loader/anchor_functions_buildflags.h" - -#include "base/base_export.h" - -#if BUILDFLAG(SUPPORTS_CODE_ORDERING) - -namespace base { -namespace android { - -// Start and end of .text, respectively. -BASE_EXPORT extern const size_t kStartOfText; -BASE_EXPORT extern const size_t kEndOfText; -// Start and end of the ordered part of .text, respectively. -BASE_EXPORT extern const size_t kStartOfOrderedText; -BASE_EXPORT extern const size_t kEndOfOrderedText; - -// Returns true if the ordering looks sane. -BASE_EXPORT bool IsOrderingSane(); - -} // namespace android -} // namespace base -#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING) - -#endif // BASE_ANDROID_LIBRARY_LOADER_ANCHOR_FUNCTIONS_H_ diff --git a/android/library_loader/anchor_functions.lds b/android/library_loader/anchor_functions.lds deleted file mode 100644 index cdeaaa2a3..000000000 --- a/android/library_loader/anchor_functions.lds +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright 2018 The Chromium 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 symbols that point to the start and end of the .text section. -PROVIDE_HIDDEN(linker_script_start_of_text = ADDR(.text)); -PROVIDE_HIDDEN(linker_script_end_of_text = ADDR(.text) + SIZEOF(.text)); diff --git a/android/library_loader/library_load_from_apk_status_codes.h b/android/library_loader/library_load_from_apk_status_codes.h deleted file mode 100644 index 8910d4800..000000000 --- a/android/library_loader/library_load_from_apk_status_codes.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_ -#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_ - -namespace base { -namespace android { - -namespace { - -// This enum must be kept in sync with the LibraryLoadFromApkStatus enum in -// tools/metrics/histograms/histograms.xml. -// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.library_loader -enum LibraryLoadFromApkStatusCodes { - // The loader was unable to determine whether the functionality is supported. - LIBRARY_LOAD_FROM_APK_STATUS_CODES_UNKNOWN = 0, - - // The device does not support loading a library directly from the APK file - // (obsolete). - LIBRARY_LOAD_FROM_APK_STATUS_CODES_NOT_SUPPORTED_OBSOLETE = 1, - - // The device supports loading a library directly from the APK file. - // (obsolete). - LIBRARY_LOAD_FROM_APK_STATUS_CODES_SUPPORTED_OBSOLETE = 2, - - // The Chromium library was successfully loaded directly from the APK file. - LIBRARY_LOAD_FROM_APK_STATUS_CODES_SUCCESSFUL = 3, - - // The Chromium library was successfully loaded using the unpack library - // fallback because it was compressed or not page aligned in the APK file. - LIBRARY_LOAD_FROM_APK_STATUS_CODES_USED_UNPACK_LIBRARY_FALLBACK = 4, - - // The Chromium library was successfully loaded using the no map executable - // support fallback (obsolete). - LIBRARY_LOAD_FROM_APK_STATUS_CODES_USED_NO_MAP_EXEC_SUPPORT_FALLBACK_OBSOLETE - = 5, - - // End sentinel. - LIBRARY_LOAD_FROM_APK_STATUS_CODES_MAX = 6, -}; - -} // namespace - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOAD_FROM_APK_STATUS_CODES_H_ diff --git a/android/library_loader/library_loader_hooks.cc b/android/library_loader/library_loader_hooks.cc deleted file mode 100644 index 40bff5e33..000000000 --- a/android/library_loader/library_loader_hooks.cc +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/library_loader/library_loader_hooks.h" - -#include "base/android/jni_string.h" -#include "base/android/library_loader/anchor_functions_buildflags.h" -#include "base/android/library_loader/library_load_from_apk_status_codes.h" -#include "base/android/library_loader/library_prefetcher.h" -#include "base/android/orderfile/orderfile_buildflags.h" -#include "base/at_exit.h" -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" -#include "jni/LibraryLoader_jni.h" - -#if BUILDFLAG(ORDERFILE_INSTRUMENTATION) -#include "base/android/orderfile/orderfile_instrumentation.h" -#endif - -namespace base { -namespace android { - -namespace { - -base::AtExitManager* g_at_exit_manager = NULL; -const char* g_library_version_number = ""; -LibraryLoadedHook* g_registration_callback = NULL; -NativeInitializationHook* g_native_initialization_hook = NULL; - -enum RendererHistogramCode { - // Renderer load at fixed address success, fail, or not attempted. - // Renderers do not attempt to load at at fixed address if on a - // low-memory device on which browser load at fixed address has already - // failed. - LFA_SUCCESS = 0, - LFA_BACKOFF_USED = 1, - LFA_NOT_ATTEMPTED = 2, - - // End sentinel, also used as nothing-pending indicator. - MAX_RENDERER_HISTOGRAM_CODE = 3, - NO_PENDING_HISTOGRAM_CODE = MAX_RENDERER_HISTOGRAM_CODE -}; - -enum BrowserHistogramCode { - // Non-low-memory random address browser loads. - NORMAL_LRA_SUCCESS = 0, - - // Low-memory browser loads at fixed address, success or fail. - LOW_MEMORY_LFA_SUCCESS = 1, - LOW_MEMORY_LFA_BACKOFF_USED = 2, - - MAX_BROWSER_HISTOGRAM_CODE = 3, -}; - -RendererHistogramCode g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE; - -// Indicate whether g_library_preloader_renderer_histogram_code is valid -bool g_library_preloader_renderer_histogram_code_registered = false; - -// The return value of NativeLibraryPreloader.loadLibrary() in child processes, -// it is initialized to the invalid value which shouldn't showup in UMA report. -int g_library_preloader_renderer_histogram_code = -1; - -// The amount of time, in milliseconds, that it took to load the shared -// libraries in the renderer. Set in -// RegisterChromiumAndroidLinkerRendererHistogram. -long g_renderer_library_load_time_ms = 0; - -void RecordChromiumAndroidLinkerRendererHistogram() { - if (g_renderer_histogram_code == NO_PENDING_HISTOGRAM_CODE) - return; - // Record and release the pending histogram value. - UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.RendererStates", - g_renderer_histogram_code, - MAX_RENDERER_HISTOGRAM_CODE); - g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE; - - // Record how long it took to load the shared libraries. - UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.RendererLoadTime", - base::TimeDelta::FromMilliseconds(g_renderer_library_load_time_ms)); -} - -void RecordLibraryPreloaderRendereHistogram() { - if (g_library_preloader_renderer_histogram_code_registered) { - UmaHistogramSparse("Android.NativeLibraryPreloader.Result.Renderer", - g_library_preloader_renderer_histogram_code); - } -} - -#if BUILDFLAG(SUPPORTS_CODE_ORDERING) -bool ShouldDoOrderfileMemoryOptimization() { - return CommandLine::ForCurrentProcess()->HasSwitch( - switches::kOrderfileMemoryOptimization); -} -#endif - -} // namespace - -static void JNI_LibraryLoader_RegisterChromiumAndroidLinkerRendererHistogram( - JNIEnv* env, - const JavaParamRef& jcaller, - jboolean requested_shared_relro, - jboolean load_at_fixed_address_failed, - jlong library_load_time_ms) { - // Note a pending histogram value for later recording. - if (requested_shared_relro) { - g_renderer_histogram_code = load_at_fixed_address_failed - ? LFA_BACKOFF_USED : LFA_SUCCESS; - } else { - g_renderer_histogram_code = LFA_NOT_ATTEMPTED; - } - - g_renderer_library_load_time_ms = library_load_time_ms; -} - -static void JNI_LibraryLoader_RecordChromiumAndroidLinkerBrowserHistogram( - JNIEnv* env, - const JavaParamRef& jcaller, - jboolean is_using_browser_shared_relros, - jboolean load_at_fixed_address_failed, - jint library_load_from_apk_status, - jlong library_load_time_ms) { - // For low-memory devices, record whether or not we successfully loaded the - // browser at a fixed address. Otherwise just record a normal invocation. - BrowserHistogramCode histogram_code; - if (is_using_browser_shared_relros) { - histogram_code = load_at_fixed_address_failed - ? LOW_MEMORY_LFA_BACKOFF_USED : LOW_MEMORY_LFA_SUCCESS; - } else { - histogram_code = NORMAL_LRA_SUCCESS; - } - UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.BrowserStates", - histogram_code, - MAX_BROWSER_HISTOGRAM_CODE); - - // Record the device support for loading a library directly from the APK file. - UMA_HISTOGRAM_ENUMERATION( - "ChromiumAndroidLinker.LibraryLoadFromApkStatus", - static_cast(library_load_from_apk_status), - LIBRARY_LOAD_FROM_APK_STATUS_CODES_MAX); - - // Record how long it took to load the shared libraries. - UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.BrowserLoadTime", - base::TimeDelta::FromMilliseconds(library_load_time_ms)); -} - -static void JNI_LibraryLoader_RecordLibraryPreloaderBrowserHistogram( - JNIEnv* env, - const JavaParamRef& jcaller, - jint status) { - UmaHistogramSparse("Android.NativeLibraryPreloader.Result.Browser", status); -} - -static void JNI_LibraryLoader_RegisterLibraryPreloaderRendererHistogram( - JNIEnv* env, - const JavaParamRef& jcaller, - jint status) { - g_library_preloader_renderer_histogram_code = status; - g_library_preloader_renderer_histogram_code_registered = true; -} - -void SetNativeInitializationHook( - NativeInitializationHook native_initialization_hook) { - g_native_initialization_hook = native_initialization_hook; -} - -void RecordLibraryLoaderRendererHistograms() { - RecordChromiumAndroidLinkerRendererHistogram(); - RecordLibraryPreloaderRendereHistogram(); -} - -void SetLibraryLoadedHook(LibraryLoadedHook* func) { - g_registration_callback = func; -} - -static jboolean JNI_LibraryLoader_LibraryLoaded( - JNIEnv* env, - const JavaParamRef& jcaller, - jint library_process_type) { -#if BUILDFLAG(ORDERFILE_INSTRUMENTATION) - orderfile::StartDelayedDump(); -#endif - -#if BUILDFLAG(SUPPORTS_CODE_ORDERING) - if (ShouldDoOrderfileMemoryOptimization()) { - NativeLibraryPrefetcher::MadviseForOrderfile(); - } -#endif - - if (g_native_initialization_hook && - !g_native_initialization_hook( - static_cast(library_process_type))) - return false; - if (g_registration_callback && !g_registration_callback(env, nullptr)) - return false; - return true; -} - -void LibraryLoaderExitHook() { - if (g_at_exit_manager) { - delete g_at_exit_manager; - g_at_exit_manager = NULL; - } -} - -static void JNI_LibraryLoader_ForkAndPrefetchNativeLibrary( - JNIEnv* env, - const JavaParamRef& clazz) { -#if BUILDFLAG(SUPPORTS_CODE_ORDERING) - return NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary( - ShouldDoOrderfileMemoryOptimization()); -#endif -} - -static jint JNI_LibraryLoader_PercentageOfResidentNativeLibraryCode( - JNIEnv* env, - const JavaParamRef& clazz) { -#if BUILDFLAG(SUPPORTS_CODE_ORDERING) - return NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode(); -#else - return -1; -#endif -} - -static void JNI_LibraryLoader_PeriodicallyCollectResidency( - JNIEnv* env, - const JavaParamRef& clazz) { -#if BUILDFLAG(SUPPORTS_CODE_ORDERING) - NativeLibraryPrefetcher::PeriodicallyCollectResidency(); -#else - LOG(WARNING) << "Collecting residency is not supported."; -#endif -} - -void SetVersionNumber(const char* version_number) { - g_library_version_number = strdup(version_number); -} - -ScopedJavaLocalRef JNI_LibraryLoader_GetVersionNumber( - JNIEnv* env, - const JavaParamRef& jcaller) { - return ConvertUTF8ToJavaString(env, g_library_version_number); -} - -void InitAtExitManager() { - g_at_exit_manager = new base::AtExitManager(); -} - -} // namespace android -} // namespace base diff --git a/android/library_loader/library_loader_hooks.h b/android/library_loader/library_loader_hooks.h deleted file mode 100644 index 9dff26ac2..000000000 --- a/android/library_loader/library_loader_hooks.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_ -#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_ - -#include - -#include "base/base_export.h" -#include "base/callback.h" - -namespace base { -namespace android { - -// The process the shared library is loaded in. -// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.library_loader -enum LibraryProcessType { - // The LibraryLoad has not been initialized. - PROCESS_UNINITIALIZED = 0, - // Shared library is running in browser process. - PROCESS_BROWSER = 1, - // Shared library is running in child process. - PROCESS_CHILD = 2, - // Shared library is running in the app that uses webview. - PROCESS_WEBVIEW = 3, - // Shared library is running in child process as part of webview. - PROCESS_WEBVIEW_CHILD = 4, -}; - -typedef bool NativeInitializationHook(LibraryProcessType library_process_type); - -BASE_EXPORT void SetNativeInitializationHook( - NativeInitializationHook native_initialization_hook); - -// Record any pending renderer histogram value as histograms. Pending values -// are set by RegisterChromiumAndroidLinkerRendererHistogram and -// RegisterLibraryPreloaderRendererHistogram. -BASE_EXPORT void RecordLibraryLoaderRendererHistograms(); - -// 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); - -// 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(); - -// Initialize AtExitManager, this must be done at the begining of loading -// shared library. -void InitAtExitManager(); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_ diff --git a/android/library_loader/library_prefetcher.cc b/android/library_loader/library_prefetcher.cc deleted file mode 100644 index 2ccf6a080..000000000 --- a/android/library_loader/library_prefetcher.cc +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/library_loader/library_prefetcher.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "base/android/library_loader/anchor_functions.h" -#include "base/android/orderfile/orderfile_buildflags.h" -#include "base/bits.h" -#include "base/files/file.h" -#include "base/format_macros.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/metrics/histogram_macros.h" -#include "base/posix/eintr_wrapper.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "build/build_config.h" - -#if BUILDFLAG(ORDERFILE_INSTRUMENTATION) -#include "base/android/orderfile/orderfile_instrumentation.h" -#endif - -#if BUILDFLAG(SUPPORTS_CODE_ORDERING) - -namespace base { -namespace android { - -namespace { - -// Android defines the background priority to this value since at least 2009 -// (see Process.java). -constexpr int kBackgroundPriority = 10; -// Valid for all Android architectures. -constexpr size_t kPageSize = 4096; - -// Reads a byte per page between |start| and |end| to force it into the page -// cache. -// Heap allocations, syscalls and library functions are not allowed in this -// function. -// Returns true for success. -#if defined(ADDRESS_SANITIZER) -// Disable AddressSanitizer instrumentation for this function. It is touching -// memory that hasn't been allocated by the app, though the addresses are -// valid. Furthermore, this takes place in a child process. See crbug.com/653372 -// for the context. -__attribute__((no_sanitize_address)) -#endif -void Prefetch(size_t start, size_t end) { - unsigned char* start_ptr = reinterpret_cast(start); - unsigned char* end_ptr = reinterpret_cast(end); - unsigned char dummy = 0; - for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) { - // Volatile is required to prevent the compiler from eliminating this - // loop. - dummy ^= *static_cast(ptr); - } -} - -// Populates the per-page residency between |start| and |end| in |residency|. If -// successful, |residency| has the size of |end| - |start| in pages. -// Returns true for success. -bool Mincore(size_t start, size_t end, std::vector* residency) { - if (start % kPageSize || end % kPageSize) - return false; - size_t size = end - start; - size_t size_in_pages = size / kPageSize; - if (residency->size() != size_in_pages) - residency->resize(size_in_pages); - int err = HANDLE_EINTR( - mincore(reinterpret_cast(start), size, &(*residency)[0])); - PLOG_IF(ERROR, err) << "mincore() failed"; - return !err; -} - -// Returns the start and end of .text, aligned to the lower and upper page -// boundaries, respectively. -std::pair GetTextRange() { - // |kStartOfText| may not be at the beginning of a page, since .plt can be - // before it, yet in the same mapping for instance. - size_t start_page = kStartOfText - kStartOfText % kPageSize; - // Set the end to the page on which the beginning of the last symbol is. The - // actual symbol may spill into the next page by a few bytes, but this is - // outside of the executable code range anyway. - size_t end_page = base::bits::Align(kEndOfText, kPageSize); - return {start_page, end_page}; -} - -// Returns the start and end pages of the unordered section of .text, aligned to -// lower and upper page boundaries, respectively. -std::pair GetOrderedTextRange() { - size_t start_page = kStartOfOrderedText - kStartOfOrderedText % kPageSize; - // kEndOfUnorderedText is not considered ordered, but the byte immediately - // before is considered ordered and so can not be contained in the start page. - size_t end_page = base::bits::Align(kEndOfOrderedText, kPageSize); - return {start_page, end_page}; -} - -// Calls madvise(advice) on the specified range. Does nothing if the range is -// empty. -void MadviseOnRange(const std::pair& range, int advice) { - if (range.first >= range.second) { - return; - } - size_t size = range.second - range.first; - int err = madvise(reinterpret_cast(range.first), size, advice); - if (err) { - PLOG(ERROR) << "madvise() failed"; - } -} - -// Timestamp in ns since Unix Epoch, and residency, as returned by mincore(). -struct TimestampAndResidency { - uint64_t timestamp_nanos; - std::vector residency; - - TimestampAndResidency(uint64_t timestamp_nanos, - std::vector&& residency) - : timestamp_nanos(timestamp_nanos), residency(residency) {} -}; - -// Returns true for success. -bool CollectResidency(size_t start, - size_t end, - std::vector* data) { - // Not using base::TimeTicks() to not call too many base:: symbol that would - // pollute the reached symbols dumps. - struct timespec ts; - if (HANDLE_EINTR(clock_gettime(CLOCK_MONOTONIC, &ts))) { - PLOG(ERROR) << "Cannot get the time."; - return false; - } - uint64_t now = - static_cast(ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec; - std::vector residency; - if (!Mincore(start, end, &residency)) - return false; - - data->emplace_back(now, std::move(residency)); - return true; -} - -void DumpResidency(size_t start, - size_t end, - std::unique_ptr> data) { - auto path = base::FilePath( - base::StringPrintf("/data/local/tmp/chrome/residency-%d.txt", getpid())); - auto file = - base::File(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); - if (!file.IsValid()) { - PLOG(ERROR) << "Cannot open file to dump the residency data " - << path.value(); - return; - } - - // First line: start-end of text range. - CHECK(IsOrderingSane()); - CHECK_LT(start, kStartOfText); - CHECK_LT(kEndOfText, end); - auto start_end = base::StringPrintf("%" PRIuS " %" PRIuS "\n", - kStartOfText - start, kEndOfText - start); - file.WriteAtCurrentPos(start_end.c_str(), start_end.size()); - - for (const auto& data_point : *data) { - auto timestamp = - base::StringPrintf("%" PRIu64 " ", data_point.timestamp_nanos); - file.WriteAtCurrentPos(timestamp.c_str(), timestamp.size()); - - std::vector dump; - dump.reserve(data_point.residency.size() + 1); - for (auto c : data_point.residency) - dump.push_back(c ? '1' : '0'); - dump[dump.size() - 1] = '\n'; - file.WriteAtCurrentPos(&dump[0], dump.size()); - } -} - -// These values are persisted to logs. Entries should not be renumbered and -// numeric values should never be reused. -// Used for "LibraryLoader.PrefetchDetailedStatus". -enum class PrefetchStatus { - kSuccess = 0, - kWrongOrdering = 1, - kForkFailed = 2, - kChildProcessCrashed = 3, - kChildProcessKilled = 4, - kMaxValue = kChildProcessKilled -}; - -PrefetchStatus ForkAndPrefetch(bool ordered_only) { - if (!IsOrderingSane()) { - LOG(WARNING) << "Incorrect code ordering"; - return PrefetchStatus::kWrongOrdering; - } - - // Looking for ranges is done before the fork, to avoid syscalls and/or memory - // allocations in the forked process. The child process inherits the lock - // state of its parent thread. It cannot rely on being able to acquire any - // lock (unless special care is taken in a pre-fork handler), including being - // able to call malloc(). - // - // Always prefetch the ordered section first, as it's reached early during - // startup, and not necessarily located at the beginning of .text. - std::vector> ranges = {GetOrderedTextRange()}; - if (!ordered_only) - ranges.push_back(GetTextRange()); - - pid_t pid = fork(); - if (pid == 0) { - setpriority(PRIO_PROCESS, 0, kBackgroundPriority); - // _exit() doesn't call the atexit() handlers. - for (const auto& range : ranges) { - Prefetch(range.first, range.second); - } - _exit(EXIT_SUCCESS); - } else { - if (pid < 0) { - return PrefetchStatus::kForkFailed; - } - int status; - const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0)); - if (result == pid) { - if (WIFEXITED(status)) - return PrefetchStatus::kSuccess; - if (WIFSIGNALED(status)) { - int signal = WTERMSIG(status); - switch (signal) { - case SIGSEGV: - case SIGBUS: - return PrefetchStatus::kChildProcessCrashed; - break; - case SIGKILL: - case SIGTERM: - default: - return PrefetchStatus::kChildProcessKilled; - } - } - } - // Should not happen. Per man waitpid(2), errors are: - // - EINTR: handled. - // - ECHILD if the process doesn't have an unwaited-for child with this PID. - // - EINVAL. - return PrefetchStatus::kChildProcessKilled; - } -} - -} // namespace - -// static -void NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(bool ordered_only) { -#if BUILDFLAG(ORDERFILE_INSTRUMENTATION) - // Avoid forking with orderfile instrumentation because the child process - // would create a dump as well. - return; -#endif - - PrefetchStatus status = ForkAndPrefetch(ordered_only); - UMA_HISTOGRAM_BOOLEAN("LibraryLoader.PrefetchStatus", - status == PrefetchStatus::kSuccess); - UMA_HISTOGRAM_ENUMERATION("LibraryLoader.PrefetchDetailedStatus", status); - if (status != PrefetchStatus::kSuccess) { - LOG(WARNING) << "Cannot prefetch the library. status = " - << static_cast(status); - } -} - -// static -int NativeLibraryPrefetcher::PercentageOfResidentCode(size_t start, - size_t end) { - size_t total_pages = 0; - size_t resident_pages = 0; - - std::vector residency; - bool ok = Mincore(start, end, &residency); - if (!ok) - return -1; - total_pages += residency.size(); - resident_pages += std::count_if(residency.begin(), residency.end(), - [](unsigned char x) { return x & 1; }); - if (total_pages == 0) - return -1; - return static_cast((100 * resident_pages) / total_pages); -} - -// static -int NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode() { - if (!IsOrderingSane()) { - LOG(WARNING) << "Incorrect code ordering"; - return -1; - } - const auto& range = GetTextRange(); - return PercentageOfResidentCode(range.first, range.second); -} - -// static -void NativeLibraryPrefetcher::PeriodicallyCollectResidency() { - CHECK_EQ(static_cast(kPageSize), sysconf(_SC_PAGESIZE)); - - const auto& range = GetTextRange(); - auto data = std::make_unique>(); - for (int i = 0; i < 60; ++i) { - if (!CollectResidency(range.first, range.second, data.get())) - return; - usleep(2e5); - } - DumpResidency(range.first, range.second, std::move(data)); -} - -// static -void NativeLibraryPrefetcher::MadviseForOrderfile() { - CHECK(IsOrderingSane()); - LOG(WARNING) << "Performing experimental madvise from orderfile information"; - // First MADV_RANDOM on all of text, then turn the ordered text range back to - // normal. The ordered range may be placed anywhere within .text. - MadviseOnRange(GetTextRange(), MADV_RANDOM); - MadviseOnRange(GetOrderedTextRange(), MADV_NORMAL); -} - -} // namespace android -} // namespace base -#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING) diff --git a/android/library_loader/library_prefetcher.h b/android/library_loader/library_prefetcher.h deleted file mode 100644 index 29f294ef6..000000000 --- a/android/library_loader/library_prefetcher.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_ -#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_ - -#include - -#include - -#include "base/android/library_loader/anchor_functions_buildflags.h" -#include "base/base_export.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" - -#if BUILDFLAG(SUPPORTS_CODE_ORDERING) - -namespace base { -namespace android { - -// Forks and waits for a process prefetching the native library. This is done in -// a forked process for the following reasons: -// - Isolating the main process from mistakes in getting the address range, only -// crashing the forked process in case of mistake. -// - Not inflating the memory used by the main process uselessly, which could -// increase its likelihood to be killed. -// The forked process has background priority and, since it is not declared to -// the Android runtime, can be killed at any time, which is not an issue here. -class BASE_EXPORT NativeLibraryPrefetcher { - public: - // Finds the executable code range, forks a low priority process pre-fetching - // it wait()s for the process to exit or die. If ordered_only is true, only - // the ordered section is prefetched. See GetOrdrderedTextRange() in - // library_prefetcher.cc. - static void ForkAndPrefetchNativeLibrary(bool ordered_only); - - // Returns the percentage of the native library code currently resident in - // memory, or -1 in case of error. - static int PercentageOfResidentNativeLibraryCode(); - - // Collects residency for the native library executable multiple times, then - // dumps it to disk. - static void PeriodicallyCollectResidency(); - - // Calls madvise() on the native library executable, using orderfile - // information to decide how to advise each part of the library. - static void MadviseForOrderfile(); - - private: - // Returns the percentage of [start, end] currently resident in - // memory, or -1 in case of error. - static int PercentageOfResidentCode(size_t start, size_t end); - - FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest, - TestPercentageOfResidentCode); - - DISALLOW_IMPLICIT_CONSTRUCTORS(NativeLibraryPrefetcher); -}; - -} // namespace android -} // namespace base - -#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING) - -#endif // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_ diff --git a/android/library_loader/library_prefetcher_unittest.cc b/android/library_loader/library_prefetcher_unittest.cc deleted file mode 100644 index ebb0d48fa..000000000 --- a/android/library_loader/library_prefetcher_unittest.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/library_loader/library_prefetcher.h" - -#include -#include -#include -#include "base/android/library_loader/anchor_functions_buildflags.h" -#include "base/memory/shared_memory.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if BUILDFLAG(SUPPORTS_CODE_ORDERING) -namespace base { -namespace android { - -// Fails with ASAN, crbug.com/570423. -#if !defined(ADDRESS_SANITIZER) -namespace { -const size_t kPageSize = 4096; -} // namespace - -TEST(NativeLibraryPrefetcherTest, TestPercentageOfResidentCode) { - size_t length = 4 * kPageSize; - base::SharedMemory shared_mem; - ASSERT_TRUE(shared_mem.CreateAndMapAnonymous(length)); - void* address = shared_mem.memory(); - size_t start = reinterpret_cast(address); - size_t end = start + length; - - // Remove everything. - ASSERT_EQ(0, madvise(address, length, MADV_DONTNEED)); - EXPECT_EQ(0, NativeLibraryPrefetcher::PercentageOfResidentCode(start, end)); - - // Get everything back. - ASSERT_EQ(0, mlock(address, length)); - EXPECT_EQ(100, NativeLibraryPrefetcher::PercentageOfResidentCode(start, end)); - munlock(address, length); -} -#endif // !defined(ADDRESS_SANITIZER) - -} // namespace android -} // namespace base -#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING) diff --git a/android/linker/BUILD.gn b/android/linker/BUILD.gn deleted file mode 100644 index ee6f56915..000000000 --- a/android/linker/BUILD.gn +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//build/config/android/config.gni") - -assert(is_android) - -shared_library("chromium_android_linker") { - sources = [ - "linker_jni.cc", - ] - - # The NDK contains the crazy_linker here: - # '<(android_ndk_root)/crazy_linker.gyp:crazy_linker' - # However, we use our own fork. See bug 384700. - deps = [ - "//build:buildflag_header_h", - "//third_party/android_crazy_linker", - ] - - # Export JNI symbols. - configs -= [ "//build/config/android:hide_all_but_jni_onload" ] - configs += [ "//build/config/android:hide_all_but_jni" ] -} diff --git a/android/linker/DEPS b/android/linker/DEPS deleted file mode 100644 index 15c3afb86..000000000 --- a/android/linker/DEPS +++ /dev/null @@ -1,4 +0,0 @@ -include_rules = [ - # This code cannot depend on anything from base/ - "-base", -] diff --git a/android/linker/config.gni b/android/linker/config.gni deleted file mode 100644 index 27793ffe6..000000000 --- a/android/linker/config.gni +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//build/config/android/config.gni") -import("//build/config/compiler/compiler.gni") -import("//build/config/sanitizers/sanitizers.gni") - -# Chromium linker doesn't reliably support loading multiple libraries; -# disable for component builds, see crbug.com/657093. -# Chromium linker causes instrumentation to return incorrect results. -chromium_linker_supported = - !is_component_build && !enable_profiling && !use_order_profiling && !is_asan diff --git a/android/linker/linker_jni.cc b/android/linker/linker_jni.cc deleted file mode 100644 index ba632db68..000000000 --- a/android/linker/linker_jni.cc +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is the Android-specific Chromium linker, a tiny shared library -// implementing a custom dynamic linker that can be used to load the -// real Chromium libraries. - -// The main point of this linker is to be able to share the RELRO -// section of libchrome.so (or equivalent) between renderer processes. - -// This source code *cannot* depend on anything from base/ or the C++ -// STL, to keep the final library small, and avoid ugly dependency issues. - -#include -#include -#include -#include -#include - -#include "build/build_config.h" - -#include - -// Set this to 1 to enable debug traces to the Android log. -// Note that LOG() from "base/logging.h" cannot be used, since it is -// in base/ which hasn't been loaded yet. -#define DEBUG 0 - -#define TAG "cr_ChromiumAndroidLinker" - -#if DEBUG -#define LOG_INFO(FORMAT, ...) \ - __android_log_print(ANDROID_LOG_INFO, TAG, "%s: " FORMAT, __FUNCTION__, \ - ##__VA_ARGS__) -#else -#define LOG_INFO(FORMAT, ...) ((void)0) -#endif -#define LOG_ERROR(FORMAT, ...) \ - __android_log_print(ANDROID_LOG_ERROR, TAG, "%s: " FORMAT, __FUNCTION__, \ - ##__VA_ARGS__) - -#define UNUSED __attribute__((unused)) - -// See commentary in crazy_linker_elf_loader.cpp for the effect of setting -// this. If changing there, change here also. -// -// For more, see: -// https://crbug.com/504410 -#define RESERVE_BREAKPAD_GUARD_REGION 1 - -#if defined(ARCH_CPU_X86) -// Dalvik JIT generated code doesn't guarantee 16-byte stack alignment on -// x86 - use force_align_arg_pointer to realign the stack at the JNI -// boundary. https://crbug.com/655248 -#define JNI_GENERATOR_EXPORT \ - extern "C" __attribute__((visibility("default"), force_align_arg_pointer)) -#else -#define JNI_GENERATOR_EXPORT extern "C" __attribute__((visibility("default"))) -#endif - -namespace chromium_android_linker { - -namespace { - -// Larger than the largest library we might attempt to load. -constexpr size_t kAddressSpaceReservationSize = 192 * 1024 * 1024; - -// Size of any Breakpad guard region. 16MB is comfortably larger than the -// ~6MB relocation packing of the current 64-bit libchrome.so, the largest we -// expect to encounter. -#if RESERVE_BREAKPAD_GUARD_REGION -constexpr size_t kBreakpadGuardRegionBytes = 16 * 1024 * 1024; -#endif - -// A simple scoped UTF String class that can be initialized from -// a Java jstring handle. Modeled like std::string, which cannot -// be used here. -class String { - public: - String(JNIEnv* env, jstring str); - - inline ~String() { ::free(ptr_); } - - inline const char* c_str() const { return ptr_ ? ptr_ : ""; } - inline size_t size() const { return size_; } - - private: - char* ptr_; - size_t size_; -}; - -// Simple scoped UTF String class constructor. -String::String(JNIEnv* env, jstring str) { - size_ = env->GetStringUTFLength(str); - ptr_ = static_cast(::malloc(size_ + 1)); - - // Note: This runs before browser native code is loaded, and so cannot - // rely on anything from base/. This means that we must use - // GetStringUTFChars() and not base::android::ConvertJavaStringToUTF8(). - // - // GetStringUTFChars() suffices because the only strings used here are - // paths to APK files or names of shared libraries, all of which are - // plain ASCII, defined and hard-coded by the Chromium Android build. - // - // For more: see - // https://crbug.com/508876 - // - // Note: GetStringUTFChars() returns Java UTF-8 bytes. This is good - // enough for the linker though. - const char* bytes = env->GetStringUTFChars(str, nullptr); - ::memcpy(ptr_, bytes, size_); - ptr_[size_] = '\0'; - - env->ReleaseStringUTFChars(str, bytes); -} - -// Find the jclass JNI reference corresponding to a given |class_name|. -// |env| is the current JNI environment handle. -// On success, return true and set |*clazz|. -bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) { - *clazz = env->FindClass(class_name); - if (!*clazz) { - LOG_ERROR("Could not find class for %s", class_name); - return false; - } - return true; -} - -// Initialize a jfieldID corresponding to the field of a given |clazz|, -// with name |field_name| and signature |field_sig|. -// |env| is the current JNI environment handle. -// On success, return true and set |*field_id|. -bool InitFieldId(JNIEnv* env, - jclass clazz, - const char* field_name, - const char* field_sig, - jfieldID* field_id) { - *field_id = env->GetFieldID(clazz, field_name, field_sig); - if (!*field_id) { - LOG_ERROR("Could not find ID for field '%s'", field_name); - return false; - } - LOG_INFO("Found ID %p for field '%s'", *field_id, field_name); - return true; -} - -// Initialize a jmethodID corresponding to the static method of a given -// |clazz|, with name |method_name| and signature |method_sig|. -// |env| is the current JNI environment handle. -// On success, return true and set |*method_id|. -bool InitStaticMethodId(JNIEnv* env, - jclass clazz, - const char* method_name, - const char* method_sig, - jmethodID* method_id) { - *method_id = env->GetStaticMethodID(clazz, method_name, method_sig); - if (!*method_id) { - LOG_ERROR("Could not find ID for static method '%s'", method_name); - return false; - } - LOG_INFO("Found ID %p for static method '%s'", *method_id, method_name); - return true; -} - -// Initialize a jfieldID corresponding to the static field of a given |clazz|, -// with name |field_name| and signature |field_sig|. -// |env| is the current JNI environment handle. -// On success, return true and set |*field_id|. -bool InitStaticFieldId(JNIEnv* env, - jclass clazz, - const char* field_name, - const char* field_sig, - jfieldID* field_id) { - *field_id = env->GetStaticFieldID(clazz, field_name, field_sig); - if (!*field_id) { - LOG_ERROR("Could not find ID for static field '%s'", field_name); - return false; - } - LOG_INFO("Found ID %p for static field '%s'", *field_id, field_name); - return true; -} - -// Initialize a jint corresponding to the static integer field of a class -// with class name |class_name| and field name |field_name|. -// |env| is the current JNI environment handle. -// On success, return true and set |*value|. -bool InitStaticInt(JNIEnv* env, - const char* class_name, - const char* field_name, - jint* value) { - jclass clazz; - if (!InitClassReference(env, class_name, &clazz)) - return false; - - jfieldID field_id; - if (!InitStaticFieldId(env, clazz, field_name, "I", &field_id)) - return false; - - *value = env->GetStaticIntField(clazz, field_id); - LOG_INFO("Found value %d for class '%s', static field '%s'", - *value, class_name, field_name); - - return true; -} - -// A class used to model the field IDs of the org.chromium.base.Linker -// LibInfo inner class, used to communicate data with the Java side -// of the linker. -struct LibInfo_class { - jfieldID load_address_id; - jfieldID load_size_id; - jfieldID relro_start_id; - jfieldID relro_size_id; - jfieldID relro_fd_id; - - // Initialize an instance. - bool Init(JNIEnv* env) { - jclass clazz; - if (!InitClassReference( - env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) { - return false; - } - - return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) && - InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) && - InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) && - InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) && - InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id); - } - - void SetLoadInfo(JNIEnv* env, - jobject library_info_obj, - size_t load_address, - size_t load_size) { - env->SetLongField(library_info_obj, load_address_id, load_address); - env->SetLongField(library_info_obj, load_size_id, load_size); - } - - void SetRelroInfo(JNIEnv* env, - jobject library_info_obj, - size_t relro_start, - size_t relro_size, - int relro_fd) { - env->SetLongField(library_info_obj, relro_start_id, relro_start); - env->SetLongField(library_info_obj, relro_size_id, relro_size); - env->SetIntField(library_info_obj, relro_fd_id, relro_fd); - } - - // Use this instance to convert a RelroInfo reference into - // a crazy_library_info_t. - void GetRelroInfo(JNIEnv* env, - jobject library_info_obj, - size_t* relro_start, - size_t* relro_size, - int* relro_fd) { - if (relro_start) { - *relro_start = static_cast( - env->GetLongField(library_info_obj, relro_start_id)); - } - - if (relro_size) { - *relro_size = static_cast( - env->GetLongField(library_info_obj, relro_size_id)); - } - - if (relro_fd) { - *relro_fd = env->GetIntField(library_info_obj, relro_fd_id); - } - } -}; - -// Variable containing LibInfo for the loaded library. -LibInfo_class s_lib_info_fields; - -// Return true iff |address| is a valid address for the target CPU. -inline bool IsValidAddress(jlong address) { - return static_cast(static_cast(address)) == address; -} - -// The linker uses a single crazy_context_t object created on demand. -// There is no need to protect this against concurrent access, locking -// is already handled on the Java side. -crazy_context_t* GetCrazyContext() { - static crazy_context_t* s_crazy_context = nullptr; - - if (!s_crazy_context) { - // Create new context. - s_crazy_context = crazy_context_create(); - - // Ensure libraries located in the same directory as the linker - // can be loaded before system ones. - crazy_context_add_search_path_for_address( - s_crazy_context, reinterpret_cast(&GetCrazyContext)); - } - - return s_crazy_context; -} - -// A scoped crazy_library_t that automatically closes the handle -// on scope exit, unless Release() has been called. -class ScopedLibrary { - public: - ScopedLibrary() : lib_(nullptr) {} - - ~ScopedLibrary() { - if (lib_) - crazy_library_close_with_context(lib_, GetCrazyContext()); - } - - crazy_library_t* Get() { return lib_; } - - crazy_library_t** GetPtr() { return &lib_; } - - crazy_library_t* Release() { - crazy_library_t* ret = lib_; - lib_ = nullptr; - return ret; - } - - private: - crazy_library_t* lib_; -}; - -// Retrieve the SDK build version and pass it into the crazy linker. This -// needs to be done early in initialization, before any other crazy linker -// code is run. -// |env| is the current JNI environment handle. -// On success, return true. -bool InitSDKVersionInfo(JNIEnv* env) { - jint value = 0; - if (!InitStaticInt(env, "android/os/Build$VERSION", "SDK_INT", &value)) - return false; - - crazy_set_sdk_build_version(static_cast(value)); - LOG_INFO("Set SDK build version to %d", static_cast(value)); - - return true; -} - -} // namespace - -// Use Android ASLR to create a random address into which we expect to be -// able to load libraries. Note that this is probabilistic; we unmap the -// address we get from mmap and assume we can re-map into it later. This -// works the majority of the time. If it doesn't, client code backs out and -// then loads the library normally at any available address. -// |env| is the current JNI environment handle, and |clazz| a class. -// Returns the address selected by ASLR, or 0 on error. -JNI_GENERATOR_EXPORT jlong -Java_org_chromium_base_library_1loader_Linker_nativeGetRandomBaseLoadAddress( - JNIEnv* env, - jclass clazz) { - size_t bytes = kAddressSpaceReservationSize; - -#if RESERVE_BREAKPAD_GUARD_REGION - // Pad the requested address space size for a Breakpad guard region. - bytes += kBreakpadGuardRegionBytes; -#endif - - void* address = - mmap(nullptr, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (address == MAP_FAILED) { - LOG_INFO("Random base load address not determinable"); - return 0; - } - munmap(address, bytes); - -#if RESERVE_BREAKPAD_GUARD_REGION - // Allow for a Breakpad guard region ahead of the returned address. - address = reinterpret_cast( - reinterpret_cast(address) + kBreakpadGuardRegionBytes); -#endif - - LOG_INFO("Random base load address is %p", address); - return static_cast(reinterpret_cast(address)); -} - -// We identify the abi tag for which the linker is running. This allows -// us to select the library which matches the abi of the linker. - -#if defined(__arm__) && defined(__ARM_ARCH_7A__) -#define CURRENT_ABI "armeabi-v7a" -#elif defined(__arm__) -#define CURRENT_ABI "armeabi" -#elif defined(__i386__) -#define CURRENT_ABI "x86" -#elif defined(__mips__) -#define CURRENT_ABI "mips" -#elif defined(__x86_64__) -#define CURRENT_ABI "x86_64" -#elif defined(__aarch64__) -#define CURRENT_ABI "arm64-v8a" -#else -#error "Unsupported target abi" -#endif - -// Add a zip archive file path to the context's current search path -// list. Making it possible to load libraries directly from it. -JNI_GENERATOR_EXPORT bool -Java_org_chromium_base_library_1loader_Linker_nativeAddZipArchivePath( - JNIEnv* env, - jclass clazz, - jstring apk_path_obj) { - String apk_path(env, apk_path_obj); - - char search_path[512]; - snprintf(search_path, sizeof(search_path), "%s!lib/" CURRENT_ABI "/", - apk_path.c_str()); - - crazy_context_t* context = GetCrazyContext(); - crazy_context_add_search_path(context, search_path); - return true; -} - -// Load a library with the chromium linker. This will also call its -// JNI_OnLoad() method, which shall register its methods. Note that -// lazy native method resolution will _not_ work after this, because -// Dalvik uses the system's dlsym() which won't see the new library, -// so explicit registration is mandatory. -// -// |env| is the current JNI environment handle. -// |clazz| is the static class handle for org.chromium.base.Linker, -// and is ignored here. -// |library_name| is the library name (e.g. libfoo.so). -// |load_address| is an explicit load address. -// |lib_info_obj| is a LibInfo handle used to communicate information -// with the Java side. -// Return true on success. -JNI_GENERATOR_EXPORT bool -Java_org_chromium_base_library_1loader_Linker_nativeLoadLibrary( - JNIEnv* env, - jclass clazz, - jstring lib_name_obj, - jlong load_address, - jobject lib_info_obj) { - String library_name(env, lib_name_obj); - LOG_INFO("Called for %s, at address 0x%llx", library_name, load_address); - crazy_context_t* context = GetCrazyContext(); - - if (!IsValidAddress(load_address)) { - LOG_ERROR("Invalid address 0x%llx", - static_cast(load_address)); - return false; - } - - // Set the desired load address (0 means randomize it). - crazy_context_set_load_address(context, static_cast(load_address)); - - ScopedLibrary library; - if (!crazy_library_open(library.GetPtr(), library_name.c_str(), context)) { - return false; - } - - crazy_library_info_t info; - if (!crazy_library_get_info(library.Get(), context, &info)) { - LOG_ERROR("Could not get library information for %s: %s", - library_name.c_str(), crazy_context_get_error(context)); - return false; - } - - // Release library object to keep it alive after the function returns. - library.Release(); - - s_lib_info_fields.SetLoadInfo(env, lib_info_obj, info.load_address, - info.load_size); - LOG_INFO("Success loading library %s", library_name.c_str()); - return true; -} - -// Class holding the Java class and method ID for the Java side Linker -// postCallbackOnMainThread method. -struct JavaCallbackBindings_class { - jclass clazz; - jmethodID method_id; - - // Initialize an instance. - bool Init(JNIEnv* env, jclass linker_class) { - clazz = reinterpret_cast(env->NewGlobalRef(linker_class)); - return InitStaticMethodId(env, linker_class, "postCallbackOnMainThread", - "(J)V", &method_id); - } -}; - -static JavaCallbackBindings_class s_java_callback_bindings; - -// Designated receiver function for callbacks from Java. Its name is known -// to the Java side. -// |env| is the current JNI environment handle and is ignored here. -// |clazz| is the static class handle for org.chromium.base.Linker, -// and is ignored here. -// |arg| is a pointer to an allocated crazy_callback_t, deleted after use. -JNI_GENERATOR_EXPORT void -Java_org_chromium_base_library_1loader_Linker_nativeRunCallbackOnUiThread( - JNIEnv* env, - jclass clazz, - jlong arg) { - crazy_callback_t* callback = reinterpret_cast(arg); - - LOG_INFO("Called back from java with handler %p, opaque %p", - callback->handler, callback->opaque); - - crazy_callback_run(callback); - delete callback; -} - -// Request a callback from Java. The supplied crazy_callback_t is valid only -// for the duration of this call, so we copy it to a newly allocated -// crazy_callback_t and then call the Java side's postCallbackOnMainThread. -// This will call back to to our RunCallbackOnUiThread some time -// later on the UI thread. -// |callback_request| is a crazy_callback_t. -// |poster_opaque| is unused. -// Returns true if the callback request succeeds. -static bool PostForLaterExecution(crazy_callback_t* callback_request, - void* poster_opaque UNUSED) { - crazy_context_t* context = GetCrazyContext(); - - JavaVM* vm; - int minimum_jni_version; - crazy_context_get_java_vm(context, reinterpret_cast(&vm), - &minimum_jni_version); - - // Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own. - JNIEnv* env; - if (JNI_OK != - vm->GetEnv(reinterpret_cast(&env), minimum_jni_version)) { - LOG_ERROR("Could not create JNIEnv"); - return false; - } - - // Copy the callback; the one passed as an argument may be temporary. - crazy_callback_t* callback = new crazy_callback_t(); - *callback = *callback_request; - - LOG_INFO("Calling back to java with handler %p, opaque %p", callback->handler, - callback->opaque); - - jlong arg = static_cast(reinterpret_cast(callback)); - - env->CallStaticVoidMethod(s_java_callback_bindings.clazz, - s_java_callback_bindings.method_id, arg); - - // Back out and return false if we encounter a JNI exception. - if (env->ExceptionCheck() == JNI_TRUE) { - env->ExceptionDescribe(); - env->ExceptionClear(); - delete callback; - return false; - } - - return true; -} - -JNI_GENERATOR_EXPORT jboolean -Java_org_chromium_base_library_1loader_Linker_nativeCreateSharedRelro( - JNIEnv* env, - jclass clazz, - jstring library_name, - jlong load_address, - jobject lib_info_obj) { - String lib_name(env, library_name); - - LOG_INFO("Called for %s", lib_name.c_str()); - - if (!IsValidAddress(load_address)) { - LOG_ERROR("Invalid address 0x%llx", - static_cast(load_address)); - return false; - } - - ScopedLibrary library; - if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) { - LOG_ERROR("Could not find %s", lib_name.c_str()); - return false; - } - - crazy_context_t* context = GetCrazyContext(); - size_t relro_start = 0; - size_t relro_size = 0; - int relro_fd = -1; - - if (!crazy_library_create_shared_relro( - library.Get(), context, static_cast(load_address), - &relro_start, &relro_size, &relro_fd)) { - LOG_ERROR("Could not create shared RELRO sharing for %s: %s\n", - lib_name.c_str(), crazy_context_get_error(context)); - return false; - } - - s_lib_info_fields.SetRelroInfo(env, lib_info_obj, relro_start, relro_size, - relro_fd); - return true; -} - -JNI_GENERATOR_EXPORT jboolean -Java_org_chromium_base_library_1loader_Linker_nativeUseSharedRelro( - JNIEnv* env, - jclass clazz, - jstring library_name, - jobject lib_info_obj) { - String lib_name(env, library_name); - - LOG_INFO("Called for %s, lib_info_ref=%p", lib_name.c_str(), lib_info_obj); - - ScopedLibrary library; - if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) { - LOG_ERROR("Could not find %s", lib_name.c_str()); - return false; - } - - crazy_context_t* context = GetCrazyContext(); - size_t relro_start = 0; - size_t relro_size = 0; - int relro_fd = -1; - s_lib_info_fields.GetRelroInfo(env, lib_info_obj, &relro_start, &relro_size, - &relro_fd); - - LOG_INFO("library=%s relro start=%p size=%p fd=%d", lib_name.c_str(), - (void*)relro_start, (void*)relro_size, relro_fd); - - if (!crazy_library_use_shared_relro(library.Get(), context, relro_start, - relro_size, relro_fd)) { - LOG_ERROR("Could not use shared RELRO for %s: %s", lib_name.c_str(), - crazy_context_get_error(context)); - return false; - } - - LOG_INFO("Library %s using shared RELRO section!", lib_name.c_str()); - - return true; -} - -static bool LinkerJNIInit(JavaVM* vm, JNIEnv* env) { - LOG_INFO("Entering"); - - // Initialize SDK version info. - LOG_INFO("Retrieving SDK version info"); - if (!InitSDKVersionInfo(env)) - return false; - - // Find LibInfo field ids. - LOG_INFO("Caching field IDs"); - if (!s_lib_info_fields.Init(env)) { - return false; - } - - // Register native methods. - jclass linker_class; - if (!InitClassReference(env, "org/chromium/base/library_loader/Linker", - &linker_class)) - return false; - - // Resolve and save the Java side Linker callback class and method. - LOG_INFO("Resolving callback bindings"); - if (!s_java_callback_bindings.Init(env, linker_class)) { - return false; - } - - // Save JavaVM* handle into context. - crazy_context_t* context = GetCrazyContext(); - crazy_context_set_java_vm(context, vm, JNI_VERSION_1_4); - - // Register the function that the crazy linker can call to post code - // for later execution. - crazy_context_set_callback_poster(context, &PostForLaterExecution, nullptr); - - return true; -} - -// JNI_OnLoad() hook called when the linker library is loaded through -// the regular System.LoadLibrary) API. This shall save the Java VM -// handle and initialize LibInfo fields. -jint JNI_OnLoad(JavaVM* vm, void* reserved) { - LOG_INFO("Entering"); - // Get new JNIEnv - JNIEnv* env; - if (JNI_OK != vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_4)) { - LOG_ERROR("Could not create JNIEnv"); - return -1; - } - - // Initialize linker base and implementations. - if (!LinkerJNIInit(vm, env)) { - return -1; - } - - LOG_INFO("Done"); - return JNI_VERSION_1_4; -} - -} // namespace chromium_android_linker - -jint JNI_OnLoad(JavaVM* vm, void* reserved) { - return chromium_android_linker::JNI_OnLoad(vm, reserved); -} diff --git a/android/locale_utils.cc b/android/locale_utils.cc deleted file mode 100644 index b3a234636..000000000 --- a/android/locale_utils.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/locale_utils.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "jni/LocaleUtils_jni.h" - -namespace base { -namespace android { - -std::string GetDefaultCountryCode() { - JNIEnv* env = base::android::AttachCurrentThread(); - return ConvertJavaStringToUTF8(Java_LocaleUtils_getDefaultCountryCode(env)); -} - -std::string GetDefaultLocaleString() { - JNIEnv* env = base::android::AttachCurrentThread(); - ScopedJavaLocalRef locale = - Java_LocaleUtils_getDefaultLocaleString(env); - return ConvertJavaStringToUTF8(locale); -} - -} // namespace android -} // namespace base diff --git a/android/locale_utils.h b/android/locale_utils.h deleted file mode 100644 index be68890df..000000000 --- a/android/locale_utils.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_LOCALE_UTILS_H_ -#define BASE_ANDROID_LOCALE_UTILS_H_ - -#include - -#include - -#include "base/base_export.h" - -namespace base { -namespace android { - -BASE_EXPORT std::string GetDefaultCountryCode(); - -// Return the current default locale of the device as string. -BASE_EXPORT std::string GetDefaultLocaleString(); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_LOCALE_UTILS_H_ diff --git a/android/memory_pressure_listener_android.cc b/android/memory_pressure_listener_android.cc deleted file mode 100644 index cab66e1c6..000000000 --- a/android/memory_pressure_listener_android.cc +++ /dev/null @@ -1,30 +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/memory_pressure_listener_android.h" - -#include "base/memory/memory_pressure_listener.h" -#include "jni/MemoryPressureListener_jni.h" - -using base::android::JavaParamRef; - -// Defined and called by JNI. -static void JNI_MemoryPressureListener_OnMemoryPressure( - JNIEnv* env, - const JavaParamRef& clazz, - jint memory_pressure_level) { - base::MemoryPressureListener::NotifyMemoryPressure( - static_cast( - memory_pressure_level)); -} - -namespace base { -namespace android { - -void MemoryPressureListenerAndroid::Initialize(JNIEnv* env) { - Java_MemoryPressureListener_addNativeCallback(env); -} - -} // namespace android -} // namespace base diff --git a/android/memory_pressure_listener_android.h b/android/memory_pressure_listener_android.h deleted file mode 100644 index 9edfd421a..000000000 --- a/android/memory_pressure_listener_android.h +++ /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. - -#ifndef BASE_ANDROID_MEMORY_PRESSURE_LISTENER_ANDROID_H_ -#define BASE_ANDROID_MEMORY_PRESSURE_LISTENER_ANDROID_H_ - -#include "base/android/jni_android.h" -#include "base/macros.h" - -namespace base { -namespace android { - -// Implements the C++ counter part of MemoryPressureListener.java -class BASE_EXPORT MemoryPressureListenerAndroid { - public: - static void Initialize(JNIEnv* env); - - // Called by JNI. - static void OnMemoryPressure(int memory_pressure_type); - - private: - DISALLOW_COPY_AND_ASSIGN(MemoryPressureListenerAndroid); -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_MEMORY_PRESSURE_LISTENER_ANDROID_H_ diff --git a/android/orderfile/BUILD.gn b/android/orderfile/BUILD.gn deleted file mode 100644 index ff0bfff14..000000000 --- a/android/orderfile/BUILD.gn +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2018 The Chromium 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/config.gni") - -if (use_order_profiling && target_cpu == "arm") { - static_library("orderfile_instrumentation") { - sources = [ - "orderfile_instrumentation.cc", - "orderfile_instrumentation.h", - ] - deps = [ - "//base", - ] - } - - executable("orderfile_instrumentation_perftest") { - testonly = true - - sources = [ - "orderfile_instrumentation_perftest.cc", - ] - - deps = [ - ":orderfile_instrumentation", - "//base", - "//testing/gtest", - "//testing/perf", - ] - - configs -= [ "//build/config/android:default_orderfile_instrumentation" ] - } -} diff --git a/android/orderfile/OWNERS b/android/orderfile/OWNERS deleted file mode 100644 index d45b803ab..000000000 --- a/android/orderfile/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -lizeb@chromium.org -mattcary@chromium.org -pasko@chromium.org diff --git a/android/orderfile/orderfile_instrumentation.cc b/android/orderfile/orderfile_instrumentation.cc deleted file mode 100644 index f06cc20be..000000000 --- a/android/orderfile/orderfile_instrumentation.cc +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright (c) 2017 The Chromium 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/orderfile/orderfile_instrumentation.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "base/android/library_loader/anchor_functions.h" -#include "base/android/orderfile/orderfile_buildflags.h" -#include "base/files/file.h" -#include "base/format_macros.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/strings/stringprintf.h" -#include "build/build_config.h" - -#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) -#include - -#include "base/command_line.h" -#include "base/time/time.h" -#include "base/trace_event/memory_dump_manager.h" -#include "base/trace_event/memory_dump_provider.h" -#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) - -#if !defined(ARCH_CPU_ARMEL) -#error Only supported on ARM. -#endif // !defined(ARCH_CPU_ARMEL) - -// Must be applied to all functions within this file. -#define NO_INSTRUMENT_FUNCTION __attribute__((no_instrument_function)) - -namespace base { -namespace android { -namespace orderfile { - -namespace { -// Constants used for StartDelayedDump(). -constexpr int kDelayInSeconds = 30; -constexpr int kInitialDelayInSeconds = kPhases == 1 ? kDelayInSeconds : 5; - -#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) -// This is defined in content/public/common/content_switches.h, which is not -// accessible in ::base. -constexpr const char kProcessTypeSwitch[] = "type"; -#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) - -// These are large overestimates, which is not an issue, as the data is -// allocated in .bss, and on linux doesn't take any actual memory when it's not -// touched. -constexpr size_t kBitfieldSize = 1 << 22; -constexpr size_t kMaxTextSizeInBytes = kBitfieldSize * (4 * 32); -constexpr size_t kMaxElements = 1 << 20; - -// Data required to log reached offsets. -struct LogData { - std::atomic offsets[kBitfieldSize]; - std::atomic ordered_offsets[kMaxElements]; - std::atomic index; -}; - -LogData g_data[kPhases]; -std::atomic g_data_index; - -#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) -// Dump offsets when a memory dump is requested. Used only if -// switches::kDevtoolsInstrumentationDumping is set. -class OrderfileMemoryDumpHook : public base::trace_event::MemoryDumpProvider { - NO_INSTRUMENT_FUNCTION bool OnMemoryDump( - const base::trace_event::MemoryDumpArgs& args, - base::trace_event::ProcessMemoryDump* pmd) override { - // Disable instrumentation now to cut down on orderfile pollution. - if (!Disable()) { - return true; // A dump has already been started. - } - std::stringstream process_type_str; - Dump(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - kProcessTypeSwitch)); - return true; // If something goes awry, a fatal error will be created - // internally. - } -}; -#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) - -// |RecordAddress()| adds an element to a concurrent bitset and to a concurrent -// append-only list of offsets. -// -// Ordering: -// Two consecutive calls to |RecordAddress()| from the same thread will be -// ordered in the same way in the result, as written by -// |StopAndDumpToFile()|. The result will contain exactly one instance of each -// unique offset relative to |kStartOfText| passed to |RecordAddress()|. -// -// Implementation: -// The "set" part is implemented with a bitfield, |g_offset|. The insertion -// order is recorded in |g_ordered_offsets|. -// This is not a class to make sure there isn't a static constructor, as it -// would cause issue with an instrumented static constructor calling this code. -// -// Limitations: -// - Only records offsets to addresses between |kStartOfText| and |kEndOfText|. -// - Capacity of the set is limited by |kMaxElements|. -// - Some insertions at the end of collection may be lost. - -// Records that |address| has been reached, if recording is enabled. -// To avoid infinite recursion, this *must* *never* call any instrumented -// function, unless |Disable()| is called first. -template -__attribute__((always_inline, no_instrument_function)) void RecordAddress( - size_t address) { - int index = g_data_index.load(std::memory_order_relaxed); - if (index >= kPhases) - return; - - const size_t start = - for_testing ? kStartOfTextForTesting : base::android::kStartOfText; - const size_t end = - for_testing ? kEndOfTextForTesting : base::android::kEndOfText; - if (UNLIKELY(address < start || address > end)) { - Disable(); - // If the start and end addresses are set incorrectly, this code path is - // likely happening during a static initializer. Logging at this time is - // prone to deadlock. By crashing immediately we at least have a chance to - // get a stack trace from the system to give some clue about the nature of - // the problem. - IMMEDIATE_CRASH(); - } - - size_t offset = address - start; - static_assert(sizeof(int) == 4, - "Collection and processing code assumes that sizeof(int) == 4"); - size_t offset_index = offset / 4; - - auto* offsets = g_data[index].offsets; - // Atomically set the corresponding bit in the array. - std::atomic* element = offsets + (offset_index / 32); - // First, a racy check. This saves a CAS if the bit is already set, and - // allows the cache line to remain shared acoss CPUs in this case. - uint32_t value = element->load(std::memory_order_relaxed); - uint32_t mask = 1 << (offset_index % 32); - if (value & mask) - return; - - auto before = element->fetch_or(mask, std::memory_order_relaxed); - if (before & mask) - return; - - // We were the first one to set the element, record it in the ordered - // elements list. - // Use relaxed ordering, as the value is not published, or used for - // synchronization. - auto* ordered_offsets = g_data[index].ordered_offsets; - auto& ordered_offsets_index = g_data[index].index; - size_t insertion_index = - ordered_offsets_index.fetch_add(1, std::memory_order_relaxed); - if (UNLIKELY(insertion_index >= kMaxElements)) { - Disable(); - LOG(FATAL) << "Too many reached offsets"; - } - ordered_offsets[insertion_index].store(offset, std::memory_order_relaxed); -} - -NO_INSTRUMENT_FUNCTION bool DumpToFile(const base::FilePath& path, - const LogData& data) { - auto file = - base::File(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); - if (!file.IsValid()) { - PLOG(ERROR) << "Could not open " << path; - return false; - } - - if (data.index == 0) { - LOG(ERROR) << "No entries to dump"; - return false; - } - - size_t count = data.index - 1; - for (size_t i = 0; i < count; i++) { - // |g_ordered_offsets| is initialized to 0, so a 0 in the middle of it - // indicates a case where the index was incremented, but the write is not - // visible in this thread yet. Safe to skip, also because the function at - // the start of text is never called. - auto offset = data.ordered_offsets[i].load(std::memory_order_relaxed); - if (!offset) - continue; - auto offset_str = base::StringPrintf("%" PRIuS "\n", offset); - if (file.WriteAtCurrentPos(offset_str.c_str(), - static_cast(offset_str.size())) < 0) { - // If the file could be opened, but writing has failed, it's likely that - // data was partially written. Producing incomplete profiling data would - // lead to a poorly performing orderfile, but might not be otherwised - // noticed. So we crash instead. - LOG(FATAL) << "Error writing profile data"; - } - } - return true; -} - -// Stops recording, and outputs the data to |path|. -NO_INSTRUMENT_FUNCTION void StopAndDumpToFile(int pid, - uint64_t start_ns_since_epoch, - const std::string& tag) { - Disable(); - - for (int phase = 0; phase < kPhases; phase++) { - std::string tag_str; - if (!tag.empty()) - tag_str = base::StringPrintf("%s-", tag.c_str()); - auto path = base::StringPrintf( - "/data/local/tmp/chrome/orderfile/profile-hitmap-%s%d-%" PRIu64 - ".txt_%d", - tag_str.c_str(), pid, start_ns_since_epoch, phase); - if (!DumpToFile(base::FilePath(path), g_data[phase])) { - LOG(ERROR) << "Problem with dump " << phase << " (" << tag << ")"; - } - } -} - -} // namespace - -NO_INSTRUMENT_FUNCTION bool Disable() { - auto old_phase = g_data_index.exchange(kPhases, std::memory_order_relaxed); - std::atomic_thread_fence(std::memory_order_seq_cst); - return old_phase != kPhases; -} - -NO_INSTRUMENT_FUNCTION void SanityChecks() { - CHECK_LT(base::android::kEndOfText - base::android::kStartOfText, - kMaxTextSizeInBytes); - CHECK(base::android::IsOrderingSane()); -} - -NO_INSTRUMENT_FUNCTION bool SwitchToNextPhaseOrDump( - int pid, - uint64_t start_ns_since_epoch) { - int before = g_data_index.fetch_add(1, std::memory_order_relaxed); - if (before + 1 == kPhases) { - StopAndDumpToFile(pid, start_ns_since_epoch, ""); - return true; - } - return false; -} - -NO_INSTRUMENT_FUNCTION void StartDelayedDump() { - // Using std::thread and not using base::TimeTicks() in order to to not call - // too many base:: symbols that would pollute the reached symbol dumps. - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts)) - PLOG(FATAL) << "clock_gettime."; - uint64_t start_ns_since_epoch = - static_cast(ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec; - int pid = getpid(); - -#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) - static auto* g_orderfile_memory_dump_hook = new OrderfileMemoryDumpHook(); - base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( - g_orderfile_memory_dump_hook, "Orderfile", nullptr); -#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) - - std::thread([pid, start_ns_since_epoch]() { - sleep(kInitialDelayInSeconds); -#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) - SwitchToNextPhaseOrDump(pid, start_ns_since_epoch); -// Return, letting devtools tracing handle any post-startup phases. -#else - while (!SwitchToNextPhaseOrDump(pid, start_ns_since_epoch)) - sleep(kDelayInSeconds); -#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) - }) - .detach(); -} - -NO_INSTRUMENT_FUNCTION void Dump(const std::string& tag) { - // As profiling has been disabled, none of the uses of ::base symbols below - // will enter the symbol dump. - StopAndDumpToFile( - getpid(), (base::Time::Now() - base::Time::UnixEpoch()).InNanoseconds(), - tag); -} - -NO_INSTRUMENT_FUNCTION void ResetForTesting() { - Disable(); - g_data_index = 0; - for (int i = 0; i < kPhases; i++) { - memset(reinterpret_cast(g_data[i].offsets), 0, - sizeof(uint32_t) * kBitfieldSize); - memset(reinterpret_cast(g_data[i].ordered_offsets), 0, - sizeof(uint32_t) * kMaxElements); - g_data[i].index.store(0); - } -} - -NO_INSTRUMENT_FUNCTION void RecordAddressForTesting(size_t address) { - return RecordAddress(address); -} - -NO_INSTRUMENT_FUNCTION std::vector GetOrderedOffsetsForTesting() { - std::vector result; - size_t max_index = g_data[0].index.load(std::memory_order_relaxed); - for (size_t i = 0; i < max_index; ++i) { - auto value = g_data[0].ordered_offsets[i].load(std::memory_order_relaxed); - if (value) - result.push_back(value); - } - return result; -} - -} // namespace orderfile -} // namespace android -} // namespace base - -extern "C" { - -NO_INSTRUMENT_FUNCTION void __cyg_profile_func_enter_bare() { - base::android::orderfile::RecordAddress( - reinterpret_cast(__builtin_return_address(0))); -} - -} // extern "C" diff --git a/android/orderfile/orderfile_instrumentation.h b/android/orderfile/orderfile_instrumentation.h deleted file mode 100644 index 8db194303..000000000 --- a/android/orderfile/orderfile_instrumentation.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2017 The Chromium 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_ORDERFILE_ORDERFILE_INSTRUMENTATION_H_ -#define BASE_ANDROID_ORDERFILE_ORDERFILE_INSTRUMENTATION_H_ - -#include -#include - -#include "base/android/orderfile/orderfile_buildflags.h" - -namespace base { -namespace android { -namespace orderfile { -#if BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) -constexpr int kPhases = 2; -#else -constexpr int kPhases = 1; -#endif // BUILDFLAG(DEVTOOLS_INSTRUMENTATION_DUMPING) - -constexpr size_t kStartOfTextForTesting = 1000; -constexpr size_t kEndOfTextForTesting = kStartOfTextForTesting + 1000 * 1000; - -// Stop recording. Returns false if recording was already disabled. -bool Disable(); - -// CHECK()s that the offsets are correctly set up. -void SanityChecks(); - -// Switches to the next recording phase. If called from the last phase, dumps -// the data to disk, and returns |true|. |pid| is the current process pid, and -// |start_ns_since_epoch| the process start timestamp. -bool SwitchToNextPhaseOrDump(int pid, uint64_t start_ns_since_epoch); - -// Starts a thread to dump instrumentation after a delay. -void StartDelayedDump(); - -// Dumps all information for the current process, annotating the dump file name -// with the given tag. Will disable instrumentation. Instrumentation must be -// disabled before this is called. -void Dump(const std::string& tag); - -// Record an |address|, if recording is enabled. Only for testing. -void RecordAddressForTesting(size_t address); - -// Resets the state. Only for testing. -void ResetForTesting(); - -// Returns an ordered list of reached offsets. Only for testing. -std::vector GetOrderedOffsetsForTesting(); -} // namespace orderfile -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_ORDERFILE_ORDERFILE_INSTRUMENTATION_H_ diff --git a/android/orderfile/orderfile_instrumentation_perftest.cc b/android/orderfile/orderfile_instrumentation_perftest.cc deleted file mode 100644 index e1a69c9b7..000000000 --- a/android/orderfile/orderfile_instrumentation_perftest.cc +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2017 The Chromium 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/orderfile/orderfile_instrumentation.h" - -#include - -#include "base/android/library_loader/anchor_functions.h" -#include "base/strings/stringprintf.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_test.h" - -namespace base { -namespace android { -namespace orderfile { - -namespace { - -// Records |addresses_count| distinct addresses |iterations| times, in -// |threads|. -void RunBenchmark(int iterations, int addresses_count, int threads) { - ResetForTesting(); - auto iterate = [iterations, addresses_count]() { - for (int i = 0; i < iterations; i++) { - for (size_t addr = kStartOfTextForTesting; - addr < static_cast(addresses_count); addr += sizeof(int)) { - RecordAddressForTesting(addr); - } - } - }; - if (threads != 1) { - for (int i = 0; i < threads - 1; ++i) - std::thread(iterate).detach(); - } - auto tick = base::TimeTicks::Now(); - iterate(); - auto tock = base::TimeTicks::Now(); - double nanos = static_cast((tock - tick).InNanoseconds()); - auto ns_per_call = - nanos / (iterations * static_cast(addresses_count)); - auto modifier = - base::StringPrintf("_%d_%d_%d", iterations, addresses_count, threads); - perf_test::PrintResult("RecordAddressCostPerCall", modifier, "", ns_per_call, - "ns", true); -} - -} // namespace - -class OrderfileInstrumentationTest : public ::testing::Test { - // Any tests need to run ResetForTesting() when they start. Because this - // perftest is built with instrumentation enabled, all code including - // ::testing::Test is instrumented. If ResetForTesting() is called earlier, - // for example in setUp(), any test harness code between setUp() and the - // actual test will change the instrumentation offset record in unpredictable - // ways and make these tests unreliable. -}; - -TEST_F(OrderfileInstrumentationTest, RecordOffset) { - ResetForTesting(); - size_t first = 1234, second = 1456; - RecordAddressForTesting(first); - RecordAddressForTesting(second); - RecordAddressForTesting(first); // No duplicates. - RecordAddressForTesting(first + 1); // 4 bytes granularity. - Disable(); - - auto reached = GetOrderedOffsetsForTesting(); - EXPECT_EQ(2UL, reached.size()); - EXPECT_EQ(first - kStartOfTextForTesting, reached[0]); - EXPECT_EQ(second - kStartOfTextForTesting, reached[1]); -} - -TEST_F(OrderfileInstrumentationTest, RecordingStops) { - ResetForTesting(); - size_t first = 1234, second = 1456, third = 1789; - RecordAddressForTesting(first); - RecordAddressForTesting(second); - Disable(); - RecordAddressForTesting(third); - - auto reached = GetOrderedOffsetsForTesting(); - ASSERT_EQ(2UL, reached.size()); - ASSERT_EQ(first - kStartOfTextForTesting, reached[0]); - ASSERT_EQ(second - kStartOfTextForTesting, reached[1]); -} - -TEST_F(OrderfileInstrumentationTest, OutOfBounds) { - ResetForTesting(); - EXPECT_DEATH(RecordAddressForTesting(kEndOfTextForTesting + 100), ""); - EXPECT_DEATH(RecordAddressForTesting(kStartOfTextForTesting - 100), ""); -} - -TEST(OrderfileInstrumentationPerfTest, RecordAddress_10_10000) { - RunBenchmark(10, 10000, 1); -} - -TEST(OrderfileInstrumentationPerfTest, RecordAddress_100_10000) { - RunBenchmark(100, 10000, 1); -} - -TEST(OrderfileInstrumentationPerfTest, RecordAddress_10_100000) { - RunBenchmark(10, 100000, 1); -} - -TEST(OrderfileInstrumentationPerfTest, RecordAddress_100_100000) { - RunBenchmark(100, 100000, 1); -} - -TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_100000_2) { - RunBenchmark(1000, 100000, 2); -} - -TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_100000_3) { - RunBenchmark(1000, 100000, 3); -} - -TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_100000_4) { - RunBenchmark(1000, 100000, 4); -} - -TEST(OrderfileInstrumentationPerfTest, RecordAddress_1000_100000_6) { - RunBenchmark(1000, 100000, 6); -} - -} // namespace orderfile -} // namespace android -} // namespace base - -// Custom runner implementation since base's one requires JNI on Android. -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/android/path_service_android.cc b/android/path_service_android.cc deleted file mode 100644 index 51be530ea..000000000 --- a/android/path_service_android.cc +++ /dev/null @@ -1,23 +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/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/files/file_path.h" -#include "base/path_service.h" -#include "jni/PathService_jni.h" - -namespace base { -namespace android { - -void JNI_PathService_Override(JNIEnv* env, - const JavaParamRef& clazz, - jint what, - const JavaParamRef& path) { - FilePath file_path(ConvertJavaStringToUTF8(env, path)); - PathService::Override(what, file_path); -} - -} // namespace android -} // namespace base diff --git a/android/path_utils.cc b/android/path_utils.cc deleted file mode 100644 index d1f6d43e9..000000000 --- a/android/path_utils.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/android/path_utils.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_array.h" -#include "base/android/jni_string.h" -#include "base/android/scoped_java_ref.h" -#include "base/files/file_path.h" - -#include "jni/PathUtils_jni.h" - -namespace base { -namespace android { - -bool GetDataDirectory(FilePath* result) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef path = Java_PathUtils_getDataDirectory(env); - FilePath data_path(ConvertJavaStringToUTF8(path)); - *result = data_path; - return true; -} - -bool GetCacheDirectory(FilePath* result) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef path = Java_PathUtils_getCacheDirectory(env); - FilePath cache_path(ConvertJavaStringToUTF8(path)); - *result = cache_path; - return true; -} - -bool GetThumbnailCacheDirectory(FilePath* result) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef path = - Java_PathUtils_getThumbnailCacheDirectory(env); - FilePath thumbnail_cache_path(ConvertJavaStringToUTF8(path)); - *result = thumbnail_cache_path; - return true; -} - -bool GetDownloadsDirectory(FilePath* result) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef path = Java_PathUtils_getDownloadsDirectory(env); - FilePath downloads_path(ConvertJavaStringToUTF8(path)); - *result = downloads_path; - return true; -} - -std::vector GetAllPrivateDownloadsDirectories() { - std::vector dirs; - JNIEnv* env = AttachCurrentThread(); - auto jarray = Java_PathUtils_getAllPrivateDownloadsDirectories(env); - base::android::AppendJavaStringArrayToStringVector(env, jarray.obj(), &dirs); - - std::vector file_paths; - for (const auto& dir : dirs) - file_paths.emplace_back(dir); - return file_paths; -} - -bool GetNativeLibraryDirectory(FilePath* result) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef path = - Java_PathUtils_getNativeLibraryDirectory(env); - FilePath library_path(ConvertJavaStringToUTF8(path)); - *result = library_path; - return true; -} - -bool GetExternalStorageDirectory(FilePath* result) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef path = - Java_PathUtils_getExternalStorageDirectory(env); - FilePath storage_path(ConvertJavaStringToUTF8(path)); - *result = storage_path; - return true; -} - -} // namespace android -} // namespace base diff --git a/android/path_utils.h b/android/path_utils.h deleted file mode 100644 index 650999d49..000000000 --- a/android/path_utils.h +++ /dev/null @@ -1,55 +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_ANDROID_PATH_UTILS_H_ -#define BASE_ANDROID_PATH_UTILS_H_ - -#include -#include - -#include "base/base_export.h" - -namespace base { - -class FilePath; - -namespace android { - -// Retrieves the absolute path to the data directory of the current -// application. The result is placed in the FilePath pointed to by 'result'. -// This method is dedicated for base_paths_android.c, Using -// PathService::Get(base::DIR_ANDROID_APP_DATA, ...) gets the data dir. -BASE_EXPORT bool GetDataDirectory(FilePath* result); - -// Retrieves the absolute path to the cache directory. The result is placed in -// the FilePath pointed to by 'result'. This method is dedicated for -// base_paths_android.c, Using PathService::Get(base::DIR_CACHE, ...) gets the -// cache dir. -BASE_EXPORT bool GetCacheDirectory(FilePath* result); - -// Retrieves the path to the thumbnail cache directory. The result is placed -// in the FilePath pointed to by 'result'. -BASE_EXPORT bool GetThumbnailCacheDirectory(FilePath* result); - -// Retrieves the path to the public downloads directory. The result is placed -// in the FilePath pointed to by 'result'. -BASE_EXPORT bool GetDownloadsDirectory(FilePath* result); - -// Retrieves the paths to all download directories, including default storage -// directory, and a private directory on external SD card. -BASE_EXPORT std::vector GetAllPrivateDownloadsDirectories(); - -// Retrieves the path to the native JNI libraries via -// ApplicationInfo.nativeLibraryDir on the Java side. The result is placed in -// the FilePath pointed to by 'result'. -BASE_EXPORT bool GetNativeLibraryDirectory(FilePath* result); - -// Retrieves the absolute path to the external storage directory. The result -// is placed in the FilePath pointed to by 'result'. -BASE_EXPORT bool GetExternalStorageDirectory(FilePath* result); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_PATH_UTILS_H_ diff --git a/android/path_utils_unittest.cc b/android/path_utils_unittest.cc deleted file mode 100644 index dca8ca13a..000000000 --- a/android/path_utils_unittest.cc +++ /dev/null @@ -1,65 +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/android/path_utils.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/strings/string_util.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace android { - -typedef testing::Test PathUtilsTest; - -namespace { -void ExpectEither(const std::string& expected1, - const std::string& expected2, - const std::string& actual) { - EXPECT_TRUE(expected1 == actual || expected2 == actual) - << "Value of: " << actual << std::endl - << "Expected either: " << expected1 << std::endl - << "or: " << expected2; -} -} // namespace - -TEST_F(PathUtilsTest, TestGetDataDirectory) { - // The string comes from the Java side and depends on the APK - // we are running in. Assumes that we are packaged in - // org.chromium.native_test - FilePath path; - GetDataDirectory(&path); - - ExpectEither("/data/data/org.chromium.native_test/app_chrome", - "/data/user/0/org.chromium.native_test/app_chrome", - path.value()); -} - -TEST_F(PathUtilsTest, TestGetCacheDirectory) { - // The string comes from the Java side and depends on the APK - // we are running in. Assumes that we are packaged in - // org.chromium.native_test - FilePath path; - GetCacheDirectory(&path); - ExpectEither("/data/data/org.chromium.native_test/cache", - "/data/user/0/org.chromium.native_test/cache", - path.value()); -} - -TEST_F(PathUtilsTest, TestGetNativeLibraryDirectory) { - // The string comes from the Java side and depends on the APK - // we are running in. Assumes that the directory contains - // the base tests shared object. - FilePath path; - GetNativeLibraryDirectory(&path); - EXPECT_TRUE( - base::PathExists(path.Append("libbase_unittests.so")) || - base::PathExists(path.Append("libbase_unittests.cr.so")) || - base::PathExists(path.Append("lib_base_unittests__library.so")) || - base::PathExists(path.Append("lib_base_unittests__library.cr.so"))); -} - -} // namespace android -} // namespace base diff --git a/android/proguard/chromium_apk.flags b/android/proguard/chromium_apk.flags deleted file mode 100644 index ac3d7f820..000000000 --- a/android/proguard/chromium_apk.flags +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Contains flags that we'd like all Chromium .apks to use. - -# Not needed for Android and saves a bit of processing time. --dontpreverify - -# Keep line number information, useful for stack traces. --keepattributes SourceFile,LineNumberTable - -# Keep all CREATOR fields within Parcelable that are kept. --keepclassmembers class * implements android.os.Parcelable { - public static *** CREATOR; -} - -# Don't obfuscate Parcelables as they might be marshalled outside Chrome. -# If we annotated all Parcelables that get put into Bundles other than -# for saveInstanceState (e.g. PendingIntents), then we could actually keep the -# names of just those ones. For now, we'll just keep them all. --keepnames class * implements android.os.Parcelable - -# Keep all enum values and valueOf methods. See -# http://proguard.sourceforge.net/index.html#manual/examples.html -# for the reason for this. Also, see http://crbug.com/248037. --keepclassmembers enum * { - public static **[] values(); -} - -# Keep classes implementing ParameterProvider -- these will be instantiated -# via reflection. --keep class * implements org.chromium.base.test.params.ParameterProvider - -# Allows Proguard freedom in removing these log related calls. We ask for debug -# and verbose logs to be stripped out in base.Log, so we are just ensuring we -# get rid of all other debug/verbose logs. --assumenosideeffects class android.util.Log { - static *** d(...); - static *** v(...); - static *** isLoggable(...); -} - -# The following chart was created on July 20, 2016, to decide on 3 optimization -# passes for Chrome. -# optimization passes | time | .dex size | dirty memory per process -# ----------------------------------------------------------------- -# 1 | 0:48 | 5805676 | 488972 -# 2 | 1:07 | 5777376 | 487092 -# 3 | 1:24 | 5772192 | 486596 -# 4 | 1:42 | 5771124 | 486484 -# 5 | 1:56 | 5770504 | 486432 --optimizationpasses 3 - -# Horizontal class merging marginally increases dex size (as of Mar 2018). --optimizations !class/merging/horizontal - -# Allowing Proguard to change modifiers. This change shrinks the .dex size by -# ~1%, and reduces the method count by ~4%. --allowaccessmodification - -# The support library contains references to newer platform versions. -# Don't warn about those in case this app is linking against an older -# platform version. We know about them, and they are safe. --dontwarn android.support.** diff --git a/android/proguard/chromium_code.flags b/android/proguard/chromium_code.flags deleted file mode 100644 index 8a3ec58b1..000000000 --- a/android/proguard/chromium_code.flags +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Contains flags that can be safely shared with Cronet, and thus would be -# appropriate for third-party apps to include. - -# Keep all annotation related attributes that can affect runtime --keepattributes RuntimeVisible*Annotations --keepattributes AnnotationDefault - -# Keep the annotations, because if we don't, the ProGuard rules that use them -# will not be respected. These classes then show up in our final dex, which we -# do not want - see crbug.com/628226. --keep @interface org.chromium.base.annotations.AccessedByNative --keep @interface org.chromium.base.annotations.CalledByNative --keep @interface org.chromium.base.annotations.CalledByNativeUnchecked --keep @interface org.chromium.base.annotations.DoNotInline --keep @interface org.chromium.base.annotations.RemovableInRelease --keep @interface org.chromium.base.annotations.UsedByReflection - -# Keeps for class level annotations. --keep @org.chromium.base.annotations.UsedByReflection class * - -# Keeps for method level annotations. --keepclasseswithmembers class * { - @org.chromium.base.annotations.AccessedByNative ; -} --keepclasseswithmembers,includedescriptorclasses class * { - @org.chromium.base.annotations.CalledByNative ; -} --keepclasseswithmembers,includedescriptorclasses class * { - @org.chromium.base.annotations.CalledByNativeUnchecked ; -} --keepclasseswithmembers class * { - @org.chromium.base.annotations.UsedByReflection ; -} --keepclasseswithmembers class * { - @org.chromium.base.annotations.UsedByReflection ; -} --keepclasseswithmembers,includedescriptorclasses class * { - native ; -} - -# Remove methods annotated with this if their return value is unused. --assumenosideeffects class ** { - @org.chromium.base.annotations.RemovableInRelease ; -} - -# Never inline classes or methods with this annotation, but allow shrinking and -# obfuscation. --keepnames,allowobfuscation @org.chromium.base.annotations.DoNotInline class * { - *; -} --keepclassmembernames,allowobfuscation class * { - @org.chromium.base.annotations.DoNotInline ; -} - -# Keep all CREATOR fields within Parcelable that are kept. --keepclassmembers class org.chromium.** implements android.os.Parcelable { - public static *** CREATOR; -} - -# Don't obfuscate Parcelables as they might be marshalled outside Chrome. -# If we annotated all Parcelables that get put into Bundles other than -# for saveInstanceState (e.g. PendingIntents), then we could actually keep the -# names of just those ones. For now, we'll just keep them all. --keepnames class org.chromium.** implements android.os.Parcelable - -# Keep all enum values and valueOf methods. See -# http://proguard.sourceforge.net/index.html#manual/examples.html -# for the reason for this. Also, see http://crbug.com/248037. --keepclassmembers enum org.chromium.** { - public static **[] values(); -} diff --git a/android/proguard/disable_all_obfuscation.flags b/android/proguard/disable_all_obfuscation.flags deleted file mode 100644 index deca250c4..000000000 --- a/android/proguard/disable_all_obfuscation.flags +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Disables obfuscation while still allowing optimizations. --keepnames,allowoptimization class *** { - *; -} diff --git a/android/proguard/disable_chromium_obfuscation.flags b/android/proguard/disable_chromium_obfuscation.flags deleted file mode 100644 index b410239ea..000000000 --- a/android/proguard/disable_chromium_obfuscation.flags +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# Disables obfuscation for chromium packages. --keepnames,allowoptimization class com.google.android.apps.chrome.**,org.chromium.** { - *; -} diff --git a/android/proguard/enable_obfuscation.flags b/android/proguard/enable_obfuscation.flags deleted file mode 100644 index 11bc240f5..000000000 --- a/android/proguard/enable_obfuscation.flags +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# As of August 11, 2016, obfuscation was found to save 660kb on our .dex size -# and 53kb memory/process (through shrinking method/string counts). --renamesourcefileattribute PG --repackageclasses "" diff --git a/android/record_histogram.cc b/android/record_histogram.cc deleted file mode 100644 index f41ec99d1..000000000 --- a/android/record_histogram.cc +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include -#include - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/lazy_instance.h" -#include "base/macros.h" -#include "base/metrics/histogram.h" -#include "base/metrics/sparse_histogram.h" -#include "base/metrics/statistics_recorder.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/lock.h" -#include "base/time/time.h" -#include "jni/RecordHistogram_jni.h" - -namespace base { -namespace android { -namespace { - -// Simple thread-safe wrapper for caching histograms. This avoids -// relatively expensive JNI string translation for each recording. -class HistogramCache { - public: - HistogramCache() {} - - std::string HistogramConstructionParamsToString(HistogramBase* histogram) { - std::string params_str = histogram->histogram_name(); - switch (histogram->GetHistogramType()) { - case HISTOGRAM: - case LINEAR_HISTOGRAM: - case BOOLEAN_HISTOGRAM: - case CUSTOM_HISTOGRAM: { - Histogram* hist = static_cast(histogram); - params_str += StringPrintf("/%d/%d/%d", hist->declared_min(), - hist->declared_max(), hist->bucket_count()); - break; - } - case SPARSE_HISTOGRAM: - case DUMMY_HISTOGRAM: - break; - } - return params_str; - } - - void JNI_RecordHistogram_CheckHistogramArgs(JNIEnv* env, - jstring j_histogram_name, - int32_t expected_min, - int32_t expected_max, - uint32_t expected_bucket_count, - HistogramBase* histogram) { - std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); - bool valid_arguments = Histogram::InspectConstructionArguments( - histogram_name, &expected_min, &expected_max, &expected_bucket_count); - DCHECK(valid_arguments); - DCHECK(histogram->HasConstructionArguments(expected_min, expected_max, - expected_bucket_count)) - << histogram_name << "/" << expected_min << "/" << expected_max << "/" - << expected_bucket_count << " vs. " - << HistogramConstructionParamsToString(histogram); - } - - HistogramBase* JNI_RecordHistogram_BooleanHistogram(JNIEnv* env, - jstring j_histogram_name, - jlong j_histogram_key) { - DCHECK(j_histogram_name); - HistogramBase* histogram = HistogramFromKey(j_histogram_key); - if (histogram) - return histogram; - - std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); - histogram = BooleanHistogram::FactoryGet( - histogram_name, HistogramBase::kUmaTargetedHistogramFlag); - return histogram; - } - - HistogramBase* JNI_RecordHistogram_EnumeratedHistogram( - JNIEnv* env, - jstring j_histogram_name, - jlong j_histogram_key, - jint j_boundary) { - DCHECK(j_histogram_name); - HistogramBase* histogram = HistogramFromKey(j_histogram_key); - int32_t boundary = static_cast(j_boundary); - if (histogram) { - JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, 1, boundary, - boundary + 1, histogram); - return histogram; - } - - std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); - histogram = - LinearHistogram::FactoryGet(histogram_name, 1, boundary, boundary + 1, - HistogramBase::kUmaTargetedHistogramFlag); - return histogram; - } - - HistogramBase* JNI_RecordHistogram_CustomCountHistogram( - JNIEnv* env, - jstring j_histogram_name, - jlong j_histogram_key, - jint j_min, - jint j_max, - jint j_num_buckets) { - DCHECK(j_histogram_name); - int32_t min = static_cast(j_min); - int32_t max = static_cast(j_max); - int32_t num_buckets = static_cast(j_num_buckets); - HistogramBase* histogram = HistogramFromKey(j_histogram_key); - if (histogram) { - JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, min, max, - num_buckets, histogram); - return histogram; - } - - DCHECK_GE(min, 1) << "The min expected sample must be >= 1"; - - std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); - histogram = - Histogram::FactoryGet(histogram_name, min, max, num_buckets, - HistogramBase::kUmaTargetedHistogramFlag); - return histogram; - } - - HistogramBase* JNI_RecordHistogram_LinearCountHistogram( - JNIEnv* env, - jstring j_histogram_name, - jlong j_histogram_key, - jint j_min, - jint j_max, - jint j_num_buckets) { - DCHECK(j_histogram_name); - int32_t min = static_cast(j_min); - int32_t max = static_cast(j_max); - int32_t num_buckets = static_cast(j_num_buckets); - HistogramBase* histogram = HistogramFromKey(j_histogram_key); - if (histogram) { - JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, min, max, - num_buckets, histogram); - return histogram; - } - - std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); - histogram = - LinearHistogram::FactoryGet(histogram_name, min, max, num_buckets, - HistogramBase::kUmaTargetedHistogramFlag); - return histogram; - } - - HistogramBase* JNI_RecordHistogram_SparseHistogram(JNIEnv* env, - jstring j_histogram_name, - jlong j_histogram_key) { - DCHECK(j_histogram_name); - HistogramBase* histogram = HistogramFromKey(j_histogram_key); - if (histogram) - return histogram; - - std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); - histogram = SparseHistogram::FactoryGet( - histogram_name, HistogramBase::kUmaTargetedHistogramFlag); - return histogram; - } - - HistogramBase* JNI_RecordHistogram_CustomTimesHistogram( - JNIEnv* env, - jstring j_histogram_name, - jlong j_histogram_key, - jint j_min, - jint j_max, - jint j_bucket_count) { - DCHECK(j_histogram_name); - HistogramBase* histogram = HistogramFromKey(j_histogram_key); - int32_t min = static_cast(j_min); - int32_t max = static_cast(j_max); - int32_t bucket_count = static_cast(j_bucket_count); - if (histogram) { - JNI_RecordHistogram_CheckHistogramArgs(env, j_histogram_name, min, max, - bucket_count, histogram); - return histogram; - } - - std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); - // This intentionally uses FactoryGet and not FactoryTimeGet. FactoryTimeGet - // is just a convenience for constructing the underlying Histogram with - // TimeDelta arguments. - histogram = Histogram::FactoryGet(histogram_name, min, max, bucket_count, - HistogramBase::kUmaTargetedHistogramFlag); - return histogram; - } - - private: - // Convert a jlong |histogram_key| from Java to a HistogramBase* via a cast. - // The Java side caches these in a map (see RecordHistogram.java), which is - // safe to do since C++ Histogram objects are never freed. - static HistogramBase* HistogramFromKey(jlong j_histogram_key) { - return reinterpret_cast(j_histogram_key); - } - - DISALLOW_COPY_AND_ASSIGN(HistogramCache); -}; - -LazyInstance::Leaky g_histograms; - -} // namespace - -jlong JNI_RecordHistogram_RecordBooleanHistogram( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& j_histogram_name, - jlong j_histogram_key, - jboolean j_sample) { - bool sample = static_cast(j_sample); - HistogramBase* histogram = - g_histograms.Get().JNI_RecordHistogram_BooleanHistogram( - env, j_histogram_name, j_histogram_key); - histogram->AddBoolean(sample); - return reinterpret_cast(histogram); -} - -jlong JNI_RecordHistogram_RecordEnumeratedHistogram( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& j_histogram_name, - jlong j_histogram_key, - jint j_sample, - jint j_boundary) { - int sample = static_cast(j_sample); - - HistogramBase* histogram = - g_histograms.Get().JNI_RecordHistogram_EnumeratedHistogram( - env, j_histogram_name, j_histogram_key, j_boundary); - histogram->Add(sample); - return reinterpret_cast(histogram); -} - -jlong JNI_RecordHistogram_RecordCustomCountHistogram( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& j_histogram_name, - jlong j_histogram_key, - jint j_sample, - jint j_min, - jint j_max, - jint j_num_buckets) { - int sample = static_cast(j_sample); - - HistogramBase* histogram = - g_histograms.Get().JNI_RecordHistogram_CustomCountHistogram( - env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets); - histogram->Add(sample); - return reinterpret_cast(histogram); -} - -jlong JNI_RecordHistogram_RecordLinearCountHistogram( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& j_histogram_name, - jlong j_histogram_key, - jint j_sample, - jint j_min, - jint j_max, - jint j_num_buckets) { - int sample = static_cast(j_sample); - - HistogramBase* histogram = - g_histograms.Get().JNI_RecordHistogram_LinearCountHistogram( - env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets); - histogram->Add(sample); - return reinterpret_cast(histogram); -} - -jlong JNI_RecordHistogram_RecordSparseHistogram( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& j_histogram_name, - jlong j_histogram_key, - jint j_sample) { - int sample = static_cast(j_sample); - HistogramBase* histogram = - g_histograms.Get().JNI_RecordHistogram_SparseHistogram( - env, j_histogram_name, j_histogram_key); - histogram->Add(sample); - return reinterpret_cast(histogram); -} - -jlong JNI_RecordHistogram_RecordCustomTimesHistogramMilliseconds( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& j_histogram_name, - jlong j_histogram_key, - jint j_duration, - jint j_min, - jint j_max, - jint j_num_buckets) { - HistogramBase* histogram = - g_histograms.Get().JNI_RecordHistogram_CustomTimesHistogram( - env, j_histogram_name, j_histogram_key, j_min, j_max, j_num_buckets); - histogram->AddTime( - TimeDelta::FromMilliseconds(static_cast(j_duration))); - return reinterpret_cast(histogram); -} - -// This backs a Java test util for testing histograms - -// MetricsUtils.HistogramDelta. It should live in a test-specific file, but we -// currently can't have test-specific native code packaged in test-specific Java -// targets - see http://crbug.com/415945. -jint JNI_RecordHistogram_GetHistogramValueCountForTesting( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& histogram_name, - jint sample) { - HistogramBase* histogram = StatisticsRecorder::FindHistogram( - android::ConvertJavaStringToUTF8(env, histogram_name)); - if (histogram == nullptr) { - // No samples have been recorded for this histogram (yet?). - return 0; - } - - std::unique_ptr samples = histogram->SnapshotSamples(); - return samples->GetCount(static_cast(sample)); -} - -jint JNI_RecordHistogram_GetHistogramTotalCountForTesting( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& histogram_name) { - HistogramBase* histogram = StatisticsRecorder::FindHistogram( - android::ConvertJavaStringToUTF8(env, histogram_name)); - if (histogram == nullptr) { - // No samples have been recorded for this histogram. - return 0; - } - - return histogram->SnapshotSamples()->TotalCount(); -} - -} // namespace android -} // namespace base diff --git a/android/record_user_action.cc b/android/record_user_action.cc deleted file mode 100644 index 683add6b9..000000000 --- a/android/record_user_action.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/jni_string.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/metrics/user_metrics.h" -#include "jni/RecordUserAction_jni.h" - -namespace { - -struct ActionCallbackWrapper { - base::ActionCallback action_callback; -}; - -} // namespace - -namespace base { -namespace android { - -static void JNI_RecordUserAction_RecordUserAction( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& j_action) { - RecordComputedAction(ConvertJavaStringToUTF8(env, j_action)); -} - -static void OnActionRecorded(const JavaRef& callback, - const std::string& action) { - JNIEnv* env = AttachCurrentThread(); - Java_UserActionCallback_onActionRecorded( - env, callback, ConvertUTF8ToJavaString(env, action)); -} - -static jlong JNI_RecordUserAction_AddActionCallbackForTesting( - JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& callback) { - // Create a wrapper for the ActionCallback, so it can life on the heap until - // RemoveActionCallbackForTesting() is called. - auto* wrapper = new ActionCallbackWrapper{base::Bind( - &OnActionRecorded, ScopedJavaGlobalRef(env, callback))}; - base::AddActionCallback(wrapper->action_callback); - return reinterpret_cast(wrapper); -} - -static void JNI_RecordUserAction_RemoveActionCallbackForTesting( - JNIEnv* env, - const JavaParamRef& clazz, - jlong callback_id) { - DCHECK(callback_id); - auto* wrapper = reinterpret_cast(callback_id); - base::RemoveActionCallback(wrapper->action_callback); - delete wrapper; -} - -} // namespace android -} // namespace base diff --git a/android/scoped_hardware_buffer_handle.cc b/android/scoped_hardware_buffer_handle.cc deleted file mode 100644 index 55b0a7027..000000000 --- a/android/scoped_hardware_buffer_handle.cc +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2018 The Chromium 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/scoped_hardware_buffer_handle.h" - -#include "base/android/android_hardware_buffer_compat.h" -#include "base/logging.h" -#include "base/posix/unix_domain_socket.h" - -namespace base { -namespace android { - -ScopedHardwareBufferHandle::ScopedHardwareBufferHandle() = default; - -ScopedHardwareBufferHandle::ScopedHardwareBufferHandle( - ScopedHardwareBufferHandle&& other) { - *this = std::move(other); -} - -ScopedHardwareBufferHandle::~ScopedHardwareBufferHandle() { - reset(); -} - -// static -ScopedHardwareBufferHandle ScopedHardwareBufferHandle::Adopt( - AHardwareBuffer* buffer) { - return ScopedHardwareBufferHandle(buffer); -} - -ScopedHardwareBufferHandle& ScopedHardwareBufferHandle::operator=( - ScopedHardwareBufferHandle&& other) { - reset(); - std::swap(buffer_, other.buffer_); - return *this; -} - -bool ScopedHardwareBufferHandle::is_valid() const { - return buffer_ != nullptr; -} - -AHardwareBuffer* ScopedHardwareBufferHandle::get() const { - return buffer_; -} - -void ScopedHardwareBufferHandle::reset() { - if (buffer_) { - AndroidHardwareBufferCompat::GetInstance().Release(buffer_); - buffer_ = nullptr; - } -} - -AHardwareBuffer* ScopedHardwareBufferHandle::Take() { - AHardwareBuffer* buffer = nullptr; - std::swap(buffer, buffer_); - return buffer; -} - -ScopedHardwareBufferHandle ScopedHardwareBufferHandle::Clone() const { - DCHECK(buffer_); - AndroidHardwareBufferCompat::GetInstance().Acquire(buffer_); - return ScopedHardwareBufferHandle(buffer_); -} - -ScopedFD ScopedHardwareBufferHandle::SerializeAsFileDescriptor() const { - DCHECK(is_valid()); - - ScopedFD reader, writer; - if (!CreateSocketPair(&reader, &writer)) { - PLOG(ERROR) << "socketpair"; - return ScopedFD(); - } - - // NOTE: SendHandleToUnixSocket does NOT acquire or retain a reference to the - // buffer object. The caller is therefore responsible for ensuring that the - // buffer remains alive through the lifetime of this file descriptor. - int result = - AndroidHardwareBufferCompat::GetInstance().SendHandleToUnixSocket( - buffer_, writer.get()); - if (result < 0) { - PLOG(ERROR) << "send"; - return ScopedFD(); - } - - return reader; -} - -// static -ScopedHardwareBufferHandle -ScopedHardwareBufferHandle::DeserializeFromFileDescriptor(ScopedFD fd) { - DCHECK(fd.is_valid()); - DCHECK(AndroidHardwareBufferCompat::IsSupportAvailable()); - AHardwareBuffer* buffer = nullptr; - - // NOTE: Upon success, RecvHandleFromUnixSocket acquires a new reference to - // the AHardwareBuffer. - int result = - AndroidHardwareBufferCompat::GetInstance().RecvHandleFromUnixSocket( - fd.get(), &buffer); - if (result < 0) { - PLOG(ERROR) << "recv"; - return ScopedHardwareBufferHandle(); - } - - return ScopedHardwareBufferHandle(buffer); -} - -ScopedHardwareBufferHandle::ScopedHardwareBufferHandle(AHardwareBuffer* buffer) - : buffer_(buffer) { - DCHECK(AndroidHardwareBufferCompat::IsSupportAvailable()); -} - -} // namespace android -} // namespace base diff --git a/android/scoped_hardware_buffer_handle.h b/android/scoped_hardware_buffer_handle.h deleted file mode 100644 index b8121ae81..000000000 --- a/android/scoped_hardware_buffer_handle.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018 The Chromium 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_SCOPED_HARDWARE_BUFFER_HANDLE_H_ -#define BASE_ANDROID_SCOPED_HARDWARE_BUFFER_HANDLE_H_ - -#include "base/base_export.h" -#include "base/files/scoped_file.h" -#include "base/macros.h" - -extern "C" typedef struct AHardwareBuffer AHardwareBuffer; - -namespace base { -namespace android { - -// Owns a single reference to an AHardwareBuffer object. -class BASE_EXPORT ScopedHardwareBufferHandle { - public: - ScopedHardwareBufferHandle(); - - // Takes ownership of |other|'s buffer reference. Does NOT acquire a new one. - ScopedHardwareBufferHandle(ScopedHardwareBufferHandle&& other); - - // Releases this handle's reference to the underlying buffer object if still - // valid. - ~ScopedHardwareBufferHandle(); - - // Assumes ownership of an existing reference to |buffer|. This does NOT - // acquire a new reference. - static ScopedHardwareBufferHandle Adopt(AHardwareBuffer* buffer); - - // Takes ownership of |other|'s buffer reference. Does NOT acquire a new one. - ScopedHardwareBufferHandle& operator=(ScopedHardwareBufferHandle&& other); - - bool is_valid() const; - - AHardwareBuffer* get() const; - - // Releases this handle's reference to the underlying buffer object if still - // valid. Invalidates this handle. - void reset(); - - // Passes implicit ownership of this handle's reference over to the caller, - // invalidating |this|. Returns the raw buffer handle. - // - // The caller is responsible for eventually releasing this reference to the - // buffer object. - AHardwareBuffer* Take() WARN_UNUSED_RESULT; - - // Creates a new handle with its own newly acquired reference to the - // underlying buffer object. |this| must be a valid handle. - ScopedHardwareBufferHandle Clone() const; - - // Consumes a handle and returns a file descriptor which can be used to - // transmit the handle over IPC. A subsequent receiver may use - // |DeserializeFromFileDescriptor()| to recover the buffer handle. - // - // NOTE: The returned file descriptor DOES NOT own a reference to the - // underlying AHardwareBuffer. When using this for IPC, the caller is - // responsible for retaining at least one reference to the buffer object to - // keep it alive while the descriptor is in transit. - ScopedFD SerializeAsFileDescriptor() const; - - // Consumes the supplied single-use file descriptor (which must have been - // returned by a previous call to |SerializeAsFileDescriptor()|, perhaps in - // a different process), and recovers an AHardwareBuffer object from it. - // - // This acquires a new reference to the AHardwareBuffer, with ownership passed - // to the caller via the returned ScopedHardwareBufferHandle. - static ScopedHardwareBufferHandle DeserializeFromFileDescriptor(ScopedFD fd) - WARN_UNUSED_RESULT; - - private: - // Assumes ownership of an existing reference to |buffer|. This does NOT - // acquire a new reference. - explicit ScopedHardwareBufferHandle(AHardwareBuffer* buffer); - - AHardwareBuffer* buffer_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(ScopedHardwareBufferHandle); -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_SCOPED_HARDWARE_BUFFER_HANDLE_H_ diff --git a/android/scoped_java_ref.cc b/android/scoped_java_ref.cc deleted file mode 100644 index 7d31a75bc..000000000 --- a/android/scoped_java_ref.cc +++ /dev/null @@ -1,92 +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/android/scoped_java_ref.h" - -#include "base/android/jni_android.h" -#include "base/logging.h" - -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(nullptr); -} - -#if DCHECK_IS_ON() -// This constructor is inlined when DCHECKs are disabled; don't add anything -// else here. -JavaRef::JavaRef(JNIEnv* env, jobject obj) : obj_(obj) { - if (obj) { - DCHECK(env && env->GetObjectRefType(obj) == JNILocalRefType); - } -} -#endif - -JNIEnv* JavaRef::SetNewLocalRef(JNIEnv* env, jobject obj) { - if (!env) { - env = AttachCurrentThread(); - } else { - DCHECK_EQ(env, AttachCurrentThread()); // Is |env| on correct thread. - } - if (obj) - obj = env->NewLocalRef(obj); - if (obj_) - env->DeleteLocalRef(obj_); - obj_ = obj; - return env; -} - -void JavaRef::SetNewGlobalRef(JNIEnv* env, jobject obj) { - if (!env) { - env = AttachCurrentThread(); - } else { - DCHECK_EQ(env, AttachCurrentThread()); // Is |env| on correct thread. - } - if (obj) - obj = env->NewGlobalRef(obj); - if (obj_) - env->DeleteGlobalRef(obj_); - obj_ = obj; -} - -void JavaRef::ResetLocalRef(JNIEnv* env) { - if (obj_) { - DCHECK_EQ(env, AttachCurrentThread()); // Is |env| on correct thread. - env->DeleteLocalRef(obj_); - obj_ = nullptr; - } -} - -void JavaRef::ResetGlobalRef() { - if (obj_) { - AttachCurrentThread()->DeleteGlobalRef(obj_); - obj_ = nullptr; - } -} - -jobject JavaRef::ReleaseInternal() { - jobject obj = obj_; - obj_ = nullptr; - return obj; -} - -} // namespace android -} // namespace base diff --git a/android/scoped_java_ref.h b/android/scoped_java_ref.h deleted file mode 100644 index 8bb18d4a4..000000000 --- a/android/scoped_java_ref.h +++ /dev/null @@ -1,285 +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_ANDROID_SCOPED_JAVA_REF_H_ -#define BASE_ANDROID_SCOPED_JAVA_REF_H_ - -#include -#include - -#include -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/macros.h" - -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 class JavaRef; - -// Template specialization of JavaRef, which acts as the base class for all -// other JavaRef<> template types. This allows you to e.g. pass -// ScopedJavaLocalRef into a function taking const JavaRef& -template<> -class BASE_EXPORT JavaRef { - public: - // Initializes a null reference. Don't add anything else here; it's inlined. - constexpr JavaRef() : obj_(nullptr) {} - - // Allow nullptr to be converted to JavaRef. This avoids having to declare an - // empty JavaRef just to pass null to a function, and makes C++ "nullptr" and - // Java "null" equivalent. - constexpr JavaRef(std::nullptr_t) : JavaRef() {} - - // Public to allow destruction of null JavaRef objects. - // Don't add anything else here; it's inlined. - ~JavaRef() {} - - jobject obj() const { return obj_; } - - bool is_null() const { return obj_ == nullptr; } - - protected: - // Takes ownership of the |obj| reference passed; requires it to be a local - // reference type. -#if DCHECK_IS_ON() - // Implementation contains a DCHECK; implement out-of-line when DCHECK_IS_ON. - JavaRef(JNIEnv* env, jobject obj); -#else - // Don't add anything else here; it's inlined. - JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {} -#endif - - void swap(JavaRef& other) { std::swap(obj_, other.obj_); } - - // The following are implementation detail convenience methods, for - // use by the sub-classes. - JNIEnv* SetNewLocalRef(JNIEnv* env, jobject obj); - void SetNewGlobalRef(JNIEnv* env, jobject obj); - void ResetLocalRef(JNIEnv* env); - void ResetGlobalRef(); - jobject ReleaseInternal(); - - private: - jobject obj_; - - DISALLOW_COPY_AND_ASSIGN(JavaRef); -}; - -// Generic base class for ScopedJavaLocalRef and ScopedJavaGlobalRef. Useful -// for allowing functions to accept a reference without having to mandate -// whether it is a local or global type. -template -class JavaRef : public JavaRef { - public: - JavaRef() {} - JavaRef(std::nullptr_t) : JavaRef(nullptr) {} - ~JavaRef() {} - - T obj() const { return static_cast(JavaRef::obj()); } - - protected: - JavaRef(JNIEnv* env, T obj) : JavaRef(env, obj) {} - - private: - DISALLOW_COPY_AND_ASSIGN(JavaRef); -}; - -// Holds a local reference to a JNI method parameter. -// Method parameters should not be deleted, and so this class exists purely to -// wrap them as a JavaRef in the JNI binding generator. Do not create -// instances manually. -template -class JavaParamRef : public JavaRef { - public: - // Assumes that |obj| is a parameter passed to a JNI method from Java. - // Does not assume ownership as parameters should not be deleted. - JavaParamRef(JNIEnv* env, T obj) : JavaRef(env, obj) {} - - // Allow nullptr to be converted to JavaParamRef. Some unit tests call JNI - // methods directly from C++ and pass null for objects which are not actually - // used by the implementation (e.g. the caller object); allow this to keep - // working. - JavaParamRef(std::nullptr_t) : JavaRef(nullptr) {} - - ~JavaParamRef() {} - - // TODO(torne): remove this cast once we're using JavaRef consistently. - // http://crbug.com/506850 - operator T() const { return JavaRef::obj(); } - - private: - DISALLOW_COPY_AND_ASSIGN(JavaParamRef); -}; - -// Holds a local reference to a Java object. The local reference is scoped -// to the lifetime of this object. -// Instances of this class may hold onto any JNIEnv passed into it until -// destroyed. Therefore, since a JNIEnv is only suitable for use on a single -// thread, objects of this class must be created, used, and destroyed, on a -// single thread. -// Therefore, this class should only be used as a stack-based object and from a -// single thread. If you wish to have the reference outlive the current -// callstack (e.g. as a class member) or you wish to pass it across threads, -// use a ScopedJavaGlobalRef instead. -template -class ScopedJavaLocalRef : public JavaRef { - public: - constexpr ScopedJavaLocalRef() : env_(nullptr) {} - constexpr ScopedJavaLocalRef(std::nullptr_t) : env_(nullptr) {} - - // Non-explicit copy constructor, to allow ScopedJavaLocalRef to be returned - // by value as this is the normal usage pattern. - ScopedJavaLocalRef(const ScopedJavaLocalRef& other) - : env_(other.env_) { - this->SetNewLocalRef(env_, other.obj()); - } - - ScopedJavaLocalRef(ScopedJavaLocalRef&& other) : env_(other.env_) { - this->swap(other); - } - - explicit ScopedJavaLocalRef(const JavaRef& other) : env_(nullptr) { - this->Reset(other); - } - - // Assumes that |obj| is a local reference to a Java object and takes - // ownership of this local reference. - // TODO(torne): this shouldn't be used outside of JNI helper functions but - // there are currently some cases where there aren't helpers for things. - ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef(env, obj), env_(env) {} - - ~ScopedJavaLocalRef() { - this->Reset(); - } - - // Overloaded assignment operator defined for consistency with the implicit - // copy constructor. - void operator=(const ScopedJavaLocalRef& other) { - this->Reset(other); - } - - void operator=(ScopedJavaLocalRef&& other) { - env_ = other.env_; - this->swap(other); - } - - void Reset() { - this->ResetLocalRef(env_); - } - - void Reset(const ScopedJavaLocalRef& other) { - // We can copy over env_ here as |other| instance must be from the same - // thread as |this| local ref. (See class comment for multi-threading - // limitations, and alternatives). - this->Reset(other.env_, other.obj()); - } - - void Reset(const JavaRef& other) { - // If |env_| was not yet set (is still null) it will be attached to the - // current thread in SetNewLocalRef(). - this->Reset(env_, other.obj()); - } - - // Creates a new local reference to the Java object, unlike the constructor - // with the same parameters that takes ownership of the existing reference. - // TODO(torne): these should match as this is confusing. - void Reset(JNIEnv* env, T obj) { env_ = this->SetNewLocalRef(env, obj); } - - // Releases the local reference to the caller. The caller *must* delete the - // local reference when it is done with it. Note that calling a Java method - // is *not* a transfer of ownership and Release() should not be used. - T Release() { - return static_cast(this->ReleaseInternal()); - } - - 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_; - - // Prevent ScopedJavaLocalRef(JNIEnv*, T obj) from being used to take - // ownership of a JavaParamRef's underlying object - parameters are not - // allowed to be deleted and so should not be owned by ScopedJavaLocalRef. - // TODO(torne): this can be removed once JavaParamRef no longer has an - // implicit conversion back to T. - ScopedJavaLocalRef(JNIEnv* env, const JavaParamRef& other); -}; - -// Holds a global reference to a Java object. The global reference is scoped -// to the lifetime of this object. This class does not hold onto any JNIEnv* -// passed to it, hence it is safe to use across threads (within the constraints -// imposed by the underlying Java object that it references). -template -class ScopedJavaGlobalRef : public JavaRef { - public: - constexpr ScopedJavaGlobalRef() {} - constexpr ScopedJavaGlobalRef(std::nullptr_t) {} - - ScopedJavaGlobalRef(const ScopedJavaGlobalRef& other) { - this->Reset(other); - } - - ScopedJavaGlobalRef(ScopedJavaGlobalRef&& other) { this->swap(other); } - - ScopedJavaGlobalRef(JNIEnv* env, T obj) { this->Reset(env, obj); } - - explicit ScopedJavaGlobalRef(const JavaRef& other) { this->Reset(other); } - - ~ScopedJavaGlobalRef() { - this->Reset(); - } - - // Overloaded assignment operator defined for consistency with the implicit - // copy constructor. - void operator=(const ScopedJavaGlobalRef& other) { - this->Reset(other); - } - - void operator=(ScopedJavaGlobalRef&& other) { this->swap(other); } - - void Reset() { - this->ResetGlobalRef(); - } - - void Reset(const JavaRef& other) { this->Reset(nullptr, other.obj()); } - - void Reset(JNIEnv* env, const JavaParamRef& other) { - this->Reset(env, other.obj()); - } - - void Reset(JNIEnv* env, T obj) { this->SetNewGlobalRef(env, obj); } - - // Releases the global reference to the caller. The caller *must* delete the - // global reference when it is done with it. Note that calling a Java method - // is *not* a transfer of ownership and Release() should not be used. - T Release() { - return static_cast(this->ReleaseInternal()); - } -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_SCOPED_JAVA_REF_H_ diff --git a/android/scoped_java_ref_unittest.cc b/android/scoped_java_ref_unittest.cc deleted file mode 100644 index 99d035bb7..000000000 --- a/android/scoped_java_ref_unittest.cc +++ /dev/null @@ -1,133 +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/android/scoped_java_ref.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace android { - -namespace { -int g_local_refs = 0; -int g_global_refs = 0; - -const JNINativeInterface* g_previous_functions; - -jobject NewGlobalRef(JNIEnv* env, jobject obj) { - ++g_global_refs; - return g_previous_functions->NewGlobalRef(env, obj); -} - -void DeleteGlobalRef(JNIEnv* env, jobject obj) { - --g_global_refs; - return g_previous_functions->DeleteGlobalRef(env, obj); -} - -jobject NewLocalRef(JNIEnv* env, jobject obj) { - ++g_local_refs; - return g_previous_functions->NewLocalRef(env, obj); -} - -void DeleteLocalRef(JNIEnv* env, jobject obj) { - --g_local_refs; - return g_previous_functions->DeleteLocalRef(env, obj); -} -} // namespace - -class ScopedJavaRefTest : public testing::Test { - protected: - void SetUp() override { - g_local_refs = 0; - g_global_refs = 0; - JNIEnv* env = AttachCurrentThread(); - g_previous_functions = env->functions; - hooked_functions = *g_previous_functions; - env->functions = &hooked_functions; - // We inject our own functions in JNINativeInterface so we can keep track - // of the reference counting ourselves. - hooked_functions.NewGlobalRef = &NewGlobalRef; - hooked_functions.DeleteGlobalRef = &DeleteGlobalRef; - hooked_functions.NewLocalRef = &NewLocalRef; - hooked_functions.DeleteLocalRef = &DeleteLocalRef; - } - - void TearDown() override { - JNIEnv* env = AttachCurrentThread(); - env->functions = g_previous_functions; - } - // From JellyBean release, the instance of this struct provided in JNIEnv is - // read-only, so we deep copy it to allow individual functions to be hooked. - JNINativeInterface hooked_functions; -}; - -// The main purpose of this is testing the various conversions compile. -TEST_F(ScopedJavaRefTest, Conversions) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef str = ConvertUTF8ToJavaString(env, "string"); - ScopedJavaGlobalRef global(str); - { - ScopedJavaGlobalRef global_obj(str); - ScopedJavaLocalRef local_obj(global); - const JavaRef& obj_ref1(str); - const JavaRef& obj_ref2(global); - EXPECT_TRUE(env->IsSameObject(obj_ref1.obj(), obj_ref2.obj())); - EXPECT_TRUE(env->IsSameObject(global_obj.obj(), obj_ref2.obj())); - } - global.Reset(str); - const JavaRef& str_ref = str; - EXPECT_EQ("string", ConvertJavaStringToUTF8(str_ref)); - str.Reset(); -} - -TEST_F(ScopedJavaRefTest, RefCounts) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef str; - // The ConvertJavaStringToUTF8 below creates a new string that would normally - // return a local ref. We simulate that by starting the g_local_refs count at - // 1. - g_local_refs = 1; - str.Reset(ConvertUTF8ToJavaString(env, "string")); - EXPECT_EQ(1, g_local_refs); - EXPECT_EQ(0, g_global_refs); - { - ScopedJavaGlobalRef global_str(str); - ScopedJavaGlobalRef global_obj(global_str); - EXPECT_EQ(1, g_local_refs); - EXPECT_EQ(2, g_global_refs); - - ScopedJavaLocalRef str2(env, str.Release()); - EXPECT_EQ(1, g_local_refs); - { - ScopedJavaLocalRef str3(str2); - EXPECT_EQ(2, g_local_refs); - } - EXPECT_EQ(1, g_local_refs); - { - ScopedJavaLocalRef str4((ScopedJavaLocalRef(str2))); - EXPECT_EQ(2, g_local_refs); - } - EXPECT_EQ(1, g_local_refs); - { - ScopedJavaLocalRef str5; - str5 = ScopedJavaLocalRef(str2); - EXPECT_EQ(2, g_local_refs); - } - EXPECT_EQ(1, g_local_refs); - str2.Reset(); - EXPECT_EQ(0, g_local_refs); - global_str.Reset(); - EXPECT_EQ(1, g_global_refs); - ScopedJavaGlobalRef global_obj2(global_obj); - EXPECT_EQ(2, g_global_refs); - } - - EXPECT_EQ(0, g_local_refs); - EXPECT_EQ(0, g_global_refs); -} - -} // namespace android -} // namespace base diff --git a/android/statistics_recorder_android.cc b/android/statistics_recorder_android.cc deleted file mode 100644 index 346a7c738..000000000 --- a/android/statistics_recorder_android.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2016 The Chromium 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 - -#include "base/android/jni_string.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/statistics_recorder.h" -#include "base/sys_info.h" -#include "jni/StatisticsRecorderAndroid_jni.h" - -using base::android::JavaParamRef; -using base::android::ConvertUTF8ToJavaString; - -namespace base { -namespace android { - -static ScopedJavaLocalRef JNI_StatisticsRecorderAndroid_ToJson( - JNIEnv* env, - const JavaParamRef& clazz, - jint verbosityLevel) { - return ConvertUTF8ToJavaString( - env, base::StatisticsRecorder::ToJSON( - static_cast(verbosityLevel))); -} - -} // namespace android -} // namespace base diff --git a/android/sys_utils.cc b/android/sys_utils.cc deleted file mode 100644 index 7872b2f7d..000000000 --- a/android/sys_utils.cc +++ /dev/null @@ -1,51 +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/sys_utils.h" - -#include - -#include "base/android/build_info.h" -#include "base/process/process_metrics.h" -#include "base/sys_info.h" -#include "base/trace_event/trace_event.h" -#include "jni/SysUtils_jni.h" - -namespace base { -namespace android { - -bool SysUtils::IsLowEndDeviceFromJni() { - JNIEnv* env = AttachCurrentThread(); - return Java_SysUtils_isLowEndDevice(env); -} - -bool SysUtils::IsCurrentlyLowMemory() { - JNIEnv* env = AttachCurrentThread(); - return Java_SysUtils_isCurrentlyLowMemory(env); -} - -// Logs the number of minor / major page faults to tracing (and also the time to -// collect) the metrics. Does nothing if tracing is not enabled. -static void JNI_SysUtils_LogPageFaultCountToTracing( - JNIEnv* env, - const base::android::JavaParamRef& jcaller) { - // This is racy, but we are OK losing data, and collecting it is potentially - // expensive (reading and parsing a file). - bool enabled; - TRACE_EVENT_CATEGORY_GROUP_ENABLED("startup", &enabled); - if (!enabled) - return; - TRACE_EVENT_BEGIN2("memory", "CollectPageFaultCount", "minor", 0, "major", 0); - std::unique_ptr process_metrics( - base::ProcessMetrics::CreateProcessMetrics( - base::GetCurrentProcessHandle())); - base::PageFaultCounts counts; - process_metrics->GetPageFaultCounts(&counts); - TRACE_EVENT_END2("memory", "CollectPageFaults", "minor", counts.minor, - "major", counts.major); -} - -} // namespace android - -} // namespace base diff --git a/android/sys_utils.h b/android/sys_utils.h deleted file mode 100644 index b1e368bac..000000000 --- a/android/sys_utils.h +++ /dev/null @@ -1,24 +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_SYS_UTILS_H_ -#define BASE_ANDROID_SYS_UTILS_H_ - -#include "base/android/jni_android.h" - -namespace base { -namespace android { - -class BASE_EXPORT SysUtils { - public: - // Returns true iff this is a low-end device. - static bool IsLowEndDeviceFromJni(); - // Returns true if system has low available memory. - static bool IsCurrentlyLowMemory(); -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_SYS_UTILS_H_ diff --git a/android/sys_utils_unittest.cc b/android/sys_utils_unittest.cc deleted file mode 100644 index d16e2368d..000000000 --- a/android/sys_utils_unittest.cc +++ /dev/null @@ -1,24 +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 -#include - -#include "base/sys_info.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace android { - -TEST(SysUtils, AmountOfPhysicalMemory) { - // Check that the RAM size reported by sysconf() matches the one - // computed by base::SysInfo::AmountOfPhysicalMemory(). - size_t sys_ram_size = - static_cast(sysconf(_SC_PHYS_PAGES) * PAGE_SIZE); - EXPECT_EQ(sys_ram_size, - static_cast(SysInfo::AmountOfPhysicalMemory())); -} - -} // namespace android -} // namespace base diff --git a/android/throw_uncaught_exception.cc b/android/throw_uncaught_exception.cc deleted file mode 100644 index 68627cc20..000000000 --- a/android/throw_uncaught_exception.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017 The Chromium 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/throw_uncaught_exception.h" - -#include "base/android/jni_android.h" - -#include "jni/ThrowUncaughtException_jni.h" - -namespace base { -namespace android { - -void ThrowUncaughtException() { - Java_ThrowUncaughtException_post(AttachCurrentThread()); -} - -} // namespace android -} // namespace base diff --git a/android/throw_uncaught_exception.h b/android/throw_uncaught_exception.h deleted file mode 100644 index 57bd9088f..000000000 --- a/android/throw_uncaught_exception.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2017 The Chromium 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_THROW_UNCAUGHT_EXCEPTION_H_ -#define BASE_ANDROID_THROW_UNCAUGHT_EXCEPTION_H_ - -#include "base/base_export.h" - -namespace base { -namespace android { - -// Throw that completely unwinds the java stack. In particular, this will not -// trigger a jni CheckException crash. -BASE_EXPORT void ThrowUncaughtException(); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_THROW_UNCAUGHT_EXCEPTION_H_ diff --git a/android/time_utils.cc b/android/time_utils.cc deleted file mode 100644 index 632dfb7a4..000000000 --- a/android/time_utils.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2016 The Chromium 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 - -#include "base/time/time.h" -#include "jni/TimeUtils_jni.h" - -namespace base { -namespace android { - -static jlong JNI_TimeUtils_GetTimeTicksNowUs( - JNIEnv* env, - const JavaParamRef& clazz) { - return (TimeTicks::Now() - TimeTicks()).InMicroseconds(); -} - -} // namespace android -} // namespace base diff --git a/android/timezone_utils.cc b/android/timezone_utils.cc deleted file mode 100644 index 5243cdc56..000000000 --- a/android/timezone_utils.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 The Chromium 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/timezone_utils.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/strings/string16.h" -#include "jni/TimezoneUtils_jni.h" - -namespace base { -namespace android { - -base::string16 GetDefaultTimeZoneId() { - JNIEnv* env = base::android::AttachCurrentThread(); - ScopedJavaLocalRef timezone_id = - Java_TimezoneUtils_getDefaultTimeZoneId(env); - return ConvertJavaStringToUTF16(timezone_id); -} - -} // namespace android -} // namespace base diff --git a/android/timezone_utils.h b/android/timezone_utils.h deleted file mode 100644 index f78ef85d7..000000000 --- a/android/timezone_utils.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2017 The Chromium 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_TIMEZONE_UTILS_H_ -#define BASE_ANDROID_TIMEZONE_UTILS_H_ - -#include - -#include "base/base_export.h" -#include "base/strings/string16.h" - -namespace base { -namespace android { - -// Return an ICU timezone created from the host timezone. -BASE_EXPORT base::string16 GetDefaultTimeZoneId(); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_TIMEZONE_UTILS_H_ diff --git a/android/trace_event_binding.cc b/android/trace_event_binding.cc deleted file mode 100644 index 623ca7529..000000000 --- a/android/trace_event_binding.cc +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include - -#include "base/android/jni_string.h" -#include "base/lazy_instance.h" -#include "base/macros.h" -#include "base/trace_event/trace_event.h" -#include "base/trace_event/trace_event_impl.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) - : name_(ConvertJavaStringToUTF8(env, jname)), - has_arg_(jarg != nullptr), - arg_(jarg ? ConvertJavaStringToUTF8(env, jarg) : "") {} - ~TraceEventDataConverter() { - } - - // Return saves values to pass to TRACE_EVENT macros. - const char* name() { return name_.c_str(); } - const char* arg_name() { return has_arg_ ? "arg" : nullptr; } - const char* arg() { return has_arg_ ? arg_.c_str() : nullptr; } - - private: - std::string name_; - bool has_arg_; - std::string arg_; - - DISALLOW_COPY_AND_ASSIGN(TraceEventDataConverter); -}; - -class TraceEnabledObserver - : public trace_event::TraceLog::EnabledStateObserver { - public: - void OnTraceLogEnabled() override { - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::Java_TraceEvent_setEnabled(env, true); - } - void OnTraceLogDisabled() override { - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::Java_TraceEvent_setEnabled(env, false); - } -}; - -base::LazyInstance::Leaky g_trace_enabled_state_observer_; - -} // namespace - -static void JNI_TraceEvent_RegisterEnabledObserver( - JNIEnv* env, - const JavaParamRef& clazz) { - bool enabled = trace_event::TraceLog::GetInstance()->IsEnabled(); - base::android::Java_TraceEvent_setEnabled(env, enabled); - trace_event::TraceLog::GetInstance()->AddEnabledStateObserver( - g_trace_enabled_state_observer_.Pointer()); -} - -static void JNI_TraceEvent_StartATrace(JNIEnv* env, - const JavaParamRef& clazz) { - base::trace_event::TraceLog::GetInstance()->StartATrace(); -} - -static void JNI_TraceEvent_StopATrace(JNIEnv* env, - const JavaParamRef& clazz) { - base::trace_event::TraceLog::GetInstance()->StopATrace(); -} - -static void JNI_TraceEvent_Instant(JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jname, - const JavaParamRef& 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 JNI_TraceEvent_Begin(JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jname, - const JavaParamRef& 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 JNI_TraceEvent_End(JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jname, - const JavaParamRef& 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 JNI_TraceEvent_BeginToplevel(JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jtarget) { - std::string target = ConvertJavaStringToUTF8(env, jtarget); - TRACE_EVENT_BEGIN1(kToplevelCategory, kLooperDispatchMessage, "target", - target); -} - -static void JNI_TraceEvent_EndToplevel(JNIEnv* env, - const JavaParamRef& clazz) { - TRACE_EVENT_END0(kToplevelCategory, kLooperDispatchMessage); -} - -static void JNI_TraceEvent_StartAsync(JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jname, - jlong jid) { - TraceEventDataConverter converter(env, jname, nullptr); - TRACE_EVENT_COPY_ASYNC_BEGIN0(kJavaCategory, converter.name(), jid); -} - -static void JNI_TraceEvent_FinishAsync(JNIEnv* env, - const JavaParamRef& clazz, - const JavaParamRef& jname, - jlong jid) { - TraceEventDataConverter converter(env, jname, nullptr); - TRACE_EVENT_COPY_ASYNC_END0(kJavaCategory, converter.name(), jid); -} - -} // namespace android -} // namespace base diff --git a/android/unguessable_token_android.cc b/android/unguessable_token_android.cc deleted file mode 100644 index d04155741..000000000 --- a/android/unguessable_token_android.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2016 The Chromium 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/unguessable_token_android.h" - -#include "jni/UnguessableToken_jni.h" - -namespace base { -namespace android { - -ScopedJavaLocalRef UnguessableTokenAndroid::Create( - JNIEnv* env, - const base::UnguessableToken& token) { - const uint64_t high = token.GetHighForSerialization(); - const uint64_t low = token.GetLowForSerialization(); - DCHECK(high); - DCHECK(low); - return Java_UnguessableToken_create(env, high, low); -} - -base::UnguessableToken UnguessableTokenAndroid::FromJavaUnguessableToken( - JNIEnv* env, - const JavaRef& token) { - const uint64_t high = - Java_UnguessableToken_getHighForSerialization(env, token); - const uint64_t low = Java_UnguessableToken_getLowForSerialization(env, token); - DCHECK(high); - DCHECK(low); - return base::UnguessableToken::Deserialize(high, low); -} - -ScopedJavaLocalRef -UnguessableTokenAndroid::ParcelAndUnparcelForTesting( - JNIEnv* env, - const JavaRef& token) { - return Java_UnguessableToken_parcelAndUnparcelForTesting(env, token); -} - -} // namespace android -} // namespace base diff --git a/android/unguessable_token_android.h b/android/unguessable_token_android.h deleted file mode 100644 index bb91f0e50..000000000 --- a/android/unguessable_token_android.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 The Chromium 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_UNGUESSABLE_TOKEN_ANDROID_H_ -#define BASE_ANDROID_UNGUESSABLE_TOKEN_ANDROID_H_ - -#include - -#include "base/android/scoped_java_ref.h" -#include "base/base_export.h" -#include "base/unguessable_token.h" - -namespace base { -namespace android { - -class BASE_EXPORT UnguessableTokenAndroid { - public: - // Create a Java UnguessableToken with the same value as |token|. - static ScopedJavaLocalRef Create( - JNIEnv* env, - const base::UnguessableToken& token); - - // Create a native UnguessableToken from Java UnguessableToken |token|. - static base::UnguessableToken FromJavaUnguessableToken( - JNIEnv* env, - const JavaRef& token); - - // Parcel UnguessableToken |token| and unparcel it, and return the result. - // While this method is intended for facilitating unit tests, it results only - // in a clone of |token|. - static ScopedJavaLocalRef ParcelAndUnparcelForTesting( - JNIEnv* env, - const JavaRef& token); - - private: - DISALLOW_COPY_AND_ASSIGN(UnguessableTokenAndroid); -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_UNGUESSABLE_TOKEN_ANDROID_H_ diff --git a/android/unguessable_token_android_unittest.cc b/android/unguessable_token_android_unittest.cc deleted file mode 100644 index bdad746d5..000000000 --- a/android/unguessable_token_android_unittest.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2016 The Chromium 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/unguessable_token_android.h" - -#include "base/android/jni_android.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace android { - -TEST(UnguessableTokenAndroid, BasicCreateToken) { - JNIEnv* env = AttachCurrentThread(); - uint64_t high = 0x1234567812345678; - uint64_t low = 0x0583503029282304; - base::UnguessableToken token = base::UnguessableToken::Deserialize(high, low); - ScopedJavaLocalRef jtoken = - UnguessableTokenAndroid::Create(env, token); - base::UnguessableToken result = - UnguessableTokenAndroid::FromJavaUnguessableToken(env, jtoken); - - EXPECT_EQ(token, result); -} - -TEST(UnguessableTokenAndroid, ParcelAndUnparcel) { - JNIEnv* env = AttachCurrentThread(); - uint64_t high = 0x1234567812345678; - uint64_t low = 0x0583503029282304; - base::UnguessableToken token = base::UnguessableToken::Deserialize(high, low); - ScopedJavaLocalRef jtoken = - UnguessableTokenAndroid::Create(env, token); - ScopedJavaLocalRef jtoken_clone = - UnguessableTokenAndroid::ParcelAndUnparcelForTesting(env, jtoken); - base::UnguessableToken token_clone = - UnguessableTokenAndroid::FromJavaUnguessableToken(env, jtoken_clone); - - EXPECT_EQ(token, token_clone); -} - -} // namespace android -} // namespace base diff --git a/at_exit.cc b/at_exit.cc deleted file mode 100644 index 52c215139..000000000 --- a/at_exit.cc +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/at_exit.h" - -#include -#include -#include - -#include "base/bind.h" -#include "base/callback.h" -#include "base/logging.h" - -namespace base { - -// Keep a stack of registered AtExitManagers. We always operate on the most -// recent, and we should never have more than one outside of testing (for a -// statically linked version of this library). Testing may use the shadow -// version of the constructor, and if we are building a dynamic library we may -// end up with multiple AtExitManagers on the same process. We don't protect -// this for thread-safe access, since it will only be modified in testing. -static AtExitManager* g_top_manager = nullptr; - -static bool g_disable_managers = false; - -AtExitManager::AtExitManager() - : processing_callbacks_(false), next_manager_(g_top_manager) { -// If multiple modules instantiate AtExitManagers they'll end up living in this -// module... they have to coexist. -#if !defined(COMPONENT_BUILD) - DCHECK(!g_top_manager); -#endif - g_top_manager = this; -} - -AtExitManager::~AtExitManager() { - if (!g_top_manager) { - NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager"; - return; - } - DCHECK_EQ(this, g_top_manager); - - if (!g_disable_managers) - ProcessCallbacksNow(); - g_top_manager = next_manager_; -} - -// static -void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) { - DCHECK(func); - RegisterTask(base::Bind(func, param)); -} - -// static -void AtExitManager::RegisterTask(base::Closure task) { - if (!g_top_manager) { - NOTREACHED() << "Tried to RegisterCallback without an AtExitManager"; - return; - } - - AutoLock lock(g_top_manager->lock_); - DCHECK(!g_top_manager->processing_callbacks_); - g_top_manager->stack_.push(std::move(task)); -} - -// static -void AtExitManager::ProcessCallbacksNow() { - if (!g_top_manager) { - NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager"; - return; - } - - // Callbacks may try to add new callbacks, so run them without holding - // |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but - // handle it gracefully in release builds so we don't deadlock. - base::stack tasks; - { - AutoLock lock(g_top_manager->lock_); - tasks.swap(g_top_manager->stack_); - g_top_manager->processing_callbacks_ = true; - } - - // Relax the cross-thread access restriction to non-thread-safe RefCount. - // It's safe since all other threads should be terminated at this point. - ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access; - - while (!tasks.empty()) { - base::Closure task = tasks.top(); - task.Run(); - tasks.pop(); - } - - // Expect that all callbacks have been run. - DCHECK(g_top_manager->stack_.empty()); -} - -void AtExitManager::DisableAllAtExitManagers() { - AutoLock lock(g_top_manager->lock_); - g_disable_managers = true; -} - -AtExitManager::AtExitManager(bool shadow) - : processing_callbacks_(false), next_manager_(g_top_manager) { - DCHECK(shadow || !g_top_manager); - g_top_manager = this; -} - -} // namespace base diff --git a/at_exit.h b/at_exit.h deleted file mode 100644 index e74de8d78..000000000 --- a/at_exit.h +++ /dev/null @@ -1,80 +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_AT_EXIT_H_ -#define BASE_AT_EXIT_H_ - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/containers/stack.h" -#include "base/macros.h" -#include "base/synchronization/lock.h" - -namespace base { - -// This class provides a facility similar to the CRT atexit(), except that -// we control when the callbacks are executed. Under Windows for a DLL they -// happen at a really bad time and under the loader lock. This facility is -// mostly used by base::Singleton. -// -// The usage is simple. Early in the main() or WinMain() scope create an -// AtExitManager object on the stack: -// int main(...) { -// base::AtExitManager exit_manager; -// -// } -// When the exit_manager object goes out of scope, all the registered -// callbacks and singleton destructors will be called. - -class BASE_EXPORT AtExitManager { - public: - typedef void (*AtExitCallbackType)(void*); - - AtExitManager(); - - // The dtor calls all the registered callbacks. Do not try to register more - // callbacks after this point. - ~AtExitManager(); - - // Registers the specified function to be called at exit. The prototype of - // the callback function is void func(void*). - static void RegisterCallback(AtExitCallbackType func, void* param); - - // Registers the specified task to be called at exit. - static void RegisterTask(base::Closure task); - - // Calls the functions registered with RegisterCallback in LIFO order. It - // is possible to register new callbacks after calling this function. - static void ProcessCallbacksNow(); - - // Disable all registered at-exit callbacks. This is used only in a single- - // process mode. - static void DisableAllAtExitManagers(); - - protected: - // This constructor will allow this instance of AtExitManager to be created - // even if one already exists. This should only be used for testing! - // AtExitManagers are kept on a global stack, and it will be removed during - // destruction. This allows you to shadow another AtExitManager. - explicit AtExitManager(bool shadow); - - private: - base::Lock lock_; - base::stack stack_; - bool processing_callbacks_; - AtExitManager* next_manager_; // Stack of managers to allow shadowing. - - DISALLOW_COPY_AND_ASSIGN(AtExitManager); -}; - -#if defined(UNIT_TEST) -class ShadowingAtExitManager : public AtExitManager { - public: - ShadowingAtExitManager() : AtExitManager(true) {} -}; -#endif // defined(UNIT_TEST) - -} // namespace base - -#endif // BASE_AT_EXIT_H_ diff --git a/at_exit_unittest.cc b/at_exit_unittest.cc deleted file mode 100644 index 3de061f6a..000000000 --- a/at_exit_unittest.cc +++ /dev/null @@ -1,87 +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/at_exit.h" -#include "base/bind.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -int g_test_counter_1 = 0; -int g_test_counter_2 = 0; - -void IncrementTestCounter1(void* unused) { - ++g_test_counter_1; -} - -void IncrementTestCounter2(void* unused) { - ++g_test_counter_2; -} - -void ZeroTestCounters() { - g_test_counter_1 = 0; - g_test_counter_2 = 0; -} - -void ExpectCounter1IsZero(void* unused) { - EXPECT_EQ(0, g_test_counter_1); -} - -void ExpectParamIsNull(void* param) { - EXPECT_EQ(nullptr, param); -} - -void ExpectParamIsCounter(void* param) { - EXPECT_EQ(&g_test_counter_1, param); -} - -} // namespace - -class AtExitTest : public testing::Test { - private: - // Don't test the global AtExitManager, because asking it to process its - // AtExit callbacks can ruin the global state that other tests may depend on. - base::ShadowingAtExitManager exit_manager_; -}; - -TEST_F(AtExitTest, Basic) { - ZeroTestCounters(); - base::AtExitManager::RegisterCallback(&IncrementTestCounter1, nullptr); - base::AtExitManager::RegisterCallback(&IncrementTestCounter2, nullptr); - base::AtExitManager::RegisterCallback(&IncrementTestCounter1, nullptr); - - EXPECT_EQ(0, g_test_counter_1); - EXPECT_EQ(0, g_test_counter_2); - base::AtExitManager::ProcessCallbacksNow(); - EXPECT_EQ(2, g_test_counter_1); - EXPECT_EQ(1, g_test_counter_2); -} - -TEST_F(AtExitTest, LIFOOrder) { - ZeroTestCounters(); - base::AtExitManager::RegisterCallback(&IncrementTestCounter1, nullptr); - base::AtExitManager::RegisterCallback(&ExpectCounter1IsZero, nullptr); - base::AtExitManager::RegisterCallback(&IncrementTestCounter2, nullptr); - - EXPECT_EQ(0, g_test_counter_1); - EXPECT_EQ(0, g_test_counter_2); - base::AtExitManager::ProcessCallbacksNow(); - EXPECT_EQ(1, g_test_counter_1); - EXPECT_EQ(1, g_test_counter_2); -} - -TEST_F(AtExitTest, Param) { - base::AtExitManager::RegisterCallback(&ExpectParamIsNull, nullptr); - base::AtExitManager::RegisterCallback(&ExpectParamIsCounter, - &g_test_counter_1); - base::AtExitManager::ProcessCallbacksNow(); -} - -TEST_F(AtExitTest, Task) { - ZeroTestCounters(); - base::AtExitManager::RegisterTask(base::Bind(&ExpectParamIsCounter, - &g_test_counter_1)); - base::AtExitManager::ProcessCallbacksNow(); -} diff --git a/atomic_ref_count.h b/atomic_ref_count.h deleted file mode 100644 index 3ffa017ac..000000000 --- a/atomic_ref_count.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a low level implementation of atomic semantics for reference -// counting. Please use base/memory/ref_counted.h directly instead. - -#ifndef BASE_ATOMIC_REF_COUNT_H_ -#define BASE_ATOMIC_REF_COUNT_H_ - -#include - -namespace base { - -class AtomicRefCount { - public: - constexpr AtomicRefCount() : ref_count_(0) {} - explicit constexpr AtomicRefCount(int initial_value) - : ref_count_(initial_value) {} - - // Increment a reference count. - void Increment() { Increment(1); } - - // Increment a reference count by "increment", which must exceed 0. - void Increment(int increment) { - ref_count_.fetch_add(increment, std::memory_order_relaxed); - } - - // Decrement a reference count, and return whether the result is non-zero. - // Insert barriers to ensure that state written before the reference count - // became zero will be visible to a thread that has just made the count zero. - bool Decrement() { - // TODO(jbroman): Technically this doesn't need to be an acquire operation - // unless the result is 1 (i.e., the ref count did indeed reach zero). - // However, there are toolchain issues that make that not work as well at - // present (notably TSAN doesn't like it). - return ref_count_.fetch_sub(1, std::memory_order_acq_rel) != 1; - } - - // Return whether the reference count is one. If the reference count is used - // in the conventional way, a refrerence count of 1 implies that the current - // thread owns the reference and no other thread shares it. This call - // performs the test for a reference count of one, and performs the memory - // barrier needed for the owning thread to act on the object, knowing that it - // has exclusive access to the object. - bool IsOne() const { return ref_count_.load(std::memory_order_acquire) == 1; } - - // Return whether the reference count is zero. With conventional object - // referencing counting, the object will be destroyed, so the reference count - // should never be zero. Hence this is generally used for a debug check. - bool IsZero() const { - return ref_count_.load(std::memory_order_acquire) == 0; - } - - // Returns the current reference count (with no barriers). This is subtle, and - // should be used only for debugging. - int SubtleRefCountForDebug() const { - return ref_count_.load(std::memory_order_relaxed); - } - - private: - std::atomic_int ref_count_; -}; - -} // namespace base - -#endif // BASE_ATOMIC_REF_COUNT_H_ diff --git a/atomic_sequence_num.h b/atomic_sequence_num.h deleted file mode 100644 index 717e37a60..000000000 --- a/atomic_sequence_num.h +++ /dev/null @@ -1,33 +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_ATOMIC_SEQUENCE_NUM_H_ -#define BASE_ATOMIC_SEQUENCE_NUM_H_ - -#include - -#include "base/macros.h" - -namespace base { - -// AtomicSequenceNumber is a thread safe increasing sequence number generator. -// Its constructor doesn't emit a static initializer, so it's safe to use as a -// global variable or static member. -class AtomicSequenceNumber { - public: - constexpr AtomicSequenceNumber() = default; - - // Returns an increasing sequence number starts from 0 for each call. - // This function can be called from any thread without data race. - inline int GetNext() { return seq_.fetch_add(1, std::memory_order_relaxed); } - - private: - std::atomic_int seq_{0}; - - DISALLOW_COPY_AND_ASSIGN(AtomicSequenceNumber); -}; - -} // namespace base - -#endif // BASE_ATOMIC_SEQUENCE_NUM_H_ diff --git a/atomicops.h b/atomicops.h deleted file mode 100644 index 4d8510e89..000000000 --- a/atomicops.h +++ /dev/null @@ -1,161 +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. - -// For atomic operations on reference counts, see atomic_refcount.h. -// For atomic operations on sequence numbers, see atomic_sequence_num.h. - -// The routines exported by this module are subtle. If you use them, even if -// you get the code right, it will depend on careful reasoning about atomicity -// and memory ordering; it will be less readable, and harder to maintain. If -// you plan to use these routines, you should have a good reason, such as solid -// evidence that performance would otherwise suffer, or there being no -// alternative. You should assume only properties explicitly guaranteed by the -// specifications in this file. You are almost certainly _not_ writing code -// just for the x86; if you assume x86 semantics, x86 hardware bugs and -// implementations on other archtectures will cause your code to break. If you -// do not know what you are doing, avoid these routines, and use a Mutex. -// -// It is incorrect to make direct assignments to/from an atomic variable. -// You should use one of the Load or Store routines. The NoBarrier -// versions are provided when no barriers are needed: -// NoBarrier_Store() -// NoBarrier_Load() -// Although there are currently no compiler enforcement, you are encouraged -// to use these. -// - -#ifndef BASE_ATOMICOPS_H_ -#define BASE_ATOMICOPS_H_ - -#include - -// Small C++ header which defines implementation specific macros used to -// identify the STL implementation. -// - libc++: captures __config for _LIBCPP_VERSION -// - libstdc++: captures bits/c++config.h for __GLIBCXX__ -#include - -#include "base/base_export.h" -#include "build/build_config.h" - -#if defined(OS_WIN) && 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 -// X64, undef it, and call its documented -// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx) -// implementation directly. -#undef MemoryBarrier -#endif - -namespace base { -namespace subtle { - -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. -#if defined(__ILP32__) || defined(OS_NACL) -// NaCl's intptr_t is not actually 64-bits on 64-bit! -// http://code.google.com/p/nativeclient/issues/detail?id=1162 -typedef int64_t Atomic64; -#else -typedef intptr_t Atomic64; -#endif -#endif - -// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or -// Atomic64 routines below, depending on your architecture. -typedef intptr_t AtomicWord; - -// Atomically execute: -// result = *ptr; -// if (*ptr == old_value) -// *ptr = new_value; -// return result; -// -// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value". -// Always return the old value of "*ptr" -// -// This routine implies no memory barriers. -Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value); - -// Atomically store new_value into *ptr, returning the previous value held in -// *ptr. This routine implies no memory barriers. -Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value); - -// Atomically increment *ptr by "increment". Returns the new value of -// *ptr with the increment applied. This routine implies no memory barriers. -Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment); - -Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment); - -// These following lower-level operations are typically useful only to people -// implementing higher-level synchronization operations like spinlocks, -// mutexes, and condition-variables. They combine CompareAndSwap(), a load, or -// a store with appropriate memory-ordering instructions. "Acquire" operations -// ensure that no later memory access can be reordered ahead of the operation. -// "Release" operations ensure that no previous memory access can be reordered -// after the operation. "Barrier" operations have both "Acquire" and "Release" -// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory -// access. -Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value); -Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value); - -void MemoryBarrier(); -void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value); -void Acquire_Store(volatile Atomic32* ptr, Atomic32 value); -void Release_Store(volatile Atomic32* ptr, Atomic32 value); - -Atomic32 NoBarrier_Load(volatile const Atomic32* ptr); -Atomic32 Acquire_Load(volatile const Atomic32* ptr); -Atomic32 Release_Load(volatile const Atomic32* ptr); - -// 64-bit atomic operations (only available on 64-bit processors). -#ifdef ARCH_CPU_64_BITS -Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value); -Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value); -Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); -Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); - -Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value); -Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value); -void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value); -void Acquire_Store(volatile Atomic64* ptr, Atomic64 value); -void Release_Store(volatile Atomic64* ptr, Atomic64 value); -Atomic64 NoBarrier_Load(volatile const Atomic64* ptr); -Atomic64 Acquire_Load(volatile const Atomic64* ptr); -Atomic64 Release_Load(volatile const Atomic64* ptr); -#endif // ARCH_CPU_64_BITS - -} // namespace subtle -} // namespace base - -#if defined(OS_WIN) -// TODO(jfb): Try to use base/atomicops_internals_portable.h everywhere. -// https://crbug.com/559247. -# include "base/atomicops_internals_x86_msvc.h" -#else -# include "base/atomicops_internals_portable.h" -#endif - -// On some platforms we need additional declarations to make -// AtomicWord compatible with our other Atomic* types. -#if defined(OS_MACOSX) || defined(OS_OPENBSD) -#include "base/atomicops_internals_atomicword_compat.h" -#endif - -#endif // BASE_ATOMICOPS_H_ diff --git a/atomicops_internals_atomicword_compat.h b/atomicops_internals_atomicword_compat.h deleted file mode 100644 index 8b000d257..000000000 --- a/atomicops_internals_atomicword_compat.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file is an internal atomic implementation, use base/atomicops.h instead. - -#ifndef BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_ -#define BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_ - -#include - -#include "build/build_config.h" - -// AtomicWord is a synonym for intptr_t, and Atomic32 is a synonym for int32_t, -// which in turn means int. On some LP32 platforms, intptr_t is an int, but -// on others, it's a long. When AtomicWord and Atomic32 are based on different -// fundamental types, their pointers are incompatible. -// -// This file defines function overloads to allow both AtomicWord and Atomic32 -// data to be used with this interface. -// -// On LP64 platforms, AtomicWord and Atomic64 are both always long, -// so this problem doesn't occur. - -#if !defined(ARCH_CPU_64_BITS) - -namespace base { -namespace subtle { - -inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr, - AtomicWord old_value, - AtomicWord new_value) { - return NoBarrier_CompareAndSwap( - reinterpret_cast(ptr), old_value, new_value); -} - -inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr, - AtomicWord new_value) { - return NoBarrier_AtomicExchange( - reinterpret_cast(ptr), new_value); -} - -inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr, - AtomicWord increment) { - return NoBarrier_AtomicIncrement( - reinterpret_cast(ptr), increment); -} - -inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr, - AtomicWord increment) { - return Barrier_AtomicIncrement( - reinterpret_cast(ptr), increment); -} - -inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr, - AtomicWord old_value, - AtomicWord new_value) { - return base::subtle::Acquire_CompareAndSwap( - reinterpret_cast(ptr), old_value, new_value); -} - -inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr, - AtomicWord old_value, - AtomicWord new_value) { - return base::subtle::Release_CompareAndSwap( - reinterpret_cast(ptr), old_value, new_value); -} - -inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) { - NoBarrier_Store( - reinterpret_cast(ptr), value); -} - -inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) { - return base::subtle::Acquire_Store( - reinterpret_cast(ptr), value); -} - -inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) { - return base::subtle::Release_Store( - reinterpret_cast(ptr), value); -} - -inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) { - return NoBarrier_Load( - reinterpret_cast(ptr)); -} - -inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) { - return base::subtle::Acquire_Load( - reinterpret_cast(ptr)); -} - -inline AtomicWord Release_Load(volatile const AtomicWord* ptr) { - return base::subtle::Release_Load( - reinterpret_cast(ptr)); -} - -} // namespace subtle -} // namespace base - -#endif // !defined(ARCH_CPU_64_BITS) - -#endif // BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_ diff --git a/atomicops_internals_portable.h b/atomicops_internals_portable.h deleted file mode 100644 index ee034dee1..000000000 --- a/atomicops_internals_portable.h +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) 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 atomicops.h instead. -// -// This implementation uses C++11 atomics' member functions. The code base is -// currently written assuming atomicity revolves around accesses instead of -// C++11's memory locations. The burden is on the programmer to ensure that all -// memory locations accessed atomically are never accessed non-atomically (tsan -// should help with this). -// -// TODO(jfb) Modify the atomicops.h API and user code to declare atomic -// locations as truly atomic. See the static_assert below. -// -// Of note in this implementation: -// * All NoBarrier variants are implemented as relaxed. -// * All Barrier variants are implemented as sequentially-consistent. -// * Compare exchange's failure ordering is always the same as the success one -// (except for release, which fails as relaxed): using a weaker ordering is -// only valid under certain uses of compare exchange. -// * Acquire store doesn't exist in the C11 memory model, it is instead -// implemented as a relaxed store followed by a sequentially consistent -// fence. -// * Release load doesn't exist in the C11 memory model, it is instead -// implemented as sequentially consistent fence followed by a relaxed load. -// * Atomic increment is expected to return the post-incremented value, whereas -// C11 fetch add returns the previous value. The implementation therefore -// needs to increment twice (which the compiler should be able to detect and -// optimize). - -#ifndef BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ -#define BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ - -#include - -#include "build/build_config.h" - -namespace base { -namespace subtle { - -// This implementation is transitional and maintains the original API for -// atomicops.h. This requires casting memory locations to the atomic types, and -// assumes that the API and the C++11 implementation are layout-compatible, -// which isn't true for all implementations or hardware platforms. The static -// assertion should detect this issue, were it to fire then this header -// shouldn't be used. -// -// TODO(jfb) If this header manages to stay committed then the API should be -// modified, and all call sites updated. -typedef volatile std::atomic* AtomicLocation32; -static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32), - "incompatible 32-bit atomic layout"); - -inline void MemoryBarrier() { -#if defined(__GLIBCXX__) - // Work around libstdc++ bug 51038 where atomic_thread_fence was declared but - // not defined, leading to the linker complaining about undefined references. - __atomic_thread_fence(std::memory_order_seq_cst); -#else - std::atomic_thread_fence(std::memory_order_seq_cst); -#endif -} - -inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - ((AtomicLocation32)ptr) - ->compare_exchange_strong(old_value, - new_value, - std::memory_order_relaxed, - std::memory_order_relaxed); - return old_value; -} - -inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, - Atomic32 new_value) { - return ((AtomicLocation32)ptr) - ->exchange(new_value, std::memory_order_relaxed); -} - -inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - return increment + - ((AtomicLocation32)ptr) - ->fetch_add(increment, std::memory_order_relaxed); -} - -inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - return increment + ((AtomicLocation32)ptr)->fetch_add(increment); -} - -inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - ((AtomicLocation32)ptr) - ->compare_exchange_strong(old_value, - new_value, - std::memory_order_acquire, - std::memory_order_acquire); - return old_value; -} - -inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - ((AtomicLocation32)ptr) - ->compare_exchange_strong(old_value, - new_value, - std::memory_order_release, - std::memory_order_relaxed); - return old_value; -} - -inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { - ((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed); -} - -inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { - ((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed); - MemoryBarrier(); -} - -inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { - ((AtomicLocation32)ptr)->store(value, std::memory_order_release); -} - -inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { - return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed); -} - -inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { - return ((AtomicLocation32)ptr)->load(std::memory_order_acquire); -} - -inline Atomic32 Release_Load(volatile const Atomic32* ptr) { - MemoryBarrier(); - return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed); -} - -#if defined(ARCH_CPU_64_BITS) - -typedef volatile std::atomic* AtomicLocation64; -static_assert(sizeof(*(AtomicLocation64) nullptr) == sizeof(Atomic64), - "incompatible 64-bit atomic layout"); - -inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - ((AtomicLocation64)ptr) - ->compare_exchange_strong(old_value, - new_value, - std::memory_order_relaxed, - std::memory_order_relaxed); - return old_value; -} - -inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, - Atomic64 new_value) { - return ((AtomicLocation64)ptr) - ->exchange(new_value, std::memory_order_relaxed); -} - -inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, - Atomic64 increment) { - return increment + - ((AtomicLocation64)ptr) - ->fetch_add(increment, std::memory_order_relaxed); -} - -inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, - Atomic64 increment) { - return increment + ((AtomicLocation64)ptr)->fetch_add(increment); -} - -inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - ((AtomicLocation64)ptr) - ->compare_exchange_strong(old_value, - new_value, - std::memory_order_acquire, - std::memory_order_acquire); - return old_value; -} - -inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - ((AtomicLocation64)ptr) - ->compare_exchange_strong(old_value, - new_value, - std::memory_order_release, - std::memory_order_relaxed); - return old_value; -} - -inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { - ((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed); -} - -inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { - ((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed); - MemoryBarrier(); -} - -inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { - ((AtomicLocation64)ptr)->store(value, std::memory_order_release); -} - -inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { - return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed); -} - -inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { - return ((AtomicLocation64)ptr)->load(std::memory_order_acquire); -} - -inline Atomic64 Release_Load(volatile const Atomic64* ptr) { - MemoryBarrier(); - return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed); -} - -#endif // defined(ARCH_CPU_64_BITS) -} // namespace subtle -} // namespace base - -#endif // BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ diff --git a/atomicops_internals_x86_msvc.h b/atomicops_internals_x86_msvc.h deleted file mode 100644 index ee9043e96..000000000 --- a/atomicops_internals_x86_msvc.h +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file is an internal atomic implementation, use base/atomicops.h instead. - -#ifndef BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ -#define BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ - -#include "base/win/windows_types.h" - -#include - -#include "base/macros.h" -#include "build/build_config.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 -// X64, undef it, and call its documented -// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx) -// implementation directly. -#undef MemoryBarrier -#endif - -namespace base { -namespace subtle { - -inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - LONG result = _InterlockedCompareExchange( - reinterpret_cast(ptr), - static_cast(new_value), - static_cast(old_value)); - return static_cast(result); -} - -inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, - Atomic32 new_value) { - LONG result = _InterlockedExchange( - reinterpret_cast(ptr), - static_cast(new_value)); - return static_cast(result); -} - -inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - return _InterlockedExchangeAdd( - reinterpret_cast(ptr), - static_cast(increment)) + increment; -} - -inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - return Barrier_AtomicIncrement(ptr, increment); -} - -inline void MemoryBarrier() { -#if defined(ARCH_CPU_64_BITS) - // See #undef and note at the top of this file. - __faststorefence(); -#else - // We use the implementation of MemoryBarrier from WinNT.h - LONG barrier; - - _InterlockedOr(&barrier, 0); -#endif -} - -inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - return NoBarrier_CompareAndSwap(ptr, old_value, new_value); -} - -inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - return NoBarrier_CompareAndSwap(ptr, old_value, new_value); -} - -inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { - *ptr = value; -} - -inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { - NoBarrier_AtomicExchange(ptr, value); - // acts as a barrier in this implementation -} - -inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { - *ptr = value; // works w/o barrier for current Intel chips as of June 2005 - // See comments in Atomic64 version of Release_Store() below. -} - -inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { - return *ptr; -} - -inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { - Atomic32 value = *ptr; - return value; -} - -inline Atomic32 Release_Load(volatile const Atomic32* ptr) { - MemoryBarrier(); - return *ptr; -} - -#if defined(_WIN64) - -// 64-bit low-level operations on 64-bit platform. - -static_assert(sizeof(Atomic64) == sizeof(PVOID), "atomic word is atomic"); - -inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - PVOID result = _InterlockedCompareExchangePointer( - reinterpret_cast(ptr), - reinterpret_cast(new_value), reinterpret_cast(old_value)); - return reinterpret_cast(result); -} - -inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, - Atomic64 new_value) { - PVOID result = - _InterlockedExchangePointer(reinterpret_cast(ptr), - reinterpret_cast(new_value)); - return reinterpret_cast(result); -} - -inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, - Atomic64 increment) { - return _InterlockedExchangeAdd64(reinterpret_cast(ptr), - static_cast(increment)) + - increment; -} - -inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, - Atomic64 increment) { - return Barrier_AtomicIncrement(ptr, increment); -} - -inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { - *ptr = value; -} - -inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { - NoBarrier_AtomicExchange(ptr, value); - // acts as a barrier in this implementation -} - -inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { - *ptr = value; // works w/o barrier for current Intel chips as of June 2005 - - // When new chips come out, check: - // IA-32 Intel Architecture Software Developer's Manual, Volume 3: - // System Programming Guide, Chatper 7: Multiple-processor management, - // Section 7.2, Memory Ordering. - // Last seen at: - // http://developer.intel.com/design/pentium4/manuals/index_new.htm -} - -inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { - return *ptr; -} - -inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { - Atomic64 value = *ptr; - return value; -} - -inline Atomic64 Release_Load(volatile const Atomic64* ptr) { - MemoryBarrier(); - return *ptr; -} - -inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - return NoBarrier_CompareAndSwap(ptr, old_value, new_value); -} - -inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - return NoBarrier_CompareAndSwap(ptr, old_value, new_value); -} - - -#endif // defined(_WIN64) - -} // namespace subtle -} // namespace base - -#endif // BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ diff --git a/atomicops_unittest.cc b/atomicops_unittest.cc deleted file mode 100644 index 729860927..000000000 --- a/atomicops_unittest.cc +++ /dev/null @@ -1,248 +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/atomicops.h" - -#include -#include - -#include "testing/gtest/include/gtest/gtest.h" - -template -static void TestAtomicIncrement() { - // For now, we just test single threaded execution - - // use a guard value to make sure the NoBarrier_AtomicIncrement doesn't go - // outside the expected address bounds. This is in particular to - // test that some future change to the asm code doesn't cause the - // 32-bit NoBarrier_AtomicIncrement doesn't do the wrong thing on 64-bit - // machines. - struct { - AtomicType prev_word; - AtomicType count; - AtomicType next_word; - } s; - - AtomicType prev_word_value, next_word_value; - memset(&prev_word_value, 0xFF, sizeof(AtomicType)); - memset(&next_word_value, 0xEE, sizeof(AtomicType)); - - s.prev_word = prev_word_value; - s.count = 0; - s.next_word = next_word_value; - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 1), 1); - EXPECT_EQ(s.count, 1); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 2), 3); - EXPECT_EQ(s.count, 3); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 3), 6); - EXPECT_EQ(s.count, 6); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -3), 3); - EXPECT_EQ(s.count, 3); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -2), 1); - EXPECT_EQ(s.count, 1); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), 0); - EXPECT_EQ(s.count, 0); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), -1); - EXPECT_EQ(s.count, -1); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -4), -5); - EXPECT_EQ(s.count, -5); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); - - EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 5), 0); - EXPECT_EQ(s.count, 0); - EXPECT_EQ(s.prev_word, prev_word_value); - EXPECT_EQ(s.next_word, next_word_value); -} - - -#define NUM_BITS(T) (sizeof(T) * 8) - - -template -static void TestCompareAndSwap() { - AtomicType value = 0; - AtomicType prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 1); - EXPECT_EQ(1, value); - EXPECT_EQ(0, prev); - - // Verify that CAS will *not* change "value" if it doesn't match the - // expected number. CAS will always return the actual value of the - // variable from before any change. - AtomicType fail = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 2); - EXPECT_EQ(1, value); - EXPECT_EQ(1, fail); - - // Use test value that has non-zero bits in both halves, more for testing - // 64-bit implementation on 32-bit platforms. - const AtomicType k_test_val = (static_cast(1) << - (NUM_BITS(AtomicType) - 2)) + 11; - value = k_test_val; - prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 5); - EXPECT_EQ(k_test_val, value); - EXPECT_EQ(k_test_val, prev); - - value = k_test_val; - prev = base::subtle::NoBarrier_CompareAndSwap(&value, k_test_val, 5); - EXPECT_EQ(5, value); - EXPECT_EQ(k_test_val, prev); -} - - -template -static void TestAtomicExchange() { - AtomicType value = 0; - AtomicType new_value = base::subtle::NoBarrier_AtomicExchange(&value, 1); - EXPECT_EQ(1, value); - EXPECT_EQ(0, new_value); - - // Use test value that has non-zero bits in both halves, more for testing - // 64-bit implementation on 32-bit platforms. - const AtomicType k_test_val = (static_cast(1) << - (NUM_BITS(AtomicType) - 2)) + 11; - value = k_test_val; - new_value = base::subtle::NoBarrier_AtomicExchange(&value, k_test_val); - EXPECT_EQ(k_test_val, value); - EXPECT_EQ(k_test_val, new_value); - - value = k_test_val; - new_value = base::subtle::NoBarrier_AtomicExchange(&value, 5); - EXPECT_EQ(5, value); - EXPECT_EQ(k_test_val, new_value); -} - - -template -static void TestAtomicIncrementBounds() { - // Test at rollover boundary between int_max and int_min - AtomicType test_val = (static_cast(1) << - (NUM_BITS(AtomicType) - 1)); - AtomicType value = -1 ^ test_val; - AtomicType new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1); - EXPECT_EQ(test_val, value); - EXPECT_EQ(value, new_value); - - base::subtle::NoBarrier_AtomicIncrement(&value, -1); - EXPECT_EQ(-1 ^ test_val, value); - - // Test at 32-bit boundary for 64-bit atomic type. - test_val = static_cast(1) << (NUM_BITS(AtomicType) / 2); - value = test_val - 1; - new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1); - EXPECT_EQ(test_val, value); - EXPECT_EQ(value, new_value); - - base::subtle::NoBarrier_AtomicIncrement(&value, -1); - EXPECT_EQ(test_val - 1, value); -} - -// Return an AtomicType with the value 0xa5a5a5.. -template -static AtomicType TestFillValue() { - AtomicType val = 0; - memset(&val, 0xa5, sizeof(AtomicType)); - return val; -} - -// This is a simple sanity check that values are correct. Not testing -// atomicity -template -static void TestStore() { - const AtomicType kVal1 = TestFillValue(); - const AtomicType kVal2 = static_cast(-1); - - AtomicType value; - - base::subtle::NoBarrier_Store(&value, kVal1); - EXPECT_EQ(kVal1, value); - base::subtle::NoBarrier_Store(&value, kVal2); - EXPECT_EQ(kVal2, value); - - base::subtle::Acquire_Store(&value, kVal1); - EXPECT_EQ(kVal1, value); - base::subtle::Acquire_Store(&value, kVal2); - EXPECT_EQ(kVal2, value); - - base::subtle::Release_Store(&value, kVal1); - EXPECT_EQ(kVal1, value); - base::subtle::Release_Store(&value, kVal2); - EXPECT_EQ(kVal2, value); -} - -// This is a simple sanity check that values are correct. Not testing -// atomicity -template -static void TestLoad() { - const AtomicType kVal1 = TestFillValue(); - const AtomicType kVal2 = static_cast(-1); - - AtomicType value; - - value = kVal1; - EXPECT_EQ(kVal1, base::subtle::NoBarrier_Load(&value)); - value = kVal2; - EXPECT_EQ(kVal2, base::subtle::NoBarrier_Load(&value)); - - value = kVal1; - EXPECT_EQ(kVal1, base::subtle::Acquire_Load(&value)); - value = kVal2; - EXPECT_EQ(kVal2, base::subtle::Acquire_Load(&value)); - - value = kVal1; - EXPECT_EQ(kVal1, base::subtle::Release_Load(&value)); - value = kVal2; - EXPECT_EQ(kVal2, base::subtle::Release_Load(&value)); -} - -TEST(AtomicOpsTest, Inc) { - TestAtomicIncrement(); - TestAtomicIncrement(); -} - -TEST(AtomicOpsTest, CompareAndSwap) { - TestCompareAndSwap(); - TestCompareAndSwap(); -} - -TEST(AtomicOpsTest, Exchange) { - TestAtomicExchange(); - TestAtomicExchange(); -} - -TEST(AtomicOpsTest, IncrementBounds) { - TestAtomicIncrementBounds(); - TestAtomicIncrementBounds(); -} - -TEST(AtomicOpsTest, Store) { - TestStore(); - TestStore(); -} - -TEST(AtomicOpsTest, Load) { - TestLoad(); - TestLoad(); -} diff --git a/auto_reset.h b/auto_reset.h deleted file mode 100644 index 8515fe9cd..000000000 --- a/auto_reset.h +++ /dev/null @@ -1,43 +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_AUTO_RESET_H_ -#define BASE_AUTO_RESET_H_ - -#include - -#include "base/macros.h" - -// base::AutoReset<> is useful for setting a variable to a new value only within -// a particular scope. An base::AutoReset<> object resets a variable to its -// original value upon destruction, making it an alternative to writing -// "var = false;" or "var = old_val;" at all of a block's exit points. -// -// This should be obvious, but note that an base::AutoReset<> instance should -// have a shorter lifetime than its scoped_variable, to prevent invalid memory -// writes when the base::AutoReset<> object is destroyed. - -namespace base { - -template -class AutoReset { - public: - AutoReset(T* scoped_variable, T new_value) - : scoped_variable_(scoped_variable), - original_value_(std::move(*scoped_variable)) { - *scoped_variable_ = std::move(new_value); - } - - ~AutoReset() { *scoped_variable_ = std::move(original_value_); } - - private: - T* scoped_variable_; - T original_value_; - - DISALLOW_COPY_AND_ASSIGN(AutoReset); -}; - -} // namespace base - -#endif // BASE_AUTO_RESET_H_ diff --git a/barrier_closure.cc b/barrier_closure.cc deleted file mode 100644 index 4426bb9d3..000000000 --- a/barrier_closure.cc +++ /dev/null @@ -1,51 +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/barrier_closure.h" - -#include - -#include "base/atomic_ref_count.h" -#include "base/bind.h" -#include "base/memory/ptr_util.h" - -namespace base { -namespace { - -// Maintains state for a BarrierClosure. -class BarrierInfo { - public: - BarrierInfo(int num_callbacks_left, OnceClosure done_closure); - void Run(); - - private: - AtomicRefCount num_callbacks_left_; - OnceClosure done_closure_; -}; - -BarrierInfo::BarrierInfo(int num_callbacks, OnceClosure done_closure) - : num_callbacks_left_(num_callbacks), - done_closure_(std::move(done_closure)) {} - -void BarrierInfo::Run() { - DCHECK(!num_callbacks_left_.IsZero()); - if (!num_callbacks_left_.Decrement()) - std::move(done_closure_).Run(); -} - -} // namespace - -RepeatingClosure BarrierClosure(int num_callbacks_left, - OnceClosure done_closure) { - DCHECK_GE(num_callbacks_left, 0); - - if (num_callbacks_left == 0) - std::move(done_closure).Run(); - - return BindRepeating( - &BarrierInfo::Run, - Owned(new BarrierInfo(num_callbacks_left, std::move(done_closure)))); -} - -} // namespace base diff --git a/barrier_closure.h b/barrier_closure.h deleted file mode 100644 index 282aa39b9..000000000 --- a/barrier_closure.h +++ /dev/null @@ -1,28 +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_BARRIER_CLOSURE_H_ -#define BASE_BARRIER_CLOSURE_H_ - -#include "base/base_export.h" -#include "base/callback.h" - -namespace base { - -// BarrierClosure executes |done_closure| after it has been invoked -// |num_closures| times. -// -// If |num_closures| is 0, |done_closure| is executed immediately. -// -// BarrierClosure is thread-safe - the count of remaining closures is -// maintained as a base::AtomicRefCount. |done_closure| will be run on -// the thread that calls the final Run() on the returned closures. -// -// |done_closure| is also cleared on the final calling thread. -BASE_EXPORT RepeatingClosure BarrierClosure(int num_closures, - OnceClosure done_closure); - -} // namespace base - -#endif // BASE_BARRIER_CLOSURE_H_ diff --git a/barrier_closure_unittest.cc b/barrier_closure_unittest.cc deleted file mode 100644 index 819f6ac2c..000000000 --- a/barrier_closure_unittest.cc +++ /dev/null @@ -1,81 +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/barrier_closure.h" - -#include "base/bind.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -void Increment(int* count) { (*count)++; } - -TEST(BarrierClosureTest, RunImmediatelyForZeroClosures) { - int count = 0; - base::Closure done_closure(base::Bind(&Increment, base::Unretained(&count))); - - base::Closure barrier_closure = base::BarrierClosure(0, done_closure); - EXPECT_EQ(1, count); -} - -TEST(BarrierClosureTest, RunAfterNumClosures) { - int count = 0; - base::Closure done_closure(base::Bind(&Increment, base::Unretained(&count))); - - base::Closure barrier_closure = base::BarrierClosure(2, done_closure); - EXPECT_EQ(0, count); - - barrier_closure.Run(); - EXPECT_EQ(0, count); - - barrier_closure.Run(); - EXPECT_EQ(1, count); -} - -class DestructionIndicator { - public: - // Sets |*destructed| to true in destructor. - DestructionIndicator(bool* destructed) : destructed_(destructed) { - *destructed_ = false; - } - - ~DestructionIndicator() { *destructed_ = true; } - - void DoNothing() {} - - private: - bool* destructed_; -}; - -TEST(BarrierClosureTest, ReleasesDoneClosureWhenDone) { - bool done_destructed = false; - base::Closure barrier_closure = base::BarrierClosure( - 1, - base::BindOnce(&DestructionIndicator::DoNothing, - base::Owned(new DestructionIndicator(&done_destructed)))); - EXPECT_FALSE(done_destructed); - barrier_closure.Run(); - EXPECT_TRUE(done_destructed); -} - -void ResetBarrierClosure(base::Closure* closure) { - *closure = base::Closure(); -} - -// Tests a case when |done_closure| resets a |barrier_closure|. -// |barrier_closure| is a Closure holding the |done_closure|. |done_closure| -// holds a pointer back to the |barrier_closure|. When |barrier_closure| is -// Run() it calls ResetBarrierClosure() which erases the |barrier_closure| while -// still inside of its Run(). The Run() implementation (in base::BarrierClosure) -// must not try use itself after executing ResetBarrierClosure() or this test -// would crash inside Run(). -TEST(BarrierClosureTest, KeepingClosureAliveUntilDone) { - base::Closure barrier_closure; - base::Closure done_closure = - base::Bind(ResetBarrierClosure, &barrier_closure); - barrier_closure = base::BarrierClosure(1, done_closure); - barrier_closure.Run(); -} - -} // namespace diff --git a/base64.cc b/base64.cc deleted file mode 100644 index ca8ee9390..000000000 --- a/base64.cc +++ /dev/null @@ -1,39 +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/base64.h" - -#include - -#include "third_party/modp_b64/modp_b64.h" - -namespace base { - -void Base64Encode(const StringPiece& input, std::string* output) { - std::string temp; - temp.resize(modp_b64_encode_len(input.size())); // makes room for null byte - - // modp_b64_encode_len() returns at least 1, so temp[0] is safe to use. - size_t output_size = modp_b64_encode(&(temp[0]), input.data(), input.size()); - - temp.resize(output_size); // strips off null byte - output->swap(temp); -} - -bool Base64Decode(const StringPiece& input, std::string* output) { - std::string temp; - temp.resize(modp_b64_decode_len(input.size())); - - // does not null terminate result since result is binary data! - size_t input_size = input.size(); - size_t output_size = modp_b64_decode(&(temp[0]), input.data(), input_size); - if (output_size == MODP_B64_ERROR) - return false; - - temp.resize(output_size); - output->swap(temp); - return true; -} - -} // namespace base diff --git a/base64.h b/base64.h deleted file mode 100644 index dd72c39a2..000000000 --- a/base64.h +++ /dev/null @@ -1,25 +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_BASE64_H_ -#define BASE_BASE64_H_ - -#include - -#include "base/base_export.h" -#include "base/strings/string_piece.h" - -namespace base { - -// Encodes the input string in base64. The encoding can be done in-place. -BASE_EXPORT void Base64Encode(const StringPiece& input, std::string* output); - -// Decodes the base64 input string. Returns true if successful and false -// otherwise. The output string is only modified if successful. The decoding can -// be done in-place. -BASE_EXPORT bool Base64Decode(const StringPiece& input, std::string* output); - -} // namespace base - -#endif // BASE_BASE64_H_ diff --git a/base64_decode_fuzzer.cc b/base64_decode_fuzzer.cc deleted file mode 100644 index 3716f727c..000000000 --- a/base64_decode_fuzzer.cc +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017 The Chromium 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 - -#include "base/base64.h" -#include "base/strings/string_piece.h" - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - std::string decode_output; - base::StringPiece data_piece(reinterpret_cast(data), size); - base::Base64Decode(data_piece, &decode_output); - return 0; -} diff --git a/base64_encode_fuzzer.cc b/base64_encode_fuzzer.cc deleted file mode 100644 index c324be08b..000000000 --- a/base64_encode_fuzzer.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2017 The Chromium 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 - -#include "base/base64.h" -#include "base/logging.h" -#include "base/strings/string_piece.h" - -// Encode some random data, and then decode it. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - std::string encode_output; - std::string decode_output; - base::StringPiece data_piece(reinterpret_cast(data), size); - base::Base64Encode(data_piece, &encode_output); - CHECK(base::Base64Decode(encode_output, &decode_output)); - CHECK_EQ(data_piece, decode_output); - return 0; -} diff --git a/base64_unittest.cc b/base64_unittest.cc deleted file mode 100644 index 91651f4d3..000000000 --- a/base64_unittest.cc +++ /dev/null @@ -1,40 +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/base64.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(Base64Test, Basic) { - const std::string kText = "hello world"; - const std::string kBase64Text = "aGVsbG8gd29ybGQ="; - - std::string encoded; - std::string decoded; - bool ok; - - Base64Encode(kText, &encoded); - EXPECT_EQ(kBase64Text, encoded); - - ok = Base64Decode(encoded, &decoded); - EXPECT_TRUE(ok); - EXPECT_EQ(kText, decoded); -} - -TEST(Base64Test, InPlace) { - const std::string kText = "hello world"; - const std::string kBase64Text = "aGVsbG8gd29ybGQ="; - std::string text(kText); - - Base64Encode(text, &text); - EXPECT_EQ(kBase64Text, text); - - bool ok = Base64Decode(text, &text); - EXPECT_TRUE(ok); - EXPECT_EQ(text, kText); -} - -} // namespace base diff --git a/base64url.cc b/base64url.cc deleted file mode 100644 index 0a2c04511..000000000 --- a/base64url.cc +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/base64url.h" - -#include - -#include "base/base64.h" -#include "base/macros.h" -#include "base/numerics/safe_math.h" -#include "base/strings/string_util.h" -#include "third_party/modp_b64/modp_b64.h" - -namespace base { - -const char kPaddingChar = '='; - -// Base64url maps {+, /} to {-, _} in order for the encoded content to be safe -// to use in a URL. These characters will be translated by this implementation. -const char kBase64Chars[] = "+/"; -const char kBase64UrlSafeChars[] = "-_"; - -void Base64UrlEncode(const StringPiece& input, - Base64UrlEncodePolicy policy, - std::string* output) { - Base64Encode(input, output); - - ReplaceChars(*output, "+", "-", output); - ReplaceChars(*output, "/", "_", output); - - switch (policy) { - case Base64UrlEncodePolicy::INCLUDE_PADDING: - // The padding included in |*output| will not be amended. - break; - case Base64UrlEncodePolicy::OMIT_PADDING: - // The padding included in |*output| will be removed. - const size_t last_non_padding_pos = - output->find_last_not_of(kPaddingChar); - if (last_non_padding_pos != std::string::npos) - output->resize(last_non_padding_pos + 1); - - break; - } -} - -bool Base64UrlDecode(const StringPiece& input, - Base64UrlDecodePolicy policy, - std::string* output) { - // Characters outside of the base64url alphabet are disallowed, which includes - // the {+, /} characters found in the conventional base64 alphabet. - if (input.find_first_of(kBase64Chars) != std::string::npos) - return false; - - const size_t required_padding_characters = input.size() % 4; - const bool needs_replacement = - input.find_first_of(kBase64UrlSafeChars) != std::string::npos; - - switch (policy) { - case Base64UrlDecodePolicy::REQUIRE_PADDING: - // Fail if the required padding is not included in |input|. - if (required_padding_characters > 0) - return false; - break; - case Base64UrlDecodePolicy::IGNORE_PADDING: - // Missing padding will be silently appended. - break; - case Base64UrlDecodePolicy::DISALLOW_PADDING: - // Fail if padding characters are included in |input|. - if (input.find_first_of(kPaddingChar) != std::string::npos) - return false; - break; - } - - // If the string either needs replacement of URL-safe characters to normal - // base64 ones, or additional padding, a copy of |input| needs to be made in - // order to make these adjustments without side effects. - if (required_padding_characters > 0 || needs_replacement) { - std::string base64_input; - - CheckedNumeric base64_input_size = input.size(); - if (required_padding_characters > 0) - base64_input_size += 4 - required_padding_characters; - - base64_input.reserve(base64_input_size.ValueOrDie()); - input.AppendToString(&base64_input); - - // Substitute the base64url URL-safe characters to their base64 equivalents. - ReplaceChars(base64_input, "-", "+", &base64_input); - ReplaceChars(base64_input, "_", "/", &base64_input); - - // Append the necessary padding characters. - base64_input.resize(base64_input_size.ValueOrDie(), '='); - - return Base64Decode(base64_input, output); - } - - return Base64Decode(input, output); -} - -} // namespace base diff --git a/base64url.h b/base64url.h deleted file mode 100644 index 66a482461..000000000 --- a/base64url.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_BASE64URL_H_ -#define BASE_BASE64URL_H_ - -#include - -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" - -namespace base { - -enum class Base64UrlEncodePolicy { - // Include the trailing padding in the output, when necessary. - INCLUDE_PADDING, - - // Remove the trailing padding from the output. - OMIT_PADDING -}; - -// Encodes the |input| string in base64url, defined in RFC 4648: -// https://tools.ietf.org/html/rfc4648#section-5 -// -// The |policy| defines whether padding should be included or omitted from the -// encoded |*output|. |input| and |*output| may reference the same storage. -BASE_EXPORT void Base64UrlEncode(const StringPiece& input, - Base64UrlEncodePolicy policy, - std::string* output); - -enum class Base64UrlDecodePolicy { - // Require inputs contain trailing padding if non-aligned. - REQUIRE_PADDING, - - // Accept inputs regardless of whether or not they have the correct padding. - IGNORE_PADDING, - - // Reject inputs if they contain any trailing padding. - DISALLOW_PADDING -}; - -// Decodes the |input| string in base64url, defined in RFC 4648: -// https://tools.ietf.org/html/rfc4648#section-5 -// -// The |policy| defines whether padding will be required, ignored or disallowed -// altogether. |input| and |*output| may reference the same storage. -BASE_EXPORT bool Base64UrlDecode(const StringPiece& input, - Base64UrlDecodePolicy policy, - std::string* output) WARN_UNUSED_RESULT; - -} // namespace base - -#endif // BASE_BASE64URL_H_ diff --git a/base64url_unittest.cc b/base64url_unittest.cc deleted file mode 100644 index 45aa4a8c5..000000000 --- a/base64url_unittest.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/base64url.h" - -#include "base/macros.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -TEST(Base64UrlTest, EncodeIncludePaddingPolicy) { - std::string output; - Base64UrlEncode("hello?world", Base64UrlEncodePolicy::INCLUDE_PADDING, - &output); - - // Base64 version: aGVsbG8/d29ybGQ= - EXPECT_EQ("aGVsbG8_d29ybGQ=", output); - - // Test for behavior for very short and empty strings. - Base64UrlEncode("??", Base64UrlEncodePolicy::INCLUDE_PADDING, &output); - EXPECT_EQ("Pz8=", output); - - Base64UrlEncode("", Base64UrlEncodePolicy::INCLUDE_PADDING, &output); - EXPECT_EQ("", output); -} - -TEST(Base64UrlTest, EncodeOmitPaddingPolicy) { - std::string output; - Base64UrlEncode("hello?world", Base64UrlEncodePolicy::OMIT_PADDING, &output); - - // base64 version: aGVsbG8/d29ybGQ= - EXPECT_EQ("aGVsbG8_d29ybGQ", output); - - // Test for behavior for very short and empty strings. - Base64UrlEncode("??", Base64UrlEncodePolicy::OMIT_PADDING, &output); - EXPECT_EQ("Pz8", output); - - Base64UrlEncode("", Base64UrlEncodePolicy::OMIT_PADDING, &output); - EXPECT_EQ("", output); -} - -TEST(Base64UrlTest, DecodeRequirePaddingPolicy) { - std::string output; - ASSERT_TRUE(Base64UrlDecode("aGVsbG8_d29ybGQ=", - Base64UrlDecodePolicy::REQUIRE_PADDING, &output)); - - EXPECT_EQ("hello?world", output); - - ASSERT_FALSE(Base64UrlDecode( - "aGVsbG8_d29ybGQ", Base64UrlDecodePolicy::REQUIRE_PADDING, &output)); - - // Test for behavior for very short and empty strings. - ASSERT_TRUE( - Base64UrlDecode("Pz8=", Base64UrlDecodePolicy::REQUIRE_PADDING, &output)); - EXPECT_EQ("??", output); - - ASSERT_TRUE( - Base64UrlDecode("", Base64UrlDecodePolicy::REQUIRE_PADDING, &output)); - EXPECT_EQ("", output); -} - -TEST(Base64UrlTest, DecodeIgnorePaddingPolicy) { - std::string output; - ASSERT_TRUE(Base64UrlDecode("aGVsbG8_d29ybGQ", - Base64UrlDecodePolicy::IGNORE_PADDING, &output)); - - EXPECT_EQ("hello?world", output); - - // Including the padding is accepted as well. - ASSERT_TRUE(Base64UrlDecode("aGVsbG8_d29ybGQ=", - Base64UrlDecodePolicy::IGNORE_PADDING, &output)); - - EXPECT_EQ("hello?world", output); -} - -TEST(Base64UrlTest, DecodeDisallowPaddingPolicy) { - std::string output; - ASSERT_FALSE(Base64UrlDecode( - "aGVsbG8_d29ybGQ=", Base64UrlDecodePolicy::DISALLOW_PADDING, &output)); - - // The policy will allow the input when padding has been omitted. - ASSERT_TRUE(Base64UrlDecode( - "aGVsbG8_d29ybGQ", Base64UrlDecodePolicy::DISALLOW_PADDING, &output)); - - EXPECT_EQ("hello?world", output); -} - -TEST(Base64UrlTest, DecodeDisallowsBase64Alphabet) { - std::string output; - - // The "/" character is part of the conventional base64 alphabet, but has been - // substituted with "_" in the base64url alphabet. - ASSERT_FALSE(Base64UrlDecode( - "aGVsbG8/d29ybGQ=", Base64UrlDecodePolicy::REQUIRE_PADDING, &output)); -} - -TEST(Base64UrlTest, DecodeDisallowsPaddingOnly) { - std::string output; - - ASSERT_FALSE(Base64UrlDecode( - "=", Base64UrlDecodePolicy::IGNORE_PADDING, &output)); - ASSERT_FALSE(Base64UrlDecode( - "==", Base64UrlDecodePolicy::IGNORE_PADDING, &output)); - ASSERT_FALSE(Base64UrlDecode( - "===", Base64UrlDecodePolicy::IGNORE_PADDING, &output)); - ASSERT_FALSE(Base64UrlDecode( - "====", Base64UrlDecodePolicy::IGNORE_PADDING, &output)); -} - -} // namespace - -} // namespace base diff --git a/base_export.h b/base_export.h deleted file mode 100644 index cf7ebd781..000000000 --- a/base_export.h +++ /dev/null @@ -1,29 +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_BASE_EXPORT_H_ -#define BASE_BASE_EXPORT_H_ - -#if defined(COMPONENT_BUILD) -#if defined(WIN32) - -#if defined(BASE_IMPLEMENTATION) -#define BASE_EXPORT __declspec(dllexport) -#else -#define BASE_EXPORT __declspec(dllimport) -#endif // defined(BASE_IMPLEMENTATION) - -#else // defined(WIN32) -#if defined(BASE_IMPLEMENTATION) -#define BASE_EXPORT __attribute__((visibility("default"))) -#else -#define BASE_EXPORT -#endif // defined(BASE_IMPLEMENTATION) -#endif - -#else // defined(COMPONENT_BUILD) -#define BASE_EXPORT -#endif - -#endif // BASE_BASE_EXPORT_H_ diff --git a/base_paths.cc b/base_paths.cc deleted file mode 100644 index e3f322e72..000000000 --- a/base_paths.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/base_paths.h" - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/path_service.h" - -namespace base { - -bool PathProvider(int key, FilePath* result) { - // NOTE: DIR_CURRENT is a special case in PathService::Get - - switch (key) { - case DIR_EXE: - if (!PathService::Get(FILE_EXE, result)) - return false; - *result = result->DirName(); - return true; - case DIR_MODULE: - if (!PathService::Get(FILE_MODULE, result)) - return false; - *result = result->DirName(); - return true; - case DIR_ASSETS: - return PathService::Get(DIR_MODULE, result); - case DIR_TEMP: - return GetTempDir(result); - case base::DIR_HOME: - *result = GetHomeDir(); - return true; - case DIR_TEST_DATA: { - FilePath test_data_path; - if (!PathService::Get(DIR_SOURCE_ROOT, &test_data_path)) - return false; - test_data_path = test_data_path.Append(FILE_PATH_LITERAL("base")); - test_data_path = test_data_path.Append(FILE_PATH_LITERAL("test")); - test_data_path = test_data_path.Append(FILE_PATH_LITERAL("data")); - if (!PathExists(test_data_path)) // We don't want to create this. - return false; - *result = test_data_path; - return true; - } - default: - return false; - } -} - -} // namespace base diff --git a/base_paths.h b/base_paths.h deleted file mode 100644 index 2a163f48d..000000000 --- a/base_paths.h +++ /dev/null @@ -1,55 +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_BASE_PATHS_H_ -#define BASE_BASE_PATHS_H_ - -// This file declares path keys for the base module. These can be used with -// the PathService to access various special directories and files. - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/base_paths_win.h" -#elif defined(OS_MACOSX) -#include "base/base_paths_mac.h" -#elif defined(OS_ANDROID) -#include "base/base_paths_android.h" -#endif - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include "base/base_paths_posix.h" -#endif - -namespace base { - -enum BasePathKey { - PATH_START = 0, - - DIR_CURRENT, // Current directory. - DIR_EXE, // Directory containing FILE_EXE. - DIR_MODULE, // Directory containing FILE_MODULE. - DIR_ASSETS, // Directory that contains application assets. - DIR_TEMP, // Temporary directory. - DIR_HOME, // User's root home directory. On Windows this will look - // like "C:\Users\" 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 - // PathService were compiled into a shared object, for - // example). - DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful - // for tests that need to locate various resources. It - // should not be used outside of test code. - DIR_USER_DESKTOP, // The current user's Desktop. - - DIR_TEST_DATA, // Used only for testing. - - PATH_END -}; - -} // namespace base - -#endif // BASE_BASE_PATHS_H_ diff --git a/base_paths_android.cc b/base_paths_android.cc deleted file mode 100644 index 078f565a5..000000000 --- a/base_paths_android.cc +++ /dev/null @@ -1,66 +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. - -// Defines base::PathProviderAndroid which replaces base::PathProviderPosix for -// Android in base/path_service.cc. - -#include -#include - -#include "base/android/jni_android.h" -#include "base/android/path_utils.h" -#include "base/base_paths.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/process/process_metrics.h" - -namespace base { - -bool PathProviderAndroid(int key, FilePath* result) { - switch (key) { - case base::FILE_EXE: { - FilePath bin_dir; - if (!ReadSymbolicLink(FilePath(kProcSelfExe), &bin_dir)) { - NOTREACHED() << "Unable to resolve " << kProcSelfExe << "."; - return false; - } - *result = bin_dir; - return true; - } - case base::FILE_MODULE: - // dladdr didn't work in Android as only the file name was returned. - NOTIMPLEMENTED(); - return false; - case base::DIR_MODULE: - return base::android::GetNativeLibraryDirectory(result); - case base::DIR_SOURCE_ROOT: - // Used only by tests. - // In that context, hooked up via base/test/test_support_android.cc. - NOTIMPLEMENTED(); - return false; - case base::DIR_USER_DESKTOP: - // Android doesn't support GetUserDesktop. - NOTIMPLEMENTED(); - return false; - case base::DIR_CACHE: - return base::android::GetCacheDirectory(result); - case base::DIR_ASSETS: - // On Android assets are normally loaded from the APK using - // base::android::OpenApkAsset(). In tests, since the assets are no - // packaged, DIR_ASSETS is overridden to point to the build directory. - return false; - case base::DIR_ANDROID_APP_DATA: - return base::android::GetDataDirectory(result); - case base::DIR_ANDROID_EXTERNAL_STORAGE: - return base::android::GetExternalStorageDirectory(result); - default: - // Note: the path system expects this function to override the default - // behavior. So no need to log an error if we don't support a given - // path. The system will just use the default. - return false; - } -} - -} // namespace base diff --git a/base_paths_android.h b/base_paths_android.h deleted file mode 100644 index 7a9ac4a67..000000000 --- a/base_paths_android.h +++ /dev/null @@ -1,25 +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_BASE_PATHS_ANDROID_H_ -#define BASE_BASE_PATHS_ANDROID_H_ - -// This file declares Android-specific path keys for the base module. -// These can be used with the PathService to access various special -// directories and files. - -namespace base { - -enum { - PATH_ANDROID_START = 300, - - DIR_ANDROID_APP_DATA, // Directory where to put Android app's data. - DIR_ANDROID_EXTERNAL_STORAGE, // Android external storage directory. - - PATH_ANDROID_END -}; - -} // namespace base - -#endif // BASE_BASE_PATHS_ANDROID_H_ diff --git a/base_paths_fuchsia.cc b/base_paths_fuchsia.cc deleted file mode 100644 index 6b0c9216f..000000000 --- a/base_paths_fuchsia.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2017 The Chromium 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_paths.h" - -#include - -#include "base/base_paths_fuchsia.h" -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/path_service.h" -#include "base/process/process.h" - -namespace base { - -base::FilePath GetPackageRoot() { - return base::FilePath("/pkg"); -} - -bool PathProviderFuchsia(int key, FilePath* result) { - switch (key) { - case FILE_MODULE: - NOTIMPLEMENTED(); - return false; - case FILE_EXE: - *result = CommandLine::ForCurrentProcess()->GetProgram(); - return true; - case DIR_APP_DATA: - // TODO(https://crbug.com/840598): Switch to /data when minfs supports - // mmap(). - DLOG(WARNING) << "Using /tmp as app data dir, changes will NOT be " - "persisted! (crbug.com/840598)"; - *result = FilePath("/tmp"); - return true; - case DIR_CACHE: - *result = FilePath("/data"); - return true; - case DIR_ASSETS: - case DIR_SOURCE_ROOT: - *result = GetPackageRoot(); - return true; - } - return false; -} - -} // namespace base diff --git a/base_paths_fuchsia.h b/base_paths_fuchsia.h deleted file mode 100644 index a2d419433..000000000 --- a/base_paths_fuchsia.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2018 The Chromium 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_BASE_PATHS_FUCHSIA_H_ -#define BASE_BASE_PATHS_FUCHSIA_H_ - -#include "base/base_export.h" -#include "base/files/file_path.h" - -namespace base { - -// These can be used with the PathService to access various special -// directories and files. -enum { - PATH_FUCHSIA_START = 1200, - - // Path to the directory which contains application libraries and resources. - DIR_FUCHSIA_RESOURCES, - - // Path to the directory which contains application user data. - DIR_APP_DATA, - - PATH_FUCHSIA_END, -}; - -// If running inside a package, returns a FilePath of the root path -// of the currently deployed package. -// Otherwise returns an empty FilePath. -BASE_EXPORT base::FilePath GetPackageRoot(); - -} // namespace base - -#endif // BASE_BASE_PATHS_FUCHSIA_H_ diff --git a/base_paths_mac.h b/base_paths_mac.h deleted file mode 100644 index ac75402f3..000000000 --- a/base_paths_mac.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_BASE_PATHS_MAC_H_ -#define BASE_BASE_PATHS_MAC_H_ - -// This file declares Mac-specific path keys for the base module. -// These can be used with the PathService to access various special -// directories and files. - -namespace base { - -enum { - PATH_MAC_START = 200, - - DIR_APP_DATA, // ~/Library/Application Support - - PATH_MAC_END -}; - -} // namespace base - -#endif // BASE_BASE_PATHS_MAC_H_ diff --git a/base_paths_mac.mm b/base_paths_mac.mm deleted file mode 100644 index 46bbd16b0..000000000 --- a/base_paths_mac.mm +++ /dev/null @@ -1,127 +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. - -// Defines base::PathProviderMac which replaces base::PathProviderPosix for Mac -// in base/path_service.cc. - -#include -#import -#include -#include - -#include "base/base_paths.h" -#include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/mac/bundle_locations.h" -#include "base/mac/foundation_util.h" -#include "base/path_service.h" -#include "base/strings/string_util.h" -#include "base/threading/thread_restrictions.h" -#include "build/build_config.h" - -namespace { - -void GetNSExecutablePath(base::FilePath* path) { - DCHECK(path); - // Executable path can have relative references ("..") depending on - // how the app was launched. - uint32_t executable_length = 0; - _NSGetExecutablePath(NULL, &executable_length); - DCHECK_GT(executable_length, 1u); - std::string executable_path; - int rv = _NSGetExecutablePath( - base::WriteInto(&executable_path, executable_length), - &executable_length); - DCHECK_EQ(rv, 0); - - // _NSGetExecutablePath may return paths containing ./ or ../ which makes - // FilePath::DirName() work incorrectly, convert it to absolute path so that - // paths such as DIR_SOURCE_ROOT can work, since we expect absolute paths to - // be returned here. - // TODO(bauerb): http://crbug.com/259796, http://crbug.com/373477 - base::ThreadRestrictions::ScopedAllowIO allow_io; - *path = base::MakeAbsoluteFilePath(base::FilePath(executable_path)); -} - -// Returns true if the module for |address| is found. |path| will contain -// the path to the module. Note that |path| may not be absolute. -bool GetModulePathForAddress(base::FilePath* path, - const void* address) WARN_UNUSED_RESULT; - -bool GetModulePathForAddress(base::FilePath* path, const void* address) { - Dl_info info; - if (dladdr(address, &info) == 0) - return false; - *path = base::FilePath(info.dli_fname); - return true; -} - -} // namespace - -namespace base { - -bool PathProviderMac(int key, base::FilePath* result) { - switch (key) { - case base::FILE_EXE: - GetNSExecutablePath(result); - return true; - case base::FILE_MODULE: - return GetModulePathForAddress(result, - reinterpret_cast(&base::PathProviderMac)); - case base::DIR_APP_DATA: { - bool success = base::mac::GetUserDirectory(NSApplicationSupportDirectory, - result); -#if defined(OS_IOS) - // On IOS, this directory does not exist unless it is created explicitly. - if (success && !base::PathExists(*result)) - success = base::CreateDirectory(*result); -#endif // defined(OS_IOS) - return success; - } - case base::DIR_SOURCE_ROOT: - // Go through PathService to catch overrides. - if (!PathService::Get(base::FILE_EXE, result)) - return false; - - // Start with the executable's directory. - *result = result->DirName(); - -#if !defined(OS_IOS) - if (base::mac::AmIBundled()) { - // The bundled app executables (Chromium, TestShell, etc) live five - // levels down, eg: - // src/xcodebuild/{Debug|Release}/Chromium.app/Contents/MacOS/Chromium - *result = result->DirName().DirName().DirName().DirName().DirName(); - } else { - // Unit tests execute two levels deep from the source root, eg: - // src/xcodebuild/{Debug|Release}/base_unittests - *result = result->DirName().DirName(); - } -#endif - return true; - case base::DIR_USER_DESKTOP: -#if defined(OS_IOS) - // iOS does not have desktop directories. - NOTIMPLEMENTED(); - return false; -#else - return base::mac::GetUserDirectory(NSDesktopDirectory, result); -#endif - case base::DIR_ASSETS: - if (!base::mac::AmIBundled()) { - return PathService::Get(base::DIR_MODULE, result); - } - *result = base::mac::FrameworkBundlePath().Append( - FILE_PATH_LITERAL("Resources")); - return true; - case base::DIR_CACHE: - return base::mac::GetUserDirectory(NSCachesDirectory, result); - default: - return false; - } -} - -} // namespace base diff --git a/base_paths_posix.cc b/base_paths_posix.cc deleted file mode 100644 index 00a15696c..000000000 --- a/base_paths_posix.cc +++ /dev/null @@ -1,119 +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. - -// Defines base::PathProviderPosix, default path provider on POSIX OSes that -// don't have their own base_paths_OS.cc implementation (i.e. all but Mac and -// Android). - -#include "base/base_paths.h" - -#include -#include - -#include -#include -#include - -#include "base/environment.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/nix/xdg_util.h" -#include "base/path_service.h" -#include "base/process/process_metrics.h" -#include "build/build_config.h" - -#if defined(OS_FREEBSD) -#include -#include -#elif defined(OS_SOLARIS) || defined(OS_AIX) -#include -#endif - -namespace base { - -bool PathProviderPosix(int key, FilePath* result) { - switch (key) { - case FILE_EXE: - case FILE_MODULE: { // TODO(evanm): is this correct? -#if defined(OS_LINUX) - FilePath bin_dir; - if (!ReadSymbolicLink(FilePath(kProcSelfExe), &bin_dir)) { - NOTREACHED() << "Unable to resolve " << kProcSelfExe << "."; - return false; - } - *result = bin_dir; - return true; -#elif defined(OS_FREEBSD) - int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; - char bin_dir[PATH_MAX + 1]; - size_t length = sizeof(bin_dir); - // Upon return, |length| is the number of bytes written to |bin_dir| - // including the string terminator. - int error = sysctl(name, 4, bin_dir, &length, NULL, 0); - if (error < 0 || length <= 1) { - NOTREACHED() << "Unable to resolve path."; - return false; - } - *result = FilePath(FilePath::StringType(bin_dir, length - 1)); - return true; -#elif defined(OS_SOLARIS) - char bin_dir[PATH_MAX + 1]; - if (realpath(getexecname(), bin_dir) == NULL) { - NOTREACHED() << "Unable to resolve " << getexecname() << "."; - return false; - } - *result = FilePath(bin_dir); - return true; -#elif defined(OS_OPENBSD) || defined(OS_AIX) - // There is currently no way to get the executable path on OpenBSD - char* cpath; - if ((cpath = getenv("CHROME_EXE_PATH")) != NULL) - *result = FilePath(cpath); - else - *result = FilePath("/usr/local/chrome/chrome"); - return true; -#endif - } - case DIR_SOURCE_ROOT: { - // Allow passing this in the environment, for more flexibility in build - // tree configurations (sub-project builds, gyp --output_dir, etc.) - std::unique_ptr env(Environment::Create()); - std::string cr_source_root; - FilePath path; - if (env->GetVar("CR_SOURCE_ROOT", &cr_source_root)) { - path = FilePath(cr_source_root); - if (PathExists(path)) { - *result = path; - return true; - } - DLOG(WARNING) << "CR_SOURCE_ROOT is set, but it appears to not " - << "point to a directory."; - } - // On POSIX, unit tests execute two levels deep from the source root. - // For example: out/{Debug|Release}/net_unittest - if (PathService::Get(DIR_EXE, &path)) { - *result = path.DirName().DirName(); - return true; - } - - DLOG(ERROR) << "Couldn't find your source root. " - << "Try running from your chromium/src directory."; - return false; - } - case DIR_USER_DESKTOP: - *result = nix::GetXDGUserDirectory("DESKTOP", "Desktop"); - return true; - case DIR_CACHE: { - std::unique_ptr env(Environment::Create()); - FilePath cache_dir( - nix::GetXDGDirectory(env.get(), "XDG_CACHE_HOME", ".cache")); - *result = cache_dir; - return true; - } - } - return false; -} - -} // namespace base diff --git a/base_paths_posix.h b/base_paths_posix.h deleted file mode 100644 index ef002aeb0..000000000 --- a/base_paths_posix.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_BASE_PATHS_POSIX_H_ -#define BASE_BASE_PATHS_POSIX_H_ - -// This file declares windows-specific path keys for the base module. -// These can be used with the PathService to access various special -// directories and files. - -namespace base { - -enum { - PATH_POSIX_START = 400, - - DIR_CACHE, // Directory where to put cache data. Note this is - // *not* where the browser cache lives, but the - // browser cache can be a subdirectory. - // This is $XDG_CACHE_HOME on Linux and - // ~/Library/Caches on Mac. - PATH_POSIX_END -}; - -} // namespace base - -#endif // BASE_BASE_PATHS_POSIX_H_ diff --git a/base_paths_win.cc b/base_paths_win.cc deleted file mode 100644 index e7e5d1f95..000000000 --- a/base_paths_win.cc +++ /dev/null @@ -1,202 +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 -#include -#include - -#include "base/base_paths.h" -#include "base/environment.h" -#include "base/files/file_path.h" -#include "base/path_service.h" -#include "base/strings/utf_string_conversions.h" -#include "base/win/current_module.h" -#include "base/win/scoped_co_mem.h" -#include "base/win/windows_version.h" - -using base::FilePath; - -namespace base { - -bool PathProviderWin(int key, FilePath* result) { - // We need to go compute the value. It would be nice to support paths with - // names longer than MAX_PATH, but the system functions don't seem to be - // designed for it either, with the exception of GetTempPath (but other - // things will surely break if the temp path is too long, so we don't bother - // handling it. - wchar_t system_buffer[MAX_PATH]; - system_buffer[0] = 0; - - FilePath cur; - switch (key) { - case base::FILE_EXE: - if (GetModuleFileName(NULL, system_buffer, MAX_PATH) == 0) - return false; - cur = FilePath(system_buffer); - break; - case base::FILE_MODULE: { - // the resource containing module is assumed to be the one that - // this code lives in, whether that's a dll or exe - if (GetModuleFileName(CURRENT_MODULE(), system_buffer, MAX_PATH) == 0) - return false; - cur = FilePath(system_buffer); - break; - } - case base::DIR_WINDOWS: - GetWindowsDirectory(system_buffer, MAX_PATH); - cur = FilePath(system_buffer); - break; - case base::DIR_SYSTEM: - GetSystemDirectory(system_buffer, MAX_PATH); - cur = FilePath(system_buffer); - break; - case base::DIR_PROGRAM_FILESX86: - if (base::win::OSInfo::GetInstance()->architecture() != - base::win::OSInfo::X86_ARCHITECTURE) { - if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILESX86, NULL, - SHGFP_TYPE_CURRENT, system_buffer))) - return false; - cur = FilePath(system_buffer); - break; - } - // Fall through to base::DIR_PROGRAM_FILES if we're on an X86 machine. - FALLTHROUGH; - case base::DIR_PROGRAM_FILES: - if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, - SHGFP_TYPE_CURRENT, system_buffer))) - return false; - cur = FilePath(system_buffer); - break; - case base::DIR_PROGRAM_FILES6432: -#if !defined(_WIN64) - if (base::win::OSInfo::GetInstance()->wow64_status() == - base::win::OSInfo::WOW64_ENABLED) { - std::unique_ptr env(base::Environment::Create()); - std::string programfiles_w6432; - // 32-bit process running in WOW64 sets ProgramW6432 environment - // variable. See - // https://msdn.microsoft.com/library/windows/desktop/aa384274.aspx. - if (!env->GetVar("ProgramW6432", &programfiles_w6432)) - return false; - // GetVar returns UTF8 - convert back to Wide. - cur = FilePath(UTF8ToWide(programfiles_w6432)); - break; - } -#endif - if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, - SHGFP_TYPE_CURRENT, system_buffer))) - return false; - cur = FilePath(system_buffer); - break; - case base::DIR_IE_INTERNET_CACHE: - if (FAILED(SHGetFolderPath(NULL, CSIDL_INTERNET_CACHE, NULL, - SHGFP_TYPE_CURRENT, system_buffer))) - return false; - cur = FilePath(system_buffer); - break; - case base::DIR_COMMON_START_MENU: - if (FAILED(SHGetFolderPath(NULL, CSIDL_COMMON_PROGRAMS, NULL, - SHGFP_TYPE_CURRENT, system_buffer))) - return false; - cur = FilePath(system_buffer); - break; - case base::DIR_START_MENU: - if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAMS, NULL, - SHGFP_TYPE_CURRENT, system_buffer))) - return false; - cur = FilePath(system_buffer); - break; - case base::DIR_APP_DATA: - if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, - system_buffer))) - return false; - cur = FilePath(system_buffer); - break; - case base::DIR_COMMON_APP_DATA: - if (FAILED(SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, - SHGFP_TYPE_CURRENT, system_buffer))) - return false; - cur = FilePath(system_buffer); - break; - case base::DIR_LOCAL_APP_DATA: - if (FAILED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, - SHGFP_TYPE_CURRENT, system_buffer))) - return false; - cur = FilePath(system_buffer); - break; - case base::DIR_SOURCE_ROOT: { - FilePath executableDir; - // On Windows, unit tests execute two levels deep from the source root. - // For example: chrome/{Debug|Release}/ui_tests.exe - PathService::Get(base::DIR_EXE, &executableDir); - cur = executableDir.DirName().DirName(); - break; - } - case base::DIR_APP_SHORTCUTS: { - if (win::GetVersion() < win::VERSION_WIN8) - return false; - - base::win::ScopedCoMem path_buf; - if (FAILED(SHGetKnownFolderPath(FOLDERID_ApplicationShortcuts, 0, NULL, - &path_buf))) - return false; - - cur = FilePath(string16(path_buf)); - break; - } - case base::DIR_USER_DESKTOP: - if (FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, - SHGFP_TYPE_CURRENT, system_buffer))) { - return false; - } - cur = FilePath(system_buffer); - break; - case base::DIR_COMMON_DESKTOP: - if (FAILED(SHGetFolderPath(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, NULL, - SHGFP_TYPE_CURRENT, system_buffer))) { - return false; - } - cur = FilePath(system_buffer); - break; - case base::DIR_USER_QUICK_LAUNCH: - if (!PathService::Get(base::DIR_APP_DATA, &cur)) - return false; - // According to various sources, appending - // "Microsoft\Internet Explorer\Quick Launch" to %appdata% is the only - // reliable way to get the quick launch folder across all versions of - // Windows. - // http://stackoverflow.com/questions/76080/how-do-you-reliably-get-the-quick- - // http://www.microsoft.com/technet/scriptcenter/resources/qanda/sept05/hey0901.mspx - cur = cur.Append(FILE_PATH_LITERAL("Microsoft")) - .Append(FILE_PATH_LITERAL("Internet Explorer")) - .Append(FILE_PATH_LITERAL("Quick Launch")); - break; - case base::DIR_TASKBAR_PINS: - if (!PathService::Get(base::DIR_USER_QUICK_LAUNCH, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("User Pinned")) - .Append(FILE_PATH_LITERAL("TaskBar")); - break; - case base::DIR_IMPLICIT_APP_SHORTCUTS: - if (!PathService::Get(base::DIR_USER_QUICK_LAUNCH, &cur)) - return false; - cur = cur.Append(FILE_PATH_LITERAL("User Pinned")) - .Append(FILE_PATH_LITERAL("ImplicitAppShortcuts")); - 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; - } - - *result = cur; - return true; -} - -} // namespace base diff --git a/base_paths_win.h b/base_paths_win.h deleted file mode 100644 index 2db16a627..000000000 --- a/base_paths_win.h +++ /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. - -#ifndef BASE_BASE_PATHS_WIN_H_ -#define BASE_BASE_PATHS_WIN_H_ - -// This file declares windows-specific path keys for the base module. -// These can be used with the PathService to access various special -// directories and files. - -namespace base { - -enum { - PATH_WIN_START = 100, - - DIR_WINDOWS, // Windows directory, usually "c:\windows" - DIR_SYSTEM, // Usually c:\windows\system32" - // 32-bit 32-bit on 64-bit 64-bit on 64-bit - // DIR_PROGRAM_FILES 1 2 1 - // DIR_PROGRAM_FILESX86 1 2 2 - // DIR_PROGRAM_FILES6432 1 1 1 - // 1 - C:\Program Files 2 - C:\Program Files (x86) - DIR_PROGRAM_FILES, // See table above. - DIR_PROGRAM_FILESX86, // See table above. - DIR_PROGRAM_FILES6432, // See table above. - - DIR_IE_INTERNET_CACHE, // Temporary Internet Files directory. - DIR_COMMON_START_MENU, // Usually "C:\ProgramData\Microsoft\Windows\ - // Start Menu\Programs" - DIR_START_MENU, // Usually "C:\Users\\AppData\Roaming\ - // Microsoft\Windows\Start Menu\Programs" - DIR_APP_DATA, // Application Data directory under the user - // profile. - DIR_LOCAL_APP_DATA, // "Local Settings\Application Data" directory - // under the user profile. - DIR_COMMON_APP_DATA, // Usually "C:\ProgramData". - DIR_APP_SHORTCUTS, // Where tiles on the start screen are stored, - // only for Windows 8. Maps to "Local\AppData\ - // Microsoft\Windows\Application Shortcuts\". - DIR_COMMON_DESKTOP, // Directory for the common desktop (visible - // on all user's Desktop). - DIR_USER_QUICK_LAUNCH, // Directory for the quick launch shortcuts. - DIR_TASKBAR_PINS, // Directory for the shortcuts pinned to taskbar. - DIR_IMPLICIT_APP_SHORTCUTS, // The implicit user pinned shortcut directory. - DIR_WINDOWS_FONTS, // Usually C:\Windows\Fonts. - - PATH_WIN_END -}; - -} // namespace base - -#endif // BASE_BASE_PATHS_WIN_H_ diff --git a/base_switches.cc b/base_switches.cc deleted file mode 100644 index 13c710b19..000000000 --- a/base_switches.cc +++ /dev/null @@ -1,121 +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/base_switches.h" -#include "build/build_config.h" - -namespace switches { - -// Delays execution of base::TaskPriority::BACKGROUND tasks until shutdown. -const char kDisableBackgroundTasks[] = "disable-background-tasks"; - -// Disables the crash reporting. -const char kDisableBreakpad[] = "disable-breakpad"; - -// Comma-separated list of feature names to disable. See also kEnableFeatures. -const char kDisableFeatures[] = "disable-features"; - -// Indicates that crash reporting should be enabled. On platforms where helper -// processes cannot access to files needed to make this decision, this flag is -// generated internally. -const char kEnableCrashReporter[] = "enable-crash-reporter"; - -// Comma-separated list of feature names to enable. See also kDisableFeatures. -const char kEnableFeatures[] = "enable-features"; - -// Generates full memory crash dump. -const char kFullMemoryCrashReport[] = "full-memory-crash-report"; - -// Force low-end device mode when set. -const char kEnableLowEndDeviceMode[] = "enable-low-end-device-mode"; - -// Force disabling of low-end device mode when set. -const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode"; - -// This option can be used to force field trials when testing changes locally. -// The argument is a list of name and value pairs, separated by slashes. If a -// trial name is prefixed with an asterisk, that trial will start activated. -// For example, the following argument defines two trials, with the second one -// activated: "GoogleNow/Enable/*MaterialDesignNTP/Default/" This option can -// also be used by the browser process to send the list of trials to a -// non-browser process, using the same format. See -// FieldTrialList::CreateTrialsFromString() in field_trial.h for details. -const char kForceFieldTrials[] = "force-fieldtrials"; - -// Suppresses all error dialogs when present. -const char kNoErrorDialogs[] = "noerrdialogs"; - -// When running certain tests that spawn child processes, this switch indicates -// to the test framework that the current process is a child process. -const char kTestChildProcess[] = "test-child-process"; - -// When running certain tests that spawn child processes, this switch indicates -// to the test framework that the current process should not initialize ICU to -// avoid creating any scoped handles too early in startup. -const char kTestDoNotInitializeIcu[] = "test-do-not-initialize-icu"; - -// Gives the default maximal active V-logging level; 0 is the default. -// Normally positive values are used for V-logging levels. -const char kV[] = "v"; - -// Gives the per-module maximal V-logging levels to override the value -// given by --v. E.g. "my_module=2,foo*=3" would change the logging -// level for all code in source files "my_module.*" and "foo*.*" -// ("-inl" suffixes are also disregarded for this matching). -// -// Any pattern containing a forward or backward slash will be tested -// against the whole pathname and not just the module. E.g., -// "*/foo/bar/*=2" would change the logging level for all code in -// source files under a "foo/bar" directory. -const char kVModule[] = "vmodule"; - -// Will wait for 60 seconds for a debugger to come to attach to the process. -const char kWaitForDebugger[] = "wait-for-debugger"; - -// Sends trace events from these categories to a file. -// --trace-to-file on its own sends to default categories. -const char kTraceToFile[] = "trace-to-file"; - -// Specifies the file name for --trace-to-file. If unspecified, it will -// go to a default file name. -const char kTraceToFileName[] = "trace-to-file-name"; - -// Specifies a location for profiling output. This will only work if chrome has -// been built with the gyp variable profiling=1 or gn arg enable_profiling=true. -// -// {pid} if present will be replaced by the pid of the process. -// {count} if present will be incremented each time a profile is generated -// for this process. -// The default is chrome-profile-{pid} for the browser and test-profile-{pid} -// for tests. -const char kProfilingFile[] = "profiling-file"; - -#if defined(OS_WIN) -// Disables the USB keyboard detection for blocking the OSK on Win8+. -const char kDisableUsbKeyboardDetect[] = "disable-usb-keyboard-detect"; -#endif - -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -// The /dev/shm partition is too small in certain VM environments, causing -// Chrome to fail or crash (see http://crbug.com/715363). Use this flag to -// work-around this issue (a temporary directory will always be used to create -// anonymous shared memory files). -const char kDisableDevShmUsage[] = "disable-dev-shm-usage"; -#endif - -#if defined(OS_POSIX) -// Used for turning on Breakpad crash reporting in a debug environment where -// crash reporting is typically compiled but disabled. -const char kEnableCrashReporterForTesting[] = - "enable-crash-reporter-for-testing"; -#endif - -#if defined(OS_ANDROID) -// Optimizes memory layout of the native library using the orderfile symbols -// given in base/android/library_loader/anchor_functions.h, via madvise and -// changing the library prefetch behavior. -const char kOrderfileMemoryOptimization[] = "orderfile-memory-optimization"; -#endif - -} // namespace switches diff --git a/base_switches.h b/base_switches.h deleted file mode 100644 index 4ef070d3f..000000000 --- a/base_switches.h +++ /dev/null @@ -1,51 +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. - -// Defines all the "base" command-line switches. - -#ifndef BASE_BASE_SWITCHES_H_ -#define BASE_BASE_SWITCHES_H_ - -#include "build/build_config.h" - -namespace switches { - -extern const char kDisableBackgroundTasks[]; -extern const char kDisableBreakpad[]; -extern const char kDisableFeatures[]; -extern const char kDisableLowEndDeviceMode[]; -extern const char kEnableCrashReporter[]; -extern const char kEnableFeatures[]; -extern const char kEnableLowEndDeviceMode[]; -extern const char kForceFieldTrials[]; -extern const char kFullMemoryCrashReport[]; -extern const char kNoErrorDialogs[]; -extern const char kProfilingFile[]; -extern const char kTestChildProcess[]; -extern const char kTestDoNotInitializeIcu[]; -extern const char kTraceToFile[]; -extern const char kTraceToFileName[]; -extern const char kV[]; -extern const char kVModule[]; -extern const char kWaitForDebugger[]; - -#if defined(OS_WIN) -extern const char kDisableUsbKeyboardDetect[]; -#endif - -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -extern const char kDisableDevShmUsage[]; -#endif - -#if defined(OS_POSIX) -extern const char kEnableCrashReporterForTesting[]; -#endif - -#if defined(OS_ANDROID) -extern const char kOrderfileMemoryOptimization[]; -#endif - -} // namespace switches - -#endif // BASE_BASE_SWITCHES_H_ diff --git a/big_endian.cc b/big_endian.cc deleted file mode 100644 index 514581fe4..000000000 --- a/big_endian.cc +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/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 -bool BigEndianReader::Read(T* value) { - if (ptr_ + sizeof(T) > end_) - return false; - ReadBigEndian(ptr_, value); - ptr_ += sizeof(T); - return true; -} - -bool BigEndianReader::ReadU8(uint8_t* value) { - return Read(value); -} - -bool BigEndianReader::ReadU16(uint16_t* value) { - return Read(value); -} - -bool BigEndianReader::ReadU32(uint32_t* value) { - return Read(value); -} - -bool BigEndianReader::ReadU64(uint64_t* 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 -bool BigEndianWriter::Write(T value) { - if (ptr_ + sizeof(T) > end_) - return false; - WriteBigEndian(ptr_, value); - ptr_ += sizeof(T); - return true; -} - -bool BigEndianWriter::WriteU8(uint8_t value) { - return Write(value); -} - -bool BigEndianWriter::WriteU16(uint16_t value) { - return Write(value); -} - -bool BigEndianWriter::WriteU32(uint32_t value) { - return Write(value); -} - -bool BigEndianWriter::WriteU64(uint64_t value) { - return Write(value); -} - -} // namespace base diff --git a/big_endian.h b/big_endian.h deleted file mode 100644 index 5684c6758..000000000 --- a/big_endian.h +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_BIG_ENDIAN_H_ -#define BASE_BIG_ENDIAN_H_ - -#include -#include - -#include "base/base_export.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 use ntohs(*(uint16_t*)ptr) which is -// potentially unaligned. -// This would cause SIGBUS on ARMv5 or earlier and ARMv6-M. -template -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_t to avoid clobbering by sign extension. - *out |= static_cast(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 -inline void WriteBigEndian(char buf[], T val) { - for (size_t i = 0; i < sizeof(T); ++i) { - buf[sizeof(T)-i-1] = static_cast(val & 0xFF); - val >>= 8; - } -} - -// Specializations to make clang happy about the (dead code) shifts above. -template <> -inline void ReadBigEndian(const char buf[], uint8_t* out) { - *out = buf[0]; -} - -template <> -inline void WriteBigEndian(char buf[], uint8_t val) { - buf[0] = static_cast(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_t* value); - bool ReadU16(uint16_t* value); - bool ReadU32(uint32_t* value); - bool ReadU64(uint64_t* value); - - private: - // Hidden to promote type safety. - template - 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_t value); - bool WriteU16(uint16_t value); - bool WriteU32(uint32_t value); - bool WriteU64(uint64_t value); - - private: - // Hidden to promote type safety. - template - bool Write(T v); - - char* ptr_; - char* end_; -}; - -} // namespace base - -#endif // BASE_BIG_ENDIAN_H_ diff --git a/big_endian_unittest.cc b/big_endian_unittest.cc deleted file mode 100644 index 4e1e7cea3..000000000 --- a/big_endian_unittest.cc +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/big_endian.h" - -#include - -#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, 0xD, 0xE, 0xF, - 0x1A, 0x2B, 0x3C, 0x4D, 0x5E }; - char buf[2]; - uint8_t u8; - uint16_t u16; - uint32_t u32; - uint64_t u64; - 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(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); - EXPECT_TRUE(reader.ReadU64(&u64)); - EXPECT_EQ(0x0B0C0D0E0F1A2B3Cllu, u64); - 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[8]; - char buf[2]; - uint8_t u8; - uint16_t u16; - uint32_t u32; - uint64_t u64; - base::StringPiece piece; - BigEndianReader reader(data, sizeof(data)); - // 8 left - EXPECT_FALSE(reader.Skip(9)); - EXPECT_TRUE(reader.Skip(1)); - // 7 left - EXPECT_FALSE(reader.ReadU64(&u64)); - EXPECT_TRUE(reader.Skip(4)); - // 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, 0xB, 0xC, 0xD, 0xE, - 0xF, 0x1A, 0x2B, 0x3C }; - 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_TRUE(writer.WriteU64(0x0B0C0D0E0F1A2B3Cllu)); - EXPECT_EQ(0, memcmp(expected, data, sizeof(expected))); -} - -TEST(BigEndianWriterTest, RespectsLength) { - char data[8]; - char buf[2]; - uint8_t u8 = 0; - uint16_t u16 = 0; - uint32_t u32 = 0; - uint64_t u64 = 0; - BigEndianWriter writer(data, sizeof(data)); - // 8 left - EXPECT_FALSE(writer.Skip(9)); - EXPECT_TRUE(writer.Skip(1)); - // 7 left - EXPECT_FALSE(writer.WriteU64(u64)); - EXPECT_TRUE(writer.Skip(4)); - // 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/bind.h b/bind.h deleted file mode 100644 index 66d5d82dd..000000000 --- a/bind.h +++ /dev/null @@ -1,482 +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_BIND_H_ -#define BASE_BIND_H_ - -#include - -#include "base/bind_internal.h" -#include "base/compiler_specific.h" -#include "build/build_config.h" - -#if defined(OS_MACOSX) && !HAS_FEATURE(objc_arc) -#include "base/mac/scoped_block.h" -#endif - -// ----------------------------------------------------------------------------- -// Usage documentation -// ----------------------------------------------------------------------------- -// -// Overview: -// base::BindOnce() and base::BindRepeating() are helpers for creating -// base::OnceCallback and base::RepeatingCallback objects respectively. -// -// For a runnable object of n-arity, the base::Bind*() family allows partial -// application of the first m arguments. The remaining n - m arguments must be -// passed when invoking the callback with Run(). -// -// // The first argument is bound at callback creation; the remaining -// // two must be passed when calling Run() on the callback object. -// base::OnceCallback cb = base::BindOnce( -// [](short x, int y, long z) { return x * y * z; }, 42); -// -// When binding to a method, the receiver object must also be specified at -// callback creation time. When Run() is invoked, the method will be invoked on -// the specified receiver object. -// -// class C : public base::RefCounted { void F(); }; -// auto instance = base::MakeRefCounted(); -// auto cb = base::BindOnce(&C::F, instance); -// cb.Run(); // Identical to instance->F() -// -// base::Bind is currently a type alias for base::BindRepeating(). In the -// future, we expect to flip this to default to base::BindOnce(). -// -// See //docs/callback.md for the full documentation. -// -// ----------------------------------------------------------------------------- -// Implementation notes -// ----------------------------------------------------------------------------- -// -// If you're reading the implementation, before proceeding further, you should -// read the top comment of base/bind_internal.h for a definition of common -// terms and concepts. - -namespace base { - -namespace internal { - -// IsOnceCallback is a std::true_type if |T| is a OnceCallback. -template -struct IsOnceCallback : std::false_type {}; - -template -struct IsOnceCallback> : std::true_type {}; - -// Helper to assert that parameter |i| of type |Arg| can be bound, which means: -// - |Arg| can be retained internally as |Storage|. -// - |Arg| can be forwarded as |Unwrapped| to |Param|. -template -struct AssertConstructible { - private: - static constexpr bool param_is_forwardable = - std::is_constructible::value; - // Unlike the check for binding into storage below, the check for - // forwardability drops the const qualifier for repeating callbacks. This is - // to try to catch instances where std::move()--which forwards as a const - // reference with repeating callbacks--is used instead of base::Passed(). - static_assert( - param_is_forwardable || - !std::is_constructible&&>::value, - "Bound argument |i| is move-only but will be forwarded by copy. " - "Ensure |Arg| is bound using base::Passed(), not std::move()."); - static_assert( - param_is_forwardable, - "Bound argument |i| of type |Arg| cannot be forwarded as " - "|Unwrapped| to the bound functor, which declares it as |Param|."); - - static constexpr bool arg_is_storable = - std::is_constructible::value; - static_assert(arg_is_storable || - !std::is_constructible&&>::value, - "Bound argument |i| is move-only but will be bound by copy. " - "Ensure |Arg| is mutable and bound using std::move()."); - static_assert(arg_is_storable, - "Bound argument |i| of type |Arg| cannot be converted and " - "bound as |Storage|."); -}; - -// Takes three same-length TypeLists, and applies AssertConstructible for each -// triples. -template -struct AssertBindArgsValidity; - -template -struct AssertBindArgsValidity, - TypeList, - TypeList, - TypeList> - : AssertConstructible, Unwrapped, Params>... { - static constexpr bool ok = true; -}; - -// The implementation of TransformToUnwrappedType below. -template -struct TransformToUnwrappedTypeImpl; - -template -struct TransformToUnwrappedTypeImpl { - using StoredType = std::decay_t; - using ForwardType = StoredType&&; - using Unwrapped = decltype(Unwrap(std::declval())); -}; - -template -struct TransformToUnwrappedTypeImpl { - using StoredType = std::decay_t; - using ForwardType = const StoredType&; - using Unwrapped = decltype(Unwrap(std::declval())); -}; - -// Transform |T| into `Unwrapped` type, which is passed to the target function. -// Example: -// In is_once == true case, -// `int&&` -> `int&&`, -// `const int&` -> `int&&`, -// `OwnedWrapper&` -> `int*&&`. -// In is_once == false case, -// `int&&` -> `const int&`, -// `const int&` -> `const int&`, -// `OwnedWrapper&` -> `int* const &`. -template -using TransformToUnwrappedType = - typename TransformToUnwrappedTypeImpl::Unwrapped; - -// Transforms |Args| into `Unwrapped` types, and packs them into a TypeList. -// If |is_method| is true, tries to dereference the first argument to support -// smart pointers. -template -struct MakeUnwrappedTypeListImpl { - using Type = TypeList...>; -}; - -// Performs special handling for this pointers. -// Example: -// int* -> int*, -// std::unique_ptr -> int*. -template -struct MakeUnwrappedTypeListImpl { - using UnwrappedReceiver = TransformToUnwrappedType; - using Type = TypeList()), - TransformToUnwrappedType...>; -}; - -template -using MakeUnwrappedTypeList = - typename MakeUnwrappedTypeListImpl::Type; - -} // namespace internal - -// Bind as OnceCallback. -template -inline OnceCallback> -BindOnce(Functor&& functor, Args&&... args) { - static_assert(!internal::IsOnceCallback>() || - (std::is_rvalue_reference() && - !std::is_const>()), - "BindOnce requires non-const rvalue for OnceCallback binding." - " I.e.: base::BindOnce(std::move(callback))."); - - // This block checks if each |args| matches to the corresponding params of the - // target function. This check does not affect the behavior of Bind, but its - // error message should be more readable. - using Helper = internal::BindTypeHelper; - using FunctorTraits = typename Helper::FunctorTraits; - using BoundArgsList = typename Helper::BoundArgsList; - using UnwrappedArgsList = - internal::MakeUnwrappedTypeList; - using BoundParamsList = typename Helper::BoundParamsList; - static_assert(internal::AssertBindArgsValidity< - std::make_index_sequence, BoundArgsList, - UnwrappedArgsList, BoundParamsList>::ok, - "The bound args need to be convertible to the target params."); - - using BindState = internal::MakeBindStateType; - using UnboundRunType = MakeUnboundRunType; - using Invoker = internal::Invoker; - using CallbackType = OnceCallback; - - // Store the invoke func into PolymorphicInvoke before casting it to - // InvokeFuncStorage, so that we can ensure its type matches to - // PolymorphicInvoke, to which CallbackType will cast back. - using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke; - PolymorphicInvoke invoke_func = &Invoker::RunOnce; - - using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage; - return CallbackType(new BindState( - reinterpret_cast(invoke_func), - std::forward(functor), - std::forward(args)...)); -} - -// Bind as RepeatingCallback. -template -inline RepeatingCallback> -BindRepeating(Functor&& functor, Args&&... args) { - static_assert( - !internal::IsOnceCallback>(), - "BindRepeating cannot bind OnceCallback. Use BindOnce with std::move()."); - - // This block checks if each |args| matches to the corresponding params of the - // target function. This check does not affect the behavior of Bind, but its - // error message should be more readable. - using Helper = internal::BindTypeHelper; - using FunctorTraits = typename Helper::FunctorTraits; - using BoundArgsList = typename Helper::BoundArgsList; - using UnwrappedArgsList = - internal::MakeUnwrappedTypeList; - using BoundParamsList = typename Helper::BoundParamsList; - static_assert(internal::AssertBindArgsValidity< - std::make_index_sequence, BoundArgsList, - UnwrappedArgsList, BoundParamsList>::ok, - "The bound args need to be convertible to the target params."); - - using BindState = internal::MakeBindStateType; - using UnboundRunType = MakeUnboundRunType; - using Invoker = internal::Invoker; - using CallbackType = RepeatingCallback; - - // Store the invoke func into PolymorphicInvoke before casting it to - // InvokeFuncStorage, so that we can ensure its type matches to - // PolymorphicInvoke, to which CallbackType will cast back. - using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke; - PolymorphicInvoke invoke_func = &Invoker::Run; - - using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage; - return CallbackType(new BindState( - reinterpret_cast(invoke_func), - std::forward(functor), - std::forward(args)...)); -} - -// Unannotated Bind. -// TODO(tzik): Deprecate this and migrate to OnceCallback and -// RepeatingCallback, once they get ready. -template -inline Callback> -Bind(Functor&& functor, Args&&... args) { - return base::BindRepeating(std::forward(functor), - std::forward(args)...); -} - -// Special cases for binding to a base::Callback without extra bound arguments. -template -OnceCallback BindOnce(OnceCallback closure) { - return closure; -} - -template -RepeatingCallback BindRepeating( - RepeatingCallback closure) { - return closure; -} - -template -Callback Bind(Callback closure) { - return closure; -} - -// Unretained() allows Bind() to bind a non-refcounted class, and to disable -// refcounting on arguments that are refcounted objects. -// -// EXAMPLE OF Unretained(): -// -// class Foo { -// public: -// void func() { cout << "Foo:f" << endl; } -// }; -// -// // In some function somewhere. -// Foo foo; -// Closure foo_callback = -// Bind(&Foo::func, Unretained(&foo)); -// foo_callback.Run(); // Prints "Foo:f". -// -// Without the Unretained() wrapper on |&foo|, the above call would fail -// to compile because Foo does not support the AddRef() and Release() methods. -template -static inline internal::UnretainedWrapper Unretained(T* o) { - return internal::UnretainedWrapper(o); -} - -// RetainedRef() accepts a ref counted object and retains a reference to it. -// When the callback is called, the object is passed as a raw pointer. -// -// EXAMPLE OF RetainedRef(): -// -// void foo(RefCountedBytes* bytes) {} -// -// scoped_refptr bytes = ...; -// Closure callback = Bind(&foo, base::RetainedRef(bytes)); -// callback.Run(); -// -// Without RetainedRef, the scoped_refptr would try to implicitly convert to -// a raw pointer and fail compilation: -// -// Closure callback = Bind(&foo, bytes); // ERROR! -template -static inline internal::RetainedRefWrapper RetainedRef(T* o) { - return internal::RetainedRefWrapper(o); -} -template -static inline internal::RetainedRefWrapper RetainedRef(scoped_refptr o) { - return internal::RetainedRefWrapper(std::move(o)); -} - -// ConstRef() allows binding a constant reference to an argument rather -// than a copy. -// -// EXAMPLE OF ConstRef(): -// -// void foo(int arg) { cout << arg << endl } -// -// int n = 1; -// Closure no_ref = Bind(&foo, n); -// Closure has_ref = Bind(&foo, ConstRef(n)); -// -// no_ref.Run(); // Prints "1" -// has_ref.Run(); // Prints "1" -// -// n = 2; -// no_ref.Run(); // Prints "1" -// has_ref.Run(); // Prints "2" -// -// Note that because ConstRef() takes a reference on |n|, |n| must outlive all -// its bound callbacks. -template -static inline internal::ConstRefWrapper ConstRef(const T& o) { - return internal::ConstRefWrapper(o); -} - -// Owned() transfers ownership of an object to the Callback resulting from -// bind; the object will be deleted when the Callback is deleted. -// -// EXAMPLE OF Owned(): -// -// void foo(int* arg) { cout << *arg << endl } -// -// int* pn = new int(1); -// Closure foo_callback = Bind(&foo, Owned(pn)); -// -// foo_callback.Run(); // Prints "1" -// foo_callback.Run(); // Prints "1" -// *n = 2; -// foo_callback.Run(); // Prints "2" -// -// foo_callback.Reset(); // |pn| is deleted. Also will happen when -// // |foo_callback| goes out of scope. -// -// Without Owned(), someone would have to know to delete |pn| when the last -// reference to the Callback is deleted. -template -static inline internal::OwnedWrapper Owned(T* o) { - return internal::OwnedWrapper(o); -} - -// Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr) -// through a Callback. Logically, this signifies a destructive transfer of -// the state of the argument into the target function. Invoking -// Callback::Run() twice on a Callback that was created with a Passed() -// argument will CHECK() because the first invocation would have already -// transferred ownership to the target function. -// -// Note that Passed() is not necessary with BindOnce(), as std::move() does the -// same thing. Avoid Passed() in favor of std::move() with BindOnce(). -// -// EXAMPLE OF Passed(): -// -// void TakesOwnership(std::unique_ptr arg) { } -// std::unique_ptr CreateFoo() { return std::make_unique(); -// } -// -// auto f = std::make_unique(); -// -// // |cb| is given ownership of Foo(). |f| is now NULL. -// // You can use std::move(f) in place of &f, but it's more verbose. -// Closure cb = Bind(&TakesOwnership, Passed(&f)); -// -// // Run was never called so |cb| still owns Foo() and deletes -// // it on Reset(). -// cb.Reset(); -// -// // |cb| is given a new Foo created by CreateFoo(). -// cb = Bind(&TakesOwnership, Passed(CreateFoo())); -// -// // |arg| in TakesOwnership() is given ownership of Foo(). |cb| -// // no longer owns Foo() and, if reset, would not delete Foo(). -// cb.Run(); // Foo() is now transferred to |arg| and deleted. -// cb.Run(); // This CHECK()s since Foo() already been used once. -// -// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and -// is best suited for use with the return value of a function or other temporary -// rvalues. The second takes a pointer to the scoper and is just syntactic sugar -// to avoid having to write Passed(std::move(scoper)). -// -// Both versions of Passed() prevent T from being an lvalue reference. The first -// via use of enable_if, and the second takes a T* which will not bind to T&. -template ::value>* = nullptr> -static inline internal::PassedWrapper Passed(T&& scoper) { - return internal::PassedWrapper(std::move(scoper)); -} -template -static inline internal::PassedWrapper Passed(T* scoper) { - return internal::PassedWrapper(std::move(*scoper)); -} - -// IgnoreResult() is used to adapt a function or Callback with a return type to -// one with a void return. This is most useful if you have a function with, -// say, a pesky ignorable bool return that you want to use with PostTask or -// something else that expect a Callback with a void return. -// -// EXAMPLE OF IgnoreResult(): -// -// int DoSomething(int arg) { cout << arg << endl; } -// -// // Assign to a Callback with a void return type. -// Callback cb = Bind(IgnoreResult(&DoSomething)); -// cb->Run(1); // Prints "1". -// -// // Prints "1" on |ml|. -// ml->PostTask(FROM_HERE, Bind(IgnoreResult(&DoSomething), 1); -template -static inline internal::IgnoreResultHelper IgnoreResult(T data) { - return internal::IgnoreResultHelper(std::move(data)); -} - -#if defined(OS_MACOSX) && !HAS_FEATURE(objc_arc) - -// RetainBlock() is used to adapt an Objective-C block when Automated Reference -// Counting (ARC) is disabled. This is unnecessary when ARC is enabled, as the -// BindOnce and BindRepeating already support blocks then. -// -// EXAMPLE OF RetainBlock(): -// -// // Wrap the block and bind it to a callback. -// Callback cb = Bind(RetainBlock(^(int n) { NSLog(@"%d", n); })); -// cb.Run(1); // Logs "1". -template -base::mac::ScopedBlock RetainBlock(R (^block)(Args...)) { - return base::mac::ScopedBlock(block, - base::scoped_policy::RETAIN); -} - -#endif // defined(OS_MACOSX) && !HAS_FEATURE(objc_arc) - -} // namespace base - -#endif // BASE_BIND_H_ diff --git a/bind_helpers.h b/bind_helpers.h deleted file mode 100644 index 15961e605..000000000 --- a/bind_helpers.h +++ /dev/null @@ -1,69 +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_BIND_HELPERS_H_ -#define BASE_BIND_HELPERS_H_ - -#include - -#include -#include - -#include "base/bind.h" -#include "base/callback.h" -#include "base/memory/weak_ptr.h" -#include "build/build_config.h" - -// This defines a set of simple functions and utilities that people want when -// using Callback<> and Bind(). - -namespace base { - -// Creates a null callback. -class BASE_EXPORT NullCallback { - public: - template - operator RepeatingCallback() const { - return RepeatingCallback(); - } - template - operator OnceCallback() const { - return OnceCallback(); - } -}; - -// Creates a callback that does nothing when called. -class BASE_EXPORT DoNothing { - public: - template - operator RepeatingCallback() const { - return Repeatedly(); - } - template - operator OnceCallback() const { - return Once(); - } - // Explicit way of specifying a specific callback type when the compiler can't - // deduce it. - template - static RepeatingCallback Repeatedly() { - return BindRepeating([](Args... args) {}); - } - template - static OnceCallback Once() { - return BindOnce([](Args... args) {}); - } -}; - -// Useful for creating a Closure that will delete a pointer when invoked. Only -// use this when necessary. In most cases MessageLoop::DeleteSoon() is a better -// fit. -template -void DeletePointer(T* obj) { - delete obj; -} - -} // namespace base - -#endif // BASE_BIND_HELPERS_H_ diff --git a/bind_internal.h b/bind_internal.h deleted file mode 100644 index 4ebecfaf7..000000000 --- a/bind_internal.h +++ /dev/null @@ -1,971 +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_BIND_INTERNAL_H_ -#define BASE_BIND_INTERNAL_H_ - -#include - -#include -#include - -#include "base/callback_internal.h" -#include "base/compiler_specific.h" -#include "base/memory/raw_scoped_refptr_mismatch_checker.h" -#include "base/memory/weak_ptr.h" -#include "base/template_util.h" -#include "build/build_config.h" - -#if defined(OS_MACOSX) && !HAS_FEATURE(objc_arc) -#include "base/mac/scoped_block.h" -#endif - -// See base/callback.h for user documentation. -// -// -// CONCEPTS: -// Functor -- A movable type representing something that should be called. -// All function pointers and Callback<> are functors even if the -// invocation syntax differs. -// RunType -- A function type (as opposed to function _pointer_ type) for -// a Callback<>::Run(). Usually just a convenience typedef. -// (Bound)Args -- A set of types that stores the arguments. -// -// Types: -// ForceVoidReturn<> -- Helper class for translating function signatures to -// equivalent forms with a "void" return type. -// FunctorTraits<> -- Type traits used to determine the correct RunType and -// invocation manner for a Functor. This is where function -// signature adapters are applied. -// InvokeHelper<> -- Take a Functor + arguments and actully invokes it. -// Handle the differing syntaxes needed for WeakPtr<> -// support. This is separate from Invoker to avoid creating -// multiple version of Invoker<>. -// Invoker<> -- Unwraps the curried parameters and executes the Functor. -// BindState<> -- Stores the curried parameters, and is the main entry point -// into the Bind() system. - -namespace base { - -template -struct IsWeakReceiver; - -template -struct BindUnwrapTraits; - -template -struct CallbackCancellationTraits; - -namespace internal { - -template -struct FunctorTraits; - -template -class UnretainedWrapper { - public: - explicit UnretainedWrapper(T* o) : ptr_(o) {} - T* get() const { return ptr_; } - - private: - T* ptr_; -}; - -template -class ConstRefWrapper { - public: - explicit ConstRefWrapper(const T& o) : ptr_(&o) {} - const T& get() const { return *ptr_; } - - private: - const T* ptr_; -}; - -template -class RetainedRefWrapper { - public: - explicit RetainedRefWrapper(T* o) : ptr_(o) {} - explicit RetainedRefWrapper(scoped_refptr o) : ptr_(std::move(o)) {} - T* get() const { return ptr_.get(); } - - private: - scoped_refptr ptr_; -}; - -template -struct IgnoreResultHelper { - explicit IgnoreResultHelper(T functor) : functor_(std::move(functor)) {} - explicit operator bool() const { return !!functor_; } - - T functor_; -}; - -// An alternate implementation is to avoid the destructive copy, and instead -// specialize ParamTraits<> for OwnedWrapper<> to change the StorageType to -// a class that is essentially a std::unique_ptr<>. -// -// The current implementation has the benefit though of leaving ParamTraits<> -// fully in callback_internal.h as well as avoiding type conversions during -// storage. -template -class OwnedWrapper { - public: - explicit OwnedWrapper(T* o) : ptr_(o) {} - ~OwnedWrapper() { delete ptr_; } - T* get() const { return ptr_; } - OwnedWrapper(OwnedWrapper&& other) { - ptr_ = other.ptr_; - other.ptr_ = NULL; - } - - private: - mutable T* ptr_; -}; - -// PassedWrapper is a copyable adapter for a scoper that ignores const. -// -// It is needed to get around the fact that Bind() takes a const reference to -// all its arguments. Because Bind() takes a const reference to avoid -// unnecessary copies, it is incompatible with movable-but-not-copyable -// types; doing a destructive "move" of the type into Bind() would violate -// the const correctness. -// -// This conundrum cannot be solved without either C++11 rvalue references or -// a O(2^n) blowup of Bind() templates to handle each combination of regular -// types and movable-but-not-copyable types. Thus we introduce a wrapper type -// that is copyable to transmit the correct type information down into -// BindState<>. Ignoring const in this type makes sense because it is only -// created when we are explicitly trying to do a destructive move. -// -// Two notes: -// 1) PassedWrapper supports any type that has a move constructor, however -// the type will need to be specifically whitelisted in order for it to be -// bound to a Callback. We guard this explicitly at the call of Passed() -// to make for clear errors. Things not given to Passed() will be forwarded -// and stored by value which will not work for general move-only types. -// 2) is_valid_ is distinct from NULL because it is valid to bind a "NULL" -// scoper to a Callback and allow the Callback to execute once. -template -class PassedWrapper { - public: - explicit PassedWrapper(T&& scoper) - : is_valid_(true), scoper_(std::move(scoper)) {} - PassedWrapper(PassedWrapper&& other) - : is_valid_(other.is_valid_), scoper_(std::move(other.scoper_)) {} - T Take() const { - CHECK(is_valid_); - is_valid_ = false; - return std::move(scoper_); - } - - private: - mutable bool is_valid_; - mutable T scoper_; -}; - -template -using Unwrapper = BindUnwrapTraits>; - -template -decltype(auto) Unwrap(T&& o) { - return Unwrapper::Unwrap(std::forward(o)); -} - -// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a -// method. It is used internally by Bind() to select the correct -// InvokeHelper that will no-op itself in the event the WeakPtr<> for -// the target object is invalidated. -// -// The first argument should be the type of the object that will be received by -// the method. -template -struct IsWeakMethod : std::false_type {}; - -template -struct IsWeakMethod : IsWeakReceiver {}; - -// Packs a list of types to hold them in a single type. -template -struct TypeList {}; - -// Used for DropTypeListItem implementation. -template -struct DropTypeListItemImpl; - -// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure. -template -struct DropTypeListItemImpl> - : DropTypeListItemImpl> {}; - -template -struct DropTypeListItemImpl<0, TypeList> { - using Type = TypeList; -}; - -template <> -struct DropTypeListItemImpl<0, TypeList<>> { - using Type = TypeList<>; -}; - -// A type-level function that drops |n| list item from given TypeList. -template -using DropTypeListItem = typename DropTypeListItemImpl::Type; - -// Used for TakeTypeListItem implementation. -template -struct TakeTypeListItemImpl; - -// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure. -template -struct TakeTypeListItemImpl, Accum...> - : TakeTypeListItemImpl, Accum..., T> {}; - -template -struct TakeTypeListItemImpl<0, TypeList, Accum...> { - using Type = TypeList; -}; - -template -struct TakeTypeListItemImpl<0, TypeList<>, Accum...> { - using Type = TypeList; -}; - -// A type-level function that takes first |n| list item from given TypeList. -// E.g. TakeTypeListItem<3, TypeList> is evaluated to -// TypeList. -template -using TakeTypeListItem = typename TakeTypeListItemImpl::Type; - -// Used for ConcatTypeLists implementation. -template -struct ConcatTypeListsImpl; - -template -struct ConcatTypeListsImpl, TypeList> { - using Type = TypeList; -}; - -// A type-level function that concats two TypeLists. -template -using ConcatTypeLists = typename ConcatTypeListsImpl::Type; - -// Used for MakeFunctionType implementation. -template -struct MakeFunctionTypeImpl; - -template -struct MakeFunctionTypeImpl> { - // MSVC 2013 doesn't support Type Alias of function types. - // Revisit this after we update it to newer version. - typedef R Type(Args...); -}; - -// A type-level function that constructs a function type that has |R| as its -// return type and has TypeLists items as its arguments. -template -using MakeFunctionType = typename MakeFunctionTypeImpl::Type; - -// Used for ExtractArgs and ExtractReturnType. -template -struct ExtractArgsImpl; - -template -struct ExtractArgsImpl { - using ReturnType = R; - using ArgsList = TypeList; -}; - -// A type-level function that extracts function arguments into a TypeList. -// E.g. ExtractArgs is evaluated to TypeList. -template -using ExtractArgs = typename ExtractArgsImpl::ArgsList; - -// A type-level function that extracts the return type of a function. -// E.g. ExtractReturnType is evaluated to R. -template -using ExtractReturnType = typename ExtractArgsImpl::ReturnType; - -template -struct ExtractCallableRunTypeImpl; - -template -struct ExtractCallableRunTypeImpl { - using Type = R(Args...); -}; - -template -struct ExtractCallableRunTypeImpl { - using Type = R(Args...); -}; - -// Evaluated to RunType of the given callable type. -// Example: -// auto f = [](int, char*) { return 0.1; }; -// ExtractCallableRunType -// is evaluated to -// double(int, char*); -template -using ExtractCallableRunType = - typename ExtractCallableRunTypeImpl::Type; - -// IsCallableObject is std::true_type if |Functor| has operator(). -// Otherwise, it's std::false_type. -// Example: -// IsCallableObject::value is false. -// -// struct Foo {}; -// IsCallableObject::value is false. -// -// int i = 0; -// auto f = [i]() {}; -// IsCallableObject::value is false. -template -struct IsCallableObject : std::false_type {}; - -template -struct IsCallableObject> - : std::true_type {}; - -// HasRefCountedTypeAsRawPtr selects true_type when any of the |Args| is a raw -// pointer to a RefCounted type. -// Implementation note: This non-specialized case handles zero-arity case only. -// Non-zero-arity cases should be handled by the specialization below. -template -struct HasRefCountedTypeAsRawPtr : std::false_type {}; - -// Implementation note: Select true_type if the first parameter is a raw pointer -// to a RefCounted type. Otherwise, skip the first parameter and check rest of -// parameters recursively. -template -struct HasRefCountedTypeAsRawPtr - : std::conditional_t::value, - std::true_type, - HasRefCountedTypeAsRawPtr> {}; - -// ForceVoidReturn<> -// -// Set of templates that support forcing the function return type to void. -template -struct ForceVoidReturn; - -template -struct ForceVoidReturn { - using RunType = void(Args...); -}; - -// FunctorTraits<> -// -// See description at top of file. -template -struct FunctorTraits; - -// For empty callable types. -// This specialization is intended to allow binding captureless lambdas by -// base::Bind(), based on the fact that captureless lambdas are empty while -// capturing lambdas are not. This also allows any functors as far as it's an -// empty class. -// Example: -// -// // Captureless lambdas are allowed. -// []() {return 42;}; -// -// // Capturing lambdas are *not* allowed. -// int x; -// [x]() {return x;}; -// -// // Any empty class with operator() is allowed. -// struct Foo { -// void operator()() const {} -// // No non-static member variable and no virtual functions. -// }; -template -struct FunctorTraits::value && - std::is_empty::value>> { - using RunType = ExtractCallableRunType; - static constexpr bool is_method = false; - static constexpr bool is_nullable = false; - - template - static ExtractReturnType Invoke(RunFunctor&& functor, - RunArgs&&... args) { - return std::forward(functor)(std::forward(args)...); - } -}; - -// For functions. -template -struct FunctorTraits { - using RunType = R(Args...); - static constexpr bool is_method = false; - static constexpr bool is_nullable = true; - - template - static R Invoke(Function&& function, RunArgs&&... args) { - return std::forward(function)(std::forward(args)...); - } -}; - -#if defined(OS_WIN) && !defined(ARCH_CPU_X86_64) - -// For functions. -template -struct FunctorTraits { - using RunType = R(Args...); - static constexpr bool is_method = false; - static constexpr bool is_nullable = true; - - template - static R Invoke(R(__stdcall* function)(Args...), RunArgs&&... args) { - return function(std::forward(args)...); - } -}; - -// For functions. -template -struct FunctorTraits { - using RunType = R(Args...); - static constexpr bool is_method = false; - static constexpr bool is_nullable = true; - - template - static R Invoke(R(__fastcall* function)(Args...), RunArgs&&... args) { - return function(std::forward(args)...); - } -}; - -#endif // defined(OS_WIN) && !defined(ARCH_CPU_X86_64) - -#if defined(OS_MACOSX) - -// Support for Objective-C blocks. There are two implementation depending -// on whether Automated Reference Counting (ARC) is enabled. When ARC is -// enabled, then the block itself can be bound as the compiler will ensure -// its lifetime will be correctly managed. Otherwise, require the block to -// be wrapped in a base::mac::ScopedBlock (via base::RetainBlock) that will -// correctly manage the block lifetime. -// -// The two implementation ensure that the One Definition Rule (ODR) is not -// broken (it is not possible to write a template base::RetainBlock that would -// work correctly both with ARC enabled and disabled). - -#if HAS_FEATURE(objc_arc) - -template -struct FunctorTraits { - using RunType = R(Args...); - static constexpr bool is_method = false; - static constexpr bool is_nullable = true; - - template - static R Invoke(BlockType&& block, RunArgs&&... args) { - // According to LLVM documentation (§ 6.3), "local variables of automatic - // storage duration do not have precise lifetime." Use objc_precise_lifetime - // to ensure that the Objective-C block is not deallocated until it has - // finished executing even if the Callback<> is destroyed during the block - // execution. - // https://clang.llvm.org/docs/AutomaticReferenceCounting.html#precise-lifetime-semantics - __attribute__((objc_precise_lifetime)) R (^scoped_block)(Args...) = block; - return scoped_block(std::forward(args)...); - } -}; - -#else // HAS_FEATURE(objc_arc) - -template -struct FunctorTraits> { - using RunType = R(Args...); - static constexpr bool is_method = false; - static constexpr bool is_nullable = true; - - template - static R Invoke(BlockType&& block, RunArgs&&... args) { - // Copy the block to ensure that the Objective-C block is not deallocated - // until it has finished executing even if the Callback<> is destroyed - // during the block execution. - base::mac::ScopedBlock scoped_block(block); - return scoped_block.get()(std::forward(args)...); - } -}; - -#endif // HAS_FEATURE(objc_arc) -#endif // defined(OS_MACOSX) - -// For methods. -template -struct FunctorTraits { - using RunType = R(Receiver*, Args...); - static constexpr bool is_method = true; - static constexpr bool is_nullable = true; - - template - static R Invoke(Method method, - ReceiverPtr&& receiver_ptr, - RunArgs&&... args) { - return ((*receiver_ptr).*method)(std::forward(args)...); - } -}; - -// For const methods. -template -struct FunctorTraits { - using RunType = R(const Receiver*, Args...); - static constexpr bool is_method = true; - static constexpr bool is_nullable = true; - - template - static R Invoke(Method method, - ReceiverPtr&& receiver_ptr, - RunArgs&&... args) { - return ((*receiver_ptr).*method)(std::forward(args)...); - } -}; - -#ifdef __cpp_noexcept_function_type -// noexcept makes a distinct function type in C++17. -// I.e. `void(*)()` and `void(*)() noexcept` are same in pre-C++17, and -// different in C++17. -template -struct FunctorTraits : FunctorTraits { -}; - -template -struct FunctorTraits - : FunctorTraits {}; - -template -struct FunctorTraits - : FunctorTraits {}; -#endif - -// For IgnoreResults. -template -struct FunctorTraits> : FunctorTraits { - using RunType = - typename ForceVoidReturn::RunType>::RunType; - - template - static void Invoke(IgnoreResultType&& ignore_result_helper, - RunArgs&&... args) { - FunctorTraits::Invoke( - std::forward(ignore_result_helper).functor_, - std::forward(args)...); - } -}; - -// For OnceCallbacks. -template -struct FunctorTraits> { - using RunType = R(Args...); - static constexpr bool is_method = false; - static constexpr bool is_nullable = true; - - template - static R Invoke(CallbackType&& callback, RunArgs&&... args) { - DCHECK(!callback.is_null()); - return std::forward(callback).Run( - std::forward(args)...); - } -}; - -// For RepeatingCallbacks. -template -struct FunctorTraits> { - using RunType = R(Args...); - static constexpr bool is_method = false; - static constexpr bool is_nullable = true; - - template - static R Invoke(CallbackType&& callback, RunArgs&&... args) { - DCHECK(!callback.is_null()); - return std::forward(callback).Run( - std::forward(args)...); - } -}; - -template -using MakeFunctorTraits = FunctorTraits>; - -// InvokeHelper<> -// -// There are 2 logical InvokeHelper<> specializations: normal, WeakCalls. -// -// The normal type just calls the underlying runnable. -// -// WeakCalls need special syntax that is applied to the first argument to check -// if they should no-op themselves. -template -struct InvokeHelper; - -template -struct InvokeHelper { - template - static inline ReturnType MakeItSo(Functor&& functor, RunArgs&&... args) { - using Traits = MakeFunctorTraits; - return Traits::Invoke(std::forward(functor), - std::forward(args)...); - } -}; - -template -struct InvokeHelper { - // WeakCalls are only supported for functions with a void return type. - // Otherwise, the function result would be undefined if the the WeakPtr<> - // is invalidated. - static_assert(std::is_void::value, - "weak_ptrs can only bind to methods without return values"); - - template - static inline void MakeItSo(Functor&& functor, - BoundWeakPtr&& weak_ptr, - RunArgs&&... args) { - if (!weak_ptr) - return; - using Traits = MakeFunctorTraits; - Traits::Invoke(std::forward(functor), - std::forward(weak_ptr), - std::forward(args)...); - } -}; - -// Invoker<> -// -// See description at the top of the file. -template -struct Invoker; - -template -struct Invoker { - static R RunOnce(BindStateBase* base, - PassingType... unbound_args) { - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - StorageType* storage = static_cast(base); - static constexpr size_t num_bound_args = - std::tuple_sizebound_args_)>::value; - return RunImpl(std::move(storage->functor_), - std::move(storage->bound_args_), - std::make_index_sequence(), - std::forward(unbound_args)...); - } - - static R Run(BindStateBase* base, PassingType... unbound_args) { - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - const StorageType* storage = static_cast(base); - static constexpr size_t num_bound_args = - std::tuple_sizebound_args_)>::value; - return RunImpl(storage->functor_, storage->bound_args_, - std::make_index_sequence(), - std::forward(unbound_args)...); - } - - private: - template - static inline R RunImpl(Functor&& functor, - BoundArgsTuple&& bound, - std::index_sequence, - UnboundArgs&&... unbound_args) { - static constexpr bool is_method = MakeFunctorTraits::is_method; - - using DecayedArgsTuple = std::decay_t; - static constexpr bool is_weak_call = - IsWeakMethod...>(); - - return InvokeHelper::MakeItSo( - std::forward(functor), - Unwrap(std::get(std::forward(bound)))..., - std::forward(unbound_args)...); - } -}; - -// Extracts necessary type info from Functor and BoundArgs. -// Used to implement MakeUnboundRunType, BindOnce and BindRepeating. -template -struct BindTypeHelper { - static constexpr size_t num_bounds = sizeof...(BoundArgs); - using FunctorTraits = MakeFunctorTraits; - - // Example: - // When Functor is `double (Foo::*)(int, const std::string&)`, and BoundArgs - // is a template pack of `Foo*` and `int16_t`: - // - RunType is `double(Foo*, int, const std::string&)`, - // - ReturnType is `double`, - // - RunParamsList is `TypeList`, - // - BoundParamsList is `TypeList`, - // - UnboundParamsList is `TypeList`, - // - BoundArgsList is `TypeList`, - // - UnboundRunType is `double(const std::string&)`. - using RunType = typename FunctorTraits::RunType; - using ReturnType = ExtractReturnType; - - using RunParamsList = ExtractArgs; - using BoundParamsList = TakeTypeListItem; - using UnboundParamsList = DropTypeListItem; - - using BoundArgsList = TypeList; - - using UnboundRunType = MakeFunctionType; -}; - -template -std::enable_if_t::is_nullable, bool> IsNull( - const Functor& functor) { - return !functor; -} - -template -std::enable_if_t::is_nullable, bool> IsNull( - const Functor&) { - return false; -} - -// Used by ApplyCancellationTraits below. -template -bool ApplyCancellationTraitsImpl(const Functor& functor, - const BoundArgsTuple& bound_args, - std::index_sequence) { - return CallbackCancellationTraits::IsCancelled( - functor, std::get(bound_args)...); -} - -// Relays |base| to corresponding CallbackCancellationTraits<>::Run(). Returns -// true if the callback |base| represents is canceled. -template -bool ApplyCancellationTraits(const BindStateBase* base) { - const BindStateType* storage = static_cast(base); - static constexpr size_t num_bound_args = - std::tuple_sizebound_args_)>::value; - return ApplyCancellationTraitsImpl( - storage->functor_, storage->bound_args_, - std::make_index_sequence()); -}; - -// BindState<> -// -// This stores all the state passed into Bind(). -template -struct BindState final : BindStateBase { - using IsCancellable = std::integral_constant< - bool, - CallbackCancellationTraits>::is_cancellable>; - - template - explicit BindState(BindStateBase::InvokeFuncStorage invoke_func, - ForwardFunctor&& functor, - ForwardBoundArgs&&... bound_args) - // IsCancellable is std::false_type if - // CallbackCancellationTraits<>::IsCancelled returns always false. - // Otherwise, it's std::true_type. - : BindState(IsCancellable{}, - invoke_func, - std::forward(functor), - std::forward(bound_args)...) {} - - Functor functor_; - std::tuple bound_args_; - - private: - template - explicit BindState(std::true_type, - BindStateBase::InvokeFuncStorage invoke_func, - ForwardFunctor&& functor, - ForwardBoundArgs&&... bound_args) - : BindStateBase(invoke_func, - &Destroy, - &ApplyCancellationTraits), - functor_(std::forward(functor)), - bound_args_(std::forward(bound_args)...) { - DCHECK(!IsNull(functor_)); - } - - template - explicit BindState(std::false_type, - BindStateBase::InvokeFuncStorage invoke_func, - ForwardFunctor&& functor, - ForwardBoundArgs&&... bound_args) - : BindStateBase(invoke_func, &Destroy), - functor_(std::forward(functor)), - bound_args_(std::forward(bound_args)...) { - DCHECK(!IsNull(functor_)); - } - - ~BindState() = default; - - static void Destroy(const BindStateBase* self) { - delete static_cast(self); - } -}; - -// Used to implement MakeBindStateType. -template -struct MakeBindStateTypeImpl; - -template -struct MakeBindStateTypeImpl { - static_assert(!HasRefCountedTypeAsRawPtr...>::value, - "A parameter is a refcounted type and needs scoped_refptr."); - using Type = BindState, std::decay_t...>; -}; - -template -struct MakeBindStateTypeImpl { - using Type = BindState>; -}; - -template -struct MakeBindStateTypeImpl { - private: - using DecayedReceiver = std::decay_t; - - static_assert(!std::is_array>::value, - "First bound argument to a method cannot be an array."); - static_assert( - !std::is_pointer::value || - IsRefCountedType>::value, - "Receivers may not be raw pointers. If using a raw pointer here is safe" - " and has no lifetime concerns, use base::Unretained() and document why" - " it's safe."); - static_assert(!HasRefCountedTypeAsRawPtr...>::value, - "A parameter is a refcounted type and needs scoped_refptr."); - - public: - using Type = BindState< - std::decay_t, - std::conditional_t::value, - scoped_refptr>, - DecayedReceiver>, - std::decay_t...>; -}; - -template -using MakeBindStateType = - typename MakeBindStateTypeImpl::is_method, - Functor, - BoundArgs...>::Type; - -} // namespace internal - -// An injection point to control |this| pointer behavior on a method invocation. -// If IsWeakReceiver<> is true_type for |T| and |T| is used for a receiver of a -// method, base::Bind cancels the method invocation if the receiver is tested as -// false. -// E.g. Foo::bar() is not called: -// struct Foo : base::SupportsWeakPtr { -// void bar() {} -// }; -// -// WeakPtr oo = nullptr; -// base::Bind(&Foo::bar, oo).Run(); -template -struct IsWeakReceiver : std::false_type {}; - -template -struct IsWeakReceiver> : IsWeakReceiver {}; - -template -struct IsWeakReceiver> : std::true_type {}; - -// An injection point to control how bound objects passed to the target -// function. BindUnwrapTraits<>::Unwrap() is called for each bound objects right -// before the target function is invoked. -template -struct BindUnwrapTraits { - template - static T&& Unwrap(T&& o) { - return std::forward(o); - } -}; - -template -struct BindUnwrapTraits> { - static T* Unwrap(const internal::UnretainedWrapper& o) { return o.get(); } -}; - -template -struct BindUnwrapTraits> { - static const T& Unwrap(const internal::ConstRefWrapper& o) { - return o.get(); - } -}; - -template -struct BindUnwrapTraits> { - static T* Unwrap(const internal::RetainedRefWrapper& o) { return o.get(); } -}; - -template -struct BindUnwrapTraits> { - static T* Unwrap(const internal::OwnedWrapper& o) { return o.get(); } -}; - -template -struct BindUnwrapTraits> { - static T Unwrap(const internal::PassedWrapper& o) { return o.Take(); } -}; - -// CallbackCancellationTraits allows customization of Callback's cancellation -// semantics. By default, callbacks are not cancellable. A specialization should -// set is_cancellable = true and implement an IsCancelled() that returns if the -// callback should be cancelled. -template -struct CallbackCancellationTraits { - static constexpr bool is_cancellable = false; -}; - -// Specialization for method bound to weak pointer receiver. -template -struct CallbackCancellationTraits< - Functor, - std::tuple, - std::enable_if_t< - internal::IsWeakMethod::is_method, - BoundArgs...>::value>> { - static constexpr bool is_cancellable = true; - - template - static bool IsCancelled(const Functor&, - const Receiver& receiver, - const Args&...) { - return !receiver; - } -}; - -// Specialization for a nested bind. -template -struct CallbackCancellationTraits, - std::tuple> { - static constexpr bool is_cancellable = true; - - template - static bool IsCancelled(const Functor& functor, const BoundArgs&...) { - return functor.IsCancelled(); - } -}; - -template -struct CallbackCancellationTraits, - std::tuple> { - static constexpr bool is_cancellable = true; - - template - static bool IsCancelled(const Functor& functor, const BoundArgs&...) { - return functor.IsCancelled(); - } -}; - -// Returns a RunType of bound functor. -// E.g. MakeUnboundRunType is evaluated to R(C). -template -using MakeUnboundRunType = - typename internal::BindTypeHelper::UnboundRunType; - -} // namespace base - -#endif // BASE_BIND_INTERNAL_H_ diff --git a/bind_unittest.cc b/bind_unittest.cc deleted file mode 100644 index f1d19a1ff..000000000 --- a/bind_unittest.cc +++ /dev/null @@ -1,1496 +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/bind.h" - -#include -#include -#include - -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/test/bind_test_util.h" -#include "base/test/gtest_util.h" -#include "build/build_config.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::Mock; -using ::testing::ByMove; -using ::testing::Return; -using ::testing::StrictMock; - -namespace base { -namespace { - -class IncompleteType; - -class NoRef { - public: - NoRef() = default; - - MOCK_METHOD0(VoidMethod0, void()); - MOCK_CONST_METHOD0(VoidConstMethod0, void()); - - MOCK_METHOD0(IntMethod0, int()); - MOCK_CONST_METHOD0(IntConstMethod0, int()); - - MOCK_METHOD1(VoidMethodWithIntArg, void(int)); - MOCK_METHOD0(UniquePtrMethod0, std::unique_ptr()); - - private: - // Particularly important in this test to ensure no copies are made. - DISALLOW_COPY_AND_ASSIGN(NoRef); -}; - -class HasRef : public NoRef { - public: - HasRef() = default; - - MOCK_CONST_METHOD0(AddRef, void()); - MOCK_CONST_METHOD0(Release, bool()); - - private: - // Particularly important in this test to ensure no copies are made. - DISALLOW_COPY_AND_ASSIGN(HasRef); -}; - -class HasRefPrivateDtor : public HasRef { - private: - ~HasRefPrivateDtor() = default; -}; - -static const int kParentValue = 1; -static const int kChildValue = 2; - -class Parent { - public: - void AddRef() const {} - void Release() const {} - virtual void VirtualSet() { value = kParentValue; } - void NonVirtualSet() { value = kParentValue; } - int value; -}; - -class Child : public Parent { - public: - void VirtualSet() override { value = kChildValue; } - void NonVirtualSet() { value = kChildValue; } -}; - -class NoRefParent { - public: - virtual void VirtualSet() { value = kParentValue; } - void NonVirtualSet() { value = kParentValue; } - int value; -}; - -class NoRefChild : public NoRefParent { - void VirtualSet() override { value = kChildValue; } - void NonVirtualSet() { value = kChildValue; } -}; - -// Used for probing the number of copies and moves that occur if a type must be -// coerced during argument forwarding in the Run() methods. -struct DerivedCopyMoveCounter { - DerivedCopyMoveCounter(int* copies, - int* assigns, - int* move_constructs, - int* move_assigns) - : copies_(copies), - assigns_(assigns), - move_constructs_(move_constructs), - move_assigns_(move_assigns) {} - int* copies_; - int* assigns_; - int* move_constructs_; - int* move_assigns_; -}; - -// Used for probing the number of copies and moves in an argument. -class CopyMoveCounter { - public: - CopyMoveCounter(int* copies, - int* assigns, - int* move_constructs, - int* move_assigns) - : copies_(copies), - assigns_(assigns), - move_constructs_(move_constructs), - move_assigns_(move_assigns) {} - - CopyMoveCounter(const CopyMoveCounter& other) - : copies_(other.copies_), - assigns_(other.assigns_), - move_constructs_(other.move_constructs_), - move_assigns_(other.move_assigns_) { - (*copies_)++; - } - - CopyMoveCounter(CopyMoveCounter&& other) - : copies_(other.copies_), - assigns_(other.assigns_), - move_constructs_(other.move_constructs_), - move_assigns_(other.move_assigns_) { - (*move_constructs_)++; - } - - // Probing for copies from coercion. - explicit CopyMoveCounter(const DerivedCopyMoveCounter& other) - : copies_(other.copies_), - assigns_(other.assigns_), - move_constructs_(other.move_constructs_), - move_assigns_(other.move_assigns_) { - (*copies_)++; - } - - // Probing for moves from coercion. - explicit CopyMoveCounter(DerivedCopyMoveCounter&& other) - : copies_(other.copies_), - assigns_(other.assigns_), - move_constructs_(other.move_constructs_), - move_assigns_(other.move_assigns_) { - (*move_constructs_)++; - } - - const CopyMoveCounter& operator=(const CopyMoveCounter& rhs) { - copies_ = rhs.copies_; - assigns_ = rhs.assigns_; - move_constructs_ = rhs.move_constructs_; - move_assigns_ = rhs.move_assigns_; - - (*assigns_)++; - - return *this; - } - - const CopyMoveCounter& operator=(CopyMoveCounter&& rhs) { - copies_ = rhs.copies_; - assigns_ = rhs.assigns_; - move_constructs_ = rhs.move_constructs_; - move_assigns_ = rhs.move_assigns_; - - (*move_assigns_)++; - - return *this; - } - - int copies() const { - return *copies_; - } - - private: - int* copies_; - int* assigns_; - int* move_constructs_; - int* move_assigns_; -}; - -// Used for probing the number of copies in an argument. The instance is a -// copyable and non-movable type. -class CopyCounter { - public: - CopyCounter(int* copies, int* assigns) - : counter_(copies, assigns, nullptr, nullptr) {} - CopyCounter(const CopyCounter& other) = default; - CopyCounter& operator=(const CopyCounter& other) = default; - - explicit CopyCounter(const DerivedCopyMoveCounter& other) : counter_(other) {} - - int copies() const { return counter_.copies(); } - - private: - CopyMoveCounter counter_; -}; - -// Used for probing the number of moves in an argument. The instance is a -// non-copyable and movable type. -class MoveCounter { - public: - MoveCounter(int* move_constructs, int* move_assigns) - : counter_(nullptr, nullptr, move_constructs, move_assigns) {} - MoveCounter(MoveCounter&& other) : counter_(std::move(other.counter_)) {} - MoveCounter& operator=(MoveCounter&& other) { - counter_ = std::move(other.counter_); - return *this; - } - - explicit MoveCounter(DerivedCopyMoveCounter&& other) - : counter_(std::move(other)) {} - - private: - CopyMoveCounter counter_; -}; - -class DeleteCounter { - public: - explicit DeleteCounter(int* deletes) - : deletes_(deletes) { - } - - ~DeleteCounter() { - (*deletes_)++; - } - - void VoidMethod0() {} - - private: - int* deletes_; -}; - -template -T PassThru(T scoper) { - return scoper; -} - -// Some test functions that we can Bind to. -template -T PolymorphicIdentity(T t) { - return t; -} - -template -struct VoidPolymorphic { - static void Run(Ts... t) {} -}; - -int Identity(int n) { - return n; -} - -int ArrayGet(const int array[], int n) { - return array[n]; -} - -int Sum(int a, int b, int c, int d, int e, int f) { - return a + b + c + d + e + f; -} - -const char* CStringIdentity(const char* s) { - return s; -} - -int GetCopies(const CopyMoveCounter& counter) { - return counter.copies(); -} - -int UnwrapNoRefParent(NoRefParent p) { - return p.value; -} - -int UnwrapNoRefParentPtr(NoRefParent* p) { - return p->value; -} - -int UnwrapNoRefParentConstRef(const NoRefParent& p) { - return p.value; -} - -void RefArgSet(int &n) { - n = 2; -} - -void PtrArgSet(int *n) { - *n = 2; -} - -int FunctionWithWeakFirstParam(WeakPtr o, int n) { - return n; -} - -int FunctionWithScopedRefptrFirstParam(const scoped_refptr& o, int n) { - return n; -} - -void TakesACallback(const Closure& callback) { - callback.Run(); -} - -int Noexcept() noexcept { - return 42; -} - -class BindTest : public ::testing::Test { - public: - BindTest() { - const_has_ref_ptr_ = &has_ref_; - const_no_ref_ptr_ = &no_ref_; - static_func_mock_ptr = &static_func_mock_; - } - - ~BindTest() override = default; - - static void VoidFunc0() { - static_func_mock_ptr->VoidMethod0(); - } - - static int IntFunc0() { return static_func_mock_ptr->IntMethod0(); } - int NoexceptMethod() noexcept { return 42; } - int ConstNoexceptMethod() const noexcept { return 42; } - - protected: - StrictMock no_ref_; - StrictMock has_ref_; - const HasRef* const_has_ref_ptr_; - const NoRef* const_no_ref_ptr_; - StrictMock static_func_mock_; - - // Used by the static functions to perform expectations. - static StrictMock* static_func_mock_ptr; - - private: - DISALLOW_COPY_AND_ASSIGN(BindTest); -}; - -StrictMock* BindTest::static_func_mock_ptr; -StrictMock* g_func_mock_ptr; - -void VoidFunc0() { - g_func_mock_ptr->VoidMethod0(); -} - -int IntFunc0() { - return g_func_mock_ptr->IntMethod0(); -} - -TEST_F(BindTest, BasicTest) { - Callback cb = Bind(&Sum, 32, 16, 8); - EXPECT_EQ(92, cb.Run(13, 12, 11)); - - Callback c1 = Bind(&Sum); - EXPECT_EQ(69, c1.Run(14, 13, 12, 11, 10, 9)); - - Callback c2 = Bind(c1, 32, 16, 8); - EXPECT_EQ(86, c2.Run(11, 10, 9)); - - Callback c3 = Bind(c2, 4, 2, 1); - EXPECT_EQ(63, c3.Run()); -} - -// Test that currying the rvalue result of another Bind() works correctly. -// - rvalue should be usable as argument to Bind(). -// - multiple runs of resulting Callback remain valid. -TEST_F(BindTest, CurryingRvalueResultOfBind) { - int n = 0; - RepeatingClosure cb = BindRepeating(&TakesACallback, - BindRepeating(&PtrArgSet, &n)); - - // If we implement Bind() such that the return value has auto_ptr-like - // semantics, the second call here will fail because ownership of - // the internal BindState<> would have been transfered to a *temporary* - // constructon of a Callback object on the first call. - cb.Run(); - EXPECT_EQ(2, n); - - n = 0; - cb.Run(); - EXPECT_EQ(2, n); -} - -TEST_F(BindTest, RepeatingCallbackBasicTest) { - RepeatingCallback c0 = BindRepeating(&Sum, 1, 2, 4, 8, 16); - - // RepeatingCallback can run via a lvalue-reference. - EXPECT_EQ(63, c0.Run(32)); - - // It is valid to call a RepeatingCallback more than once. - EXPECT_EQ(54, c0.Run(23)); - - // BindRepeating can handle a RepeatingCallback as the target functor. - RepeatingCallback c1 = BindRepeating(c0, 11); - - // RepeatingCallback can run via a rvalue-reference. - EXPECT_EQ(42, std::move(c1).Run()); - - // BindRepeating can handle a rvalue-reference of RepeatingCallback. - EXPECT_EQ(32, BindRepeating(std::move(c0), 1).Run()); -} - -TEST_F(BindTest, OnceCallbackBasicTest) { - OnceCallback c0 = BindOnce(&Sum, 1, 2, 4, 8, 16); - - // OnceCallback can run via a rvalue-reference. - EXPECT_EQ(63, std::move(c0).Run(32)); - - // After running via the rvalue-reference, the value of the OnceCallback - // is undefined. The implementation simply clears the instance after the - // invocation. - EXPECT_TRUE(c0.is_null()); - - c0 = BindOnce(&Sum, 2, 3, 5, 7, 11); - - // BindOnce can handle a rvalue-reference of OnceCallback as the target - // functor. - OnceCallback c1 = BindOnce(std::move(c0), 13); - EXPECT_EQ(41, std::move(c1).Run()); - - RepeatingCallback c2 = BindRepeating(&Sum, 2, 3, 5, 7, 11); - EXPECT_EQ(41, BindOnce(c2, 13).Run()); -} - -// IgnoreResult adapter test. -// - Function with return value. -// - Method with return value. -// - Const Method with return. -// - Method with return value bound to WeakPtr<>. -// - Const Method with return bound to WeakPtr<>. -TEST_F(BindTest, IgnoreResultForRepeating) { - EXPECT_CALL(static_func_mock_, IntMethod0()).WillOnce(Return(1337)); - EXPECT_CALL(has_ref_, AddRef()).Times(2); - EXPECT_CALL(has_ref_, Release()).Times(2); - EXPECT_CALL(has_ref_, IntMethod0()).WillOnce(Return(10)); - EXPECT_CALL(has_ref_, IntConstMethod0()).WillOnce(Return(11)); - EXPECT_CALL(no_ref_, IntMethod0()).WillOnce(Return(12)); - EXPECT_CALL(no_ref_, IntConstMethod0()).WillOnce(Return(13)); - - RepeatingClosure normal_func_cb = BindRepeating(IgnoreResult(&IntFunc0)); - normal_func_cb.Run(); - - RepeatingClosure non_void_method_cb = - BindRepeating(IgnoreResult(&HasRef::IntMethod0), &has_ref_); - non_void_method_cb.Run(); - - RepeatingClosure non_void_const_method_cb = - BindRepeating(IgnoreResult(&HasRef::IntConstMethod0), &has_ref_); - non_void_const_method_cb.Run(); - - WeakPtrFactory weak_factory(&no_ref_); - WeakPtrFactory const_weak_factory(const_no_ref_ptr_); - - RepeatingClosure non_void_weak_method_cb = - BindRepeating(IgnoreResult(&NoRef::IntMethod0), - weak_factory.GetWeakPtr()); - non_void_weak_method_cb.Run(); - - RepeatingClosure non_void_weak_const_method_cb = - BindRepeating(IgnoreResult(&NoRef::IntConstMethod0), - weak_factory.GetWeakPtr()); - non_void_weak_const_method_cb.Run(); - - weak_factory.InvalidateWeakPtrs(); - non_void_weak_const_method_cb.Run(); - non_void_weak_method_cb.Run(); -} - -TEST_F(BindTest, IgnoreResultForOnce) { - EXPECT_CALL(static_func_mock_, IntMethod0()).WillOnce(Return(1337)); - EXPECT_CALL(has_ref_, AddRef()).Times(2); - EXPECT_CALL(has_ref_, Release()).Times(2); - EXPECT_CALL(has_ref_, IntMethod0()).WillOnce(Return(10)); - EXPECT_CALL(has_ref_, IntConstMethod0()).WillOnce(Return(11)); - - OnceClosure normal_func_cb = BindOnce(IgnoreResult(&IntFunc0)); - std::move(normal_func_cb).Run(); - - OnceClosure non_void_method_cb = - BindOnce(IgnoreResult(&HasRef::IntMethod0), &has_ref_); - std::move(non_void_method_cb).Run(); - - OnceClosure non_void_const_method_cb = - BindOnce(IgnoreResult(&HasRef::IntConstMethod0), &has_ref_); - std::move(non_void_const_method_cb).Run(); - - WeakPtrFactory weak_factory(&no_ref_); - WeakPtrFactory const_weak_factory(const_no_ref_ptr_); - - OnceClosure non_void_weak_method_cb = - BindOnce(IgnoreResult(&NoRef::IntMethod0), - weak_factory.GetWeakPtr()); - OnceClosure non_void_weak_const_method_cb = - BindOnce(IgnoreResult(&NoRef::IntConstMethod0), - weak_factory.GetWeakPtr()); - - weak_factory.InvalidateWeakPtrs(); - std::move(non_void_weak_const_method_cb).Run(); - std::move(non_void_weak_method_cb).Run(); -} - -// Functions that take reference parameters. -// - Forced reference parameter type still stores a copy. -// - Forced const reference parameter type still stores a copy. -TEST_F(BindTest, ReferenceArgumentBindingForRepeating) { - int n = 1; - int& ref_n = n; - const int& const_ref_n = n; - - RepeatingCallback ref_copies_cb = BindRepeating(&Identity, ref_n); - EXPECT_EQ(n, ref_copies_cb.Run()); - n++; - EXPECT_EQ(n - 1, ref_copies_cb.Run()); - - RepeatingCallback const_ref_copies_cb = - BindRepeating(&Identity, const_ref_n); - EXPECT_EQ(n, const_ref_copies_cb.Run()); - n++; - EXPECT_EQ(n - 1, const_ref_copies_cb.Run()); -} - -TEST_F(BindTest, ReferenceArgumentBindingForOnce) { - int n = 1; - int& ref_n = n; - const int& const_ref_n = n; - - OnceCallback ref_copies_cb = BindOnce(&Identity, ref_n); - n++; - EXPECT_EQ(n - 1, std::move(ref_copies_cb).Run()); - - OnceCallback const_ref_copies_cb = - BindOnce(&Identity, const_ref_n); - n++; - EXPECT_EQ(n - 1, std::move(const_ref_copies_cb).Run()); -} - -// Check that we can pass in arrays and have them be stored as a pointer. -// - Array of values stores a pointer. -// - Array of const values stores a pointer. -TEST_F(BindTest, ArrayArgumentBindingForRepeating) { - int array[4] = {1, 1, 1, 1}; - const int (*const_array_ptr)[4] = &array; - - RepeatingCallback array_cb = BindRepeating(&ArrayGet, array, 1); - EXPECT_EQ(1, array_cb.Run()); - - RepeatingCallback const_array_cb = - BindRepeating(&ArrayGet, *const_array_ptr, 1); - EXPECT_EQ(1, const_array_cb.Run()); - - array[1] = 3; - EXPECT_EQ(3, array_cb.Run()); - EXPECT_EQ(3, const_array_cb.Run()); -} - -TEST_F(BindTest, ArrayArgumentBindingForOnce) { - int array[4] = {1, 1, 1, 1}; - const int (*const_array_ptr)[4] = &array; - - OnceCallback array_cb = BindOnce(&ArrayGet, array, 1); - OnceCallback const_array_cb = - BindOnce(&ArrayGet, *const_array_ptr, 1); - - array[1] = 3; - EXPECT_EQ(3, std::move(array_cb).Run()); - EXPECT_EQ(3, std::move(const_array_cb).Run()); -} - -// WeakPtr() support. -// - Method bound to WeakPtr<> to non-const object. -// - Const method bound to WeakPtr<> to non-const object. -// - Const method bound to WeakPtr<> to const object. -// - Normal Function with WeakPtr<> as P1 can have return type and is -// not canceled. -TEST_F(BindTest, WeakPtrForRepeating) { - EXPECT_CALL(no_ref_, VoidMethod0()); - EXPECT_CALL(no_ref_, VoidConstMethod0()).Times(2); - - WeakPtrFactory weak_factory(&no_ref_); - WeakPtrFactory const_weak_factory(const_no_ref_ptr_); - - RepeatingClosure method_cb = - BindRepeating(&NoRef::VoidMethod0, weak_factory.GetWeakPtr()); - method_cb.Run(); - - RepeatingClosure const_method_cb = - BindRepeating(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr()); - const_method_cb.Run(); - - RepeatingClosure const_method_const_ptr_cb = - BindRepeating(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr()); - const_method_const_ptr_cb.Run(); - - RepeatingCallback normal_func_cb = - BindRepeating(&FunctionWithWeakFirstParam, weak_factory.GetWeakPtr()); - EXPECT_EQ(1, normal_func_cb.Run(1)); - - weak_factory.InvalidateWeakPtrs(); - const_weak_factory.InvalidateWeakPtrs(); - - method_cb.Run(); - const_method_cb.Run(); - const_method_const_ptr_cb.Run(); - - // Still runs even after the pointers are invalidated. - EXPECT_EQ(2, normal_func_cb.Run(2)); -} - -TEST_F(BindTest, WeakPtrForOnce) { - WeakPtrFactory weak_factory(&no_ref_); - WeakPtrFactory const_weak_factory(const_no_ref_ptr_); - - OnceClosure method_cb = - BindOnce(&NoRef::VoidMethod0, weak_factory.GetWeakPtr()); - OnceClosure const_method_cb = - BindOnce(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr()); - OnceClosure const_method_const_ptr_cb = - BindOnce(&NoRef::VoidConstMethod0, const_weak_factory.GetWeakPtr()); - Callback normal_func_cb = - Bind(&FunctionWithWeakFirstParam, weak_factory.GetWeakPtr()); - - weak_factory.InvalidateWeakPtrs(); - const_weak_factory.InvalidateWeakPtrs(); - - std::move(method_cb).Run(); - std::move(const_method_cb).Run(); - std::move(const_method_const_ptr_cb).Run(); - - // Still runs even after the pointers are invalidated. - EXPECT_EQ(2, std::move(normal_func_cb).Run(2)); -} - -// ConstRef() wrapper support. -// - Binding w/o ConstRef takes a copy. -// - Binding a ConstRef takes a reference. -// - Binding ConstRef to a function ConstRef does not copy on invoke. -TEST_F(BindTest, ConstRefForRepeating) { - int n = 1; - - RepeatingCallback copy_cb = BindRepeating(&Identity, n); - RepeatingCallback const_ref_cb = BindRepeating(&Identity, ConstRef(n)); - EXPECT_EQ(n, copy_cb.Run()); - EXPECT_EQ(n, const_ref_cb.Run()); - n++; - EXPECT_EQ(n - 1, copy_cb.Run()); - EXPECT_EQ(n, const_ref_cb.Run()); - - int copies = 0; - int assigns = 0; - int move_constructs = 0; - int move_assigns = 0; - CopyMoveCounter counter(&copies, &assigns, &move_constructs, &move_assigns); - RepeatingCallback all_const_ref_cb = - BindRepeating(&GetCopies, ConstRef(counter)); - EXPECT_EQ(0, all_const_ref_cb.Run()); - EXPECT_EQ(0, copies); - EXPECT_EQ(0, assigns); - EXPECT_EQ(0, move_constructs); - EXPECT_EQ(0, move_assigns); -} - -TEST_F(BindTest, ConstRefForOnce) { - int n = 1; - - OnceCallback copy_cb = BindOnce(&Identity, n); - OnceCallback const_ref_cb = BindOnce(&Identity, ConstRef(n)); - n++; - EXPECT_EQ(n - 1, std::move(copy_cb).Run()); - EXPECT_EQ(n, std::move(const_ref_cb).Run()); - - int copies = 0; - int assigns = 0; - int move_constructs = 0; - int move_assigns = 0; - CopyMoveCounter counter(&copies, &assigns, &move_constructs, &move_assigns); - OnceCallback all_const_ref_cb = - BindOnce(&GetCopies, ConstRef(counter)); - EXPECT_EQ(0, std::move(all_const_ref_cb).Run()); - EXPECT_EQ(0, copies); - EXPECT_EQ(0, assigns); - EXPECT_EQ(0, move_constructs); - EXPECT_EQ(0, move_assigns); -} - -// Test Owned() support. -TEST_F(BindTest, OwnedForRepeating) { - int deletes = 0; - DeleteCounter* counter = new DeleteCounter(&deletes); - - // If we don't capture, delete happens on Callback destruction/reset. - // return the same value. - RepeatingCallback no_capture_cb = - BindRepeating(&PolymorphicIdentity, Owned(counter)); - ASSERT_EQ(counter, no_capture_cb.Run()); - ASSERT_EQ(counter, no_capture_cb.Run()); - EXPECT_EQ(0, deletes); - no_capture_cb.Reset(); // This should trigger a delete. - EXPECT_EQ(1, deletes); - - deletes = 0; - counter = new DeleteCounter(&deletes); - RepeatingClosure own_object_cb = - BindRepeating(&DeleteCounter::VoidMethod0, Owned(counter)); - own_object_cb.Run(); - EXPECT_EQ(0, deletes); - own_object_cb.Reset(); - EXPECT_EQ(1, deletes); -} - -TEST_F(BindTest, OwnedForOnce) { - int deletes = 0; - DeleteCounter* counter = new DeleteCounter(&deletes); - - // If we don't capture, delete happens on Callback destruction/reset. - // return the same value. - OnceCallback no_capture_cb = - BindOnce(&PolymorphicIdentity, Owned(counter)); - EXPECT_EQ(0, deletes); - no_capture_cb.Reset(); // This should trigger a delete. - EXPECT_EQ(1, deletes); - - deletes = 0; - counter = new DeleteCounter(&deletes); - OnceClosure own_object_cb = - BindOnce(&DeleteCounter::VoidMethod0, Owned(counter)); - EXPECT_EQ(0, deletes); - own_object_cb.Reset(); - EXPECT_EQ(1, deletes); -} - -template -class BindVariantsTest : public ::testing::Test { -}; - -struct RepeatingTestConfig { - template - using CallbackType = RepeatingCallback; - using ClosureType = RepeatingClosure; - - template - static CallbackType> - Bind(F&& f, Args&&... args) { - return BindRepeating(std::forward(f), std::forward(args)...); - } -}; - -struct OnceTestConfig { - template - using CallbackType = OnceCallback; - using ClosureType = OnceClosure; - - template - static CallbackType> - Bind(F&& f, Args&&... args) { - return BindOnce(std::forward(f), std::forward(args)...); - } -}; - -using BindVariantsTestConfig = ::testing::Types< - RepeatingTestConfig, OnceTestConfig>; -TYPED_TEST_CASE(BindVariantsTest, BindVariantsTestConfig); - -template -using CallbackType = typename TypeParam::template CallbackType; - -// Function type support. -// - Normal function. -// - Normal function bound with non-refcounted first argument. -// - Method bound to non-const object. -// - Method bound to scoped_refptr. -// - Const method bound to non-const object. -// - Const method bound to const object. -// - Derived classes can be used with pointers to non-virtual base functions. -// - Derived classes can be used with pointers to virtual base functions (and -// preserve virtual dispatch). -TYPED_TEST(BindVariantsTest, FunctionTypeSupport) { - using ClosureType = typename TypeParam::ClosureType; - - StrictMock has_ref; - StrictMock no_ref; - StrictMock static_func_mock; - const HasRef* const_has_ref_ptr = &has_ref; - g_func_mock_ptr = &static_func_mock; - - EXPECT_CALL(static_func_mock, VoidMethod0()); - EXPECT_CALL(has_ref, AddRef()).Times(4); - EXPECT_CALL(has_ref, Release()).Times(4); - EXPECT_CALL(has_ref, VoidMethod0()).Times(2); - EXPECT_CALL(has_ref, VoidConstMethod0()).Times(2); - - ClosureType normal_cb = TypeParam::Bind(&VoidFunc0); - CallbackType normal_non_refcounted_cb = - TypeParam::Bind(&PolymorphicIdentity, &no_ref); - std::move(normal_cb).Run(); - EXPECT_EQ(&no_ref, std::move(normal_non_refcounted_cb).Run()); - - ClosureType method_cb = TypeParam::Bind(&HasRef::VoidMethod0, &has_ref); - ClosureType method_refptr_cb = - TypeParam::Bind(&HasRef::VoidMethod0, WrapRefCounted(&has_ref)); - ClosureType const_method_nonconst_obj_cb = - TypeParam::Bind(&HasRef::VoidConstMethod0, &has_ref); - ClosureType const_method_const_obj_cb = - TypeParam::Bind(&HasRef::VoidConstMethod0, const_has_ref_ptr); - std::move(method_cb).Run(); - std::move(method_refptr_cb).Run(); - std::move(const_method_nonconst_obj_cb).Run(); - std::move(const_method_const_obj_cb).Run(); - - Child child; - child.value = 0; - ClosureType virtual_set_cb = TypeParam::Bind(&Parent::VirtualSet, &child); - std::move(virtual_set_cb).Run(); - EXPECT_EQ(kChildValue, child.value); - - child.value = 0; - ClosureType non_virtual_set_cb = - TypeParam::Bind(&Parent::NonVirtualSet, &child); - std::move(non_virtual_set_cb).Run(); - EXPECT_EQ(kParentValue, child.value); -} - -// Return value support. -// - Function with return value. -// - Method with return value. -// - Const method with return value. -// - Move-only return value. -TYPED_TEST(BindVariantsTest, ReturnValues) { - StrictMock static_func_mock; - StrictMock has_ref; - g_func_mock_ptr = &static_func_mock; - const HasRef* const_has_ref_ptr = &has_ref; - - EXPECT_CALL(static_func_mock, IntMethod0()).WillOnce(Return(1337)); - EXPECT_CALL(has_ref, AddRef()).Times(4); - EXPECT_CALL(has_ref, Release()).Times(4); - EXPECT_CALL(has_ref, IntMethod0()).WillOnce(Return(31337)); - EXPECT_CALL(has_ref, IntConstMethod0()) - .WillOnce(Return(41337)) - .WillOnce(Return(51337)); - EXPECT_CALL(has_ref, UniquePtrMethod0()) - .WillOnce(Return(ByMove(std::make_unique(42)))); - - CallbackType normal_cb = TypeParam::Bind(&IntFunc0); - CallbackType method_cb = - TypeParam::Bind(&HasRef::IntMethod0, &has_ref); - CallbackType const_method_nonconst_obj_cb = - TypeParam::Bind(&HasRef::IntConstMethod0, &has_ref); - CallbackType const_method_const_obj_cb = - TypeParam::Bind(&HasRef::IntConstMethod0, const_has_ref_ptr); - CallbackType()> move_only_rv_cb = - TypeParam::Bind(&HasRef::UniquePtrMethod0, &has_ref); - EXPECT_EQ(1337, std::move(normal_cb).Run()); - EXPECT_EQ(31337, std::move(method_cb).Run()); - EXPECT_EQ(41337, std::move(const_method_nonconst_obj_cb).Run()); - EXPECT_EQ(51337, std::move(const_method_const_obj_cb).Run()); - EXPECT_EQ(42, *std::move(move_only_rv_cb).Run()); -} - -// Argument binding tests. -// - Argument binding to primitive. -// - Argument binding to primitive pointer. -// - Argument binding to a literal integer. -// - Argument binding to a literal string. -// - Argument binding with template function. -// - Argument binding to an object. -// - Argument binding to pointer to incomplete type. -// - Argument gets type converted. -// - Pointer argument gets converted. -// - Const Reference forces conversion. -TYPED_TEST(BindVariantsTest, ArgumentBinding) { - int n = 2; - - EXPECT_EQ(n, TypeParam::Bind(&Identity, n).Run()); - EXPECT_EQ(&n, TypeParam::Bind(&PolymorphicIdentity, &n).Run()); - EXPECT_EQ(3, TypeParam::Bind(&Identity, 3).Run()); - EXPECT_STREQ("hi", TypeParam::Bind(&CStringIdentity, "hi").Run()); - EXPECT_EQ(4, TypeParam::Bind(&PolymorphicIdentity, 4).Run()); - - NoRefParent p; - p.value = 5; - EXPECT_EQ(5, TypeParam::Bind(&UnwrapNoRefParent, p).Run()); - - IncompleteType* incomplete_ptr = reinterpret_cast(123); - EXPECT_EQ(incomplete_ptr, - TypeParam::Bind(&PolymorphicIdentity, - incomplete_ptr).Run()); - - NoRefChild c; - c.value = 6; - EXPECT_EQ(6, TypeParam::Bind(&UnwrapNoRefParent, c).Run()); - - c.value = 7; - EXPECT_EQ(7, TypeParam::Bind(&UnwrapNoRefParentPtr, &c).Run()); - - c.value = 8; - EXPECT_EQ(8, TypeParam::Bind(&UnwrapNoRefParentConstRef, c).Run()); -} - -// Unbound argument type support tests. -// - Unbound value. -// - Unbound pointer. -// - Unbound reference. -// - Unbound const reference. -// - Unbound unsized array. -// - Unbound sized array. -// - Unbound array-of-arrays. -TYPED_TEST(BindVariantsTest, UnboundArgumentTypeSupport) { - CallbackType unbound_value_cb = - TypeParam::Bind(&VoidPolymorphic::Run); - CallbackType unbound_pointer_cb = - TypeParam::Bind(&VoidPolymorphic::Run); - CallbackType unbound_ref_cb = - TypeParam::Bind(&VoidPolymorphic::Run); - CallbackType unbound_const_ref_cb = - TypeParam::Bind(&VoidPolymorphic::Run); - CallbackType unbound_unsized_array_cb = - TypeParam::Bind(&VoidPolymorphic::Run); - CallbackType unbound_sized_array_cb = - TypeParam::Bind(&VoidPolymorphic::Run); - CallbackType unbound_array_of_arrays_cb = - TypeParam::Bind(&VoidPolymorphic::Run); - CallbackType unbound_ref_with_bound_arg = - TypeParam::Bind(&VoidPolymorphic::Run, 1); -} - -// Function with unbound reference parameter. -// - Original parameter is modified by callback. -TYPED_TEST(BindVariantsTest, UnboundReferenceSupport) { - int n = 0; - CallbackType unbound_ref_cb = - TypeParam::Bind(&RefArgSet); - std::move(unbound_ref_cb).Run(n); - EXPECT_EQ(2, n); -} - -// Unretained() wrapper support. -// - Method bound to Unretained() non-const object. -// - Const method bound to Unretained() non-const object. -// - Const method bound to Unretained() const object. -TYPED_TEST(BindVariantsTest, Unretained) { - StrictMock no_ref; - const NoRef* const_no_ref_ptr = &no_ref; - - EXPECT_CALL(no_ref, VoidMethod0()); - EXPECT_CALL(no_ref, VoidConstMethod0()).Times(2); - - TypeParam::Bind(&NoRef::VoidMethod0, Unretained(&no_ref)).Run(); - TypeParam::Bind(&NoRef::VoidConstMethod0, Unretained(&no_ref)).Run(); - TypeParam::Bind(&NoRef::VoidConstMethod0, Unretained(const_no_ref_ptr)).Run(); -} - -TYPED_TEST(BindVariantsTest, ScopedRefptr) { - StrictMock has_ref; - EXPECT_CALL(has_ref, AddRef()).Times(1); - EXPECT_CALL(has_ref, Release()).Times(1); - - const scoped_refptr refptr(&has_ref); - CallbackType scoped_refptr_const_ref_cb = - TypeParam::Bind(&FunctionWithScopedRefptrFirstParam, - base::ConstRef(refptr), 1); - EXPECT_EQ(1, std::move(scoped_refptr_const_ref_cb).Run()); -} - -TYPED_TEST(BindVariantsTest, UniquePtrReceiver) { - std::unique_ptr> no_ref(new StrictMock); - EXPECT_CALL(*no_ref, VoidMethod0()).Times(1); - TypeParam::Bind(&NoRef::VoidMethod0, std::move(no_ref)).Run(); -} - -// Tests for Passed() wrapper support: -// - Passed() can be constructed from a pointer to scoper. -// - Passed() can be constructed from a scoper rvalue. -// - Using Passed() gives Callback Ownership. -// - Ownership is transferred from Callback to callee on the first Run(). -// - Callback supports unbound arguments. -template -class BindMoveOnlyTypeTest : public ::testing::Test { -}; - -struct CustomDeleter { - void operator()(DeleteCounter* c) { delete c; } -}; - -using MoveOnlyTypesToTest = - ::testing::Types, - std::unique_ptr>; -TYPED_TEST_CASE(BindMoveOnlyTypeTest, MoveOnlyTypesToTest); - -TYPED_TEST(BindMoveOnlyTypeTest, PassedToBoundCallback) { - int deletes = 0; - - TypeParam ptr(new DeleteCounter(&deletes)); - Callback callback = Bind(&PassThru, Passed(&ptr)); - EXPECT_FALSE(ptr.get()); - EXPECT_EQ(0, deletes); - - // If we never invoke the Callback, it retains ownership and deletes. - callback.Reset(); - EXPECT_EQ(1, deletes); -} - -TYPED_TEST(BindMoveOnlyTypeTest, PassedWithRvalue) { - int deletes = 0; - Callback callback = Bind( - &PassThru, Passed(TypeParam(new DeleteCounter(&deletes)))); - EXPECT_EQ(0, deletes); - - // If we never invoke the Callback, it retains ownership and deletes. - callback.Reset(); - EXPECT_EQ(1, deletes); -} - -// Check that ownership can be transferred back out. -TYPED_TEST(BindMoveOnlyTypeTest, ReturnMoveOnlyType) { - int deletes = 0; - DeleteCounter* counter = new DeleteCounter(&deletes); - Callback callback = - Bind(&PassThru, Passed(TypeParam(counter))); - TypeParam result = callback.Run(); - ASSERT_EQ(counter, result.get()); - EXPECT_EQ(0, deletes); - - // Resetting does not delete since ownership was transferred. - callback.Reset(); - EXPECT_EQ(0, deletes); - - // Ensure that we actually did get ownership. - result.reset(); - EXPECT_EQ(1, deletes); -} - -TYPED_TEST(BindMoveOnlyTypeTest, UnboundForwarding) { - int deletes = 0; - TypeParam ptr(new DeleteCounter(&deletes)); - // Test unbound argument forwarding. - Callback cb_unbound = Bind(&PassThru); - cb_unbound.Run(std::move(ptr)); - EXPECT_EQ(1, deletes); -} - -void VerifyVector(const std::vector>& v) { - ASSERT_EQ(1u, v.size()); - EXPECT_EQ(12345, *v[0]); -} - -std::vector> AcceptAndReturnMoveOnlyVector( - std::vector> v) { - VerifyVector(v); - return v; -} - -// Test that a vector containing move-only types can be used with Callback. -TEST_F(BindTest, BindMoveOnlyVector) { - using MoveOnlyVector = std::vector>; - - MoveOnlyVector v; - v.push_back(WrapUnique(new int(12345))); - - // Early binding should work: - base::Callback bound_cb = - base::Bind(&AcceptAndReturnMoveOnlyVector, Passed(&v)); - MoveOnlyVector intermediate_result = bound_cb.Run(); - VerifyVector(intermediate_result); - - // As should passing it as an argument to Run(): - base::Callback unbound_cb = - base::Bind(&AcceptAndReturnMoveOnlyVector); - MoveOnlyVector final_result = unbound_cb.Run(std::move(intermediate_result)); - VerifyVector(final_result); -} - -// Argument copy-constructor usage for non-reference copy-only parameters. -// - Bound arguments are only copied once. -// - Forwarded arguments are only copied once. -// - Forwarded arguments with coercions are only copied twice (once for the -// coercion, and one for the final dispatch). -TEST_F(BindTest, ArgumentCopies) { - int copies = 0; - int assigns = 0; - - CopyCounter counter(&copies, &assigns); - Bind(&VoidPolymorphic::Run, counter); - EXPECT_EQ(1, copies); - EXPECT_EQ(0, assigns); - - copies = 0; - assigns = 0; - Bind(&VoidPolymorphic::Run, CopyCounter(&copies, &assigns)); - EXPECT_EQ(1, copies); - EXPECT_EQ(0, assigns); - - copies = 0; - assigns = 0; - Bind(&VoidPolymorphic::Run).Run(counter); - EXPECT_EQ(2, copies); - EXPECT_EQ(0, assigns); - - copies = 0; - assigns = 0; - Bind(&VoidPolymorphic::Run).Run(CopyCounter(&copies, &assigns)); - EXPECT_EQ(1, copies); - EXPECT_EQ(0, assigns); - - copies = 0; - assigns = 0; - DerivedCopyMoveCounter derived(&copies, &assigns, nullptr, nullptr); - Bind(&VoidPolymorphic::Run).Run(CopyCounter(derived)); - EXPECT_EQ(2, copies); - EXPECT_EQ(0, assigns); - - copies = 0; - assigns = 0; - Bind(&VoidPolymorphic::Run) - .Run(CopyCounter( - DerivedCopyMoveCounter(&copies, &assigns, nullptr, nullptr))); - EXPECT_EQ(2, copies); - EXPECT_EQ(0, assigns); -} - -// Argument move-constructor usage for move-only parameters. -// - Bound arguments passed by move are not copied. -TEST_F(BindTest, ArgumentMoves) { - int move_constructs = 0; - int move_assigns = 0; - - Bind(&VoidPolymorphic::Run, - MoveCounter(&move_constructs, &move_assigns)); - EXPECT_EQ(1, move_constructs); - EXPECT_EQ(0, move_assigns); - - // TODO(tzik): Support binding move-only type into a non-reference parameter - // of a variant of Callback. - - move_constructs = 0; - move_assigns = 0; - Bind(&VoidPolymorphic::Run) - .Run(MoveCounter(&move_constructs, &move_assigns)); - EXPECT_EQ(1, move_constructs); - EXPECT_EQ(0, move_assigns); - - move_constructs = 0; - move_assigns = 0; - Bind(&VoidPolymorphic::Run) - .Run(MoveCounter(DerivedCopyMoveCounter( - nullptr, nullptr, &move_constructs, &move_assigns))); - EXPECT_EQ(2, move_constructs); - EXPECT_EQ(0, move_assigns); -} - -// Argument constructor usage for non-reference movable-copyable -// parameters. -// - Bound arguments passed by move are not copied. -// - Forwarded arguments are only copied once. -// - Forwarded arguments with coercions are only copied once and moved once. -TEST_F(BindTest, ArgumentCopiesAndMoves) { - int copies = 0; - int assigns = 0; - int move_constructs = 0; - int move_assigns = 0; - - CopyMoveCounter counter(&copies, &assigns, &move_constructs, &move_assigns); - Bind(&VoidPolymorphic::Run, counter); - EXPECT_EQ(1, copies); - EXPECT_EQ(0, assigns); - EXPECT_EQ(0, move_constructs); - EXPECT_EQ(0, move_assigns); - - copies = 0; - assigns = 0; - move_constructs = 0; - move_assigns = 0; - Bind(&VoidPolymorphic::Run, - CopyMoveCounter(&copies, &assigns, &move_constructs, &move_assigns)); - EXPECT_EQ(0, copies); - EXPECT_EQ(0, assigns); - EXPECT_EQ(1, move_constructs); - EXPECT_EQ(0, move_assigns); - - copies = 0; - assigns = 0; - move_constructs = 0; - move_assigns = 0; - Bind(&VoidPolymorphic::Run).Run(counter); - EXPECT_EQ(1, copies); - EXPECT_EQ(0, assigns); - EXPECT_EQ(1, move_constructs); - EXPECT_EQ(0, move_assigns); - - copies = 0; - assigns = 0; - move_constructs = 0; - move_assigns = 0; - Bind(&VoidPolymorphic::Run) - .Run(CopyMoveCounter(&copies, &assigns, &move_constructs, &move_assigns)); - EXPECT_EQ(0, copies); - EXPECT_EQ(0, assigns); - EXPECT_EQ(1, move_constructs); - EXPECT_EQ(0, move_assigns); - - DerivedCopyMoveCounter derived_counter(&copies, &assigns, &move_constructs, - &move_assigns); - copies = 0; - assigns = 0; - move_constructs = 0; - move_assigns = 0; - Bind(&VoidPolymorphic::Run) - .Run(CopyMoveCounter(derived_counter)); - EXPECT_EQ(1, copies); - EXPECT_EQ(0, assigns); - EXPECT_EQ(1, move_constructs); - EXPECT_EQ(0, move_assigns); - - copies = 0; - assigns = 0; - move_constructs = 0; - move_assigns = 0; - Bind(&VoidPolymorphic::Run) - .Run(CopyMoveCounter(DerivedCopyMoveCounter( - &copies, &assigns, &move_constructs, &move_assigns))); - EXPECT_EQ(0, copies); - EXPECT_EQ(0, assigns); - EXPECT_EQ(2, move_constructs); - EXPECT_EQ(0, move_assigns); -} - -TEST_F(BindTest, CapturelessLambda) { - EXPECT_FALSE(internal::IsCallableObject::value); - EXPECT_FALSE(internal::IsCallableObject::value); - EXPECT_FALSE(internal::IsCallableObject::value); - EXPECT_FALSE(internal::IsCallableObject::value); - - auto f = []() {}; - EXPECT_TRUE(internal::IsCallableObject::value); - - int i = 0; - auto g = [i]() { (void)i; }; - EXPECT_TRUE(internal::IsCallableObject::value); - - auto h = [](int, double) { return 'k'; }; - EXPECT_TRUE((std::is_same< - char(int, double), - internal::ExtractCallableRunType>::value)); - - EXPECT_EQ(42, Bind([] { return 42; }).Run()); - EXPECT_EQ(42, Bind([](int i) { return i * 7; }, 6).Run()); - - int x = 1; - base::Callback cb = - Bind([](int* x, int i) { *x *= i; }, Unretained(&x)); - cb.Run(6); - EXPECT_EQ(6, x); - cb.Run(7); - EXPECT_EQ(42, x); -} - -TEST_F(BindTest, EmptyFunctor) { - struct NonEmptyFunctor { - int operator()() const { return x; } - int x = 42; - }; - - struct EmptyFunctor { - int operator()() { return 42; } - }; - - struct EmptyFunctorConst { - int operator()() const { return 42; } - }; - - EXPECT_TRUE(internal::IsCallableObject::value); - EXPECT_TRUE(internal::IsCallableObject::value); - EXPECT_TRUE(internal::IsCallableObject::value); - EXPECT_EQ(42, BindOnce(EmptyFunctor()).Run()); - EXPECT_EQ(42, BindOnce(EmptyFunctorConst()).Run()); - EXPECT_EQ(42, BindRepeating(EmptyFunctorConst()).Run()); -} - -TEST_F(BindTest, CapturingLambdaForTesting) { - int x = 6; - EXPECT_EQ(42, BindLambdaForTesting([=](int y) { return x * y; }).Run(7)); - - auto f = [x](std::unique_ptr y) { return x * *y; }; - EXPECT_EQ(42, BindLambdaForTesting(f).Run(std::make_unique(7))); -} - -TEST_F(BindTest, Cancellation) { - EXPECT_CALL(no_ref_, VoidMethodWithIntArg(_)).Times(2); - - WeakPtrFactory weak_factory(&no_ref_); - RepeatingCallback cb = - BindRepeating(&NoRef::VoidMethodWithIntArg, weak_factory.GetWeakPtr()); - RepeatingClosure cb2 = BindRepeating(cb, 8); - OnceClosure cb3 = BindOnce(cb, 8); - - OnceCallback cb4 = - BindOnce(&NoRef::VoidMethodWithIntArg, weak_factory.GetWeakPtr()); - EXPECT_FALSE(cb4.IsCancelled()); - - OnceClosure cb5 = BindOnce(std::move(cb4), 8); - - EXPECT_FALSE(cb.IsCancelled()); - EXPECT_FALSE(cb2.IsCancelled()); - EXPECT_FALSE(cb3.IsCancelled()); - EXPECT_FALSE(cb5.IsCancelled()); - - cb.Run(6); - cb2.Run(); - - weak_factory.InvalidateWeakPtrs(); - - EXPECT_TRUE(cb.IsCancelled()); - EXPECT_TRUE(cb2.IsCancelled()); - EXPECT_TRUE(cb3.IsCancelled()); - EXPECT_TRUE(cb5.IsCancelled()); - - cb.Run(6); - cb2.Run(); - std::move(cb3).Run(); - std::move(cb5).Run(); -} - -TEST_F(BindTest, OnceCallback) { - // Check if Callback variants have declarations of conversions as expected. - // Copy constructor and assignment of RepeatingCallback. - static_assert(std::is_constructible< - RepeatingClosure, const RepeatingClosure&>::value, - "RepeatingClosure should be copyable."); - static_assert( - std::is_assignable::value, - "RepeatingClosure should be copy-assignable."); - - // Move constructor and assignment of RepeatingCallback. - static_assert(std::is_constructible< - RepeatingClosure, RepeatingClosure&&>::value, - "RepeatingClosure should be movable."); - static_assert(std::is_assignable::value, - "RepeatingClosure should be move-assignable"); - - // Conversions from OnceCallback to RepeatingCallback. - static_assert(!std::is_constructible< - RepeatingClosure, const OnceClosure&>::value, - "OnceClosure should not be convertible to RepeatingClosure."); - static_assert( - !std::is_assignable::value, - "OnceClosure should not be convertible to RepeatingClosure."); - - // Destructive conversions from OnceCallback to RepeatingCallback. - static_assert(!std::is_constructible< - RepeatingClosure, OnceClosure&&>::value, - "OnceClosure should not be convertible to RepeatingClosure."); - static_assert(!std::is_assignable::value, - "OnceClosure should not be convertible to RepeatingClosure."); - - // Copy constructor and assignment of OnceCallback. - static_assert(!std::is_constructible< - OnceClosure, const OnceClosure&>::value, - "OnceClosure should not be copyable."); - static_assert(!std::is_assignable::value, - "OnceClosure should not be copy-assignable"); - - // Move constructor and assignment of OnceCallback. - static_assert(std::is_constructible< - OnceClosure, OnceClosure&&>::value, - "OnceClosure should be movable."); - static_assert(std::is_assignable::value, - "OnceClosure should be move-assignable."); - - // Conversions from RepeatingCallback to OnceCallback. - static_assert(std::is_constructible< - OnceClosure, const RepeatingClosure&>::value, - "RepeatingClosure should be convertible to OnceClosure."); - static_assert(std::is_assignable::value, - "RepeatingClosure should be convertible to OnceClosure."); - - // Destructive conversions from RepeatingCallback to OnceCallback. - static_assert(std::is_constructible< - OnceClosure, RepeatingClosure&&>::value, - "RepeatingClosure should be convertible to OnceClosure."); - static_assert(std::is_assignable::value, - "RepeatingClosure should be covretible to OnceClosure."); - - OnceClosure cb = BindOnce(&VoidPolymorphic<>::Run); - std::move(cb).Run(); - - // RepeatingCallback should be convertible to OnceCallback. - OnceClosure cb2 = BindRepeating(&VoidPolymorphic<>::Run); - std::move(cb2).Run(); - - RepeatingClosure cb3 = BindRepeating(&VoidPolymorphic<>::Run); - cb = cb3; - std::move(cb).Run(); - - cb = std::move(cb2); - - OnceCallback cb4 = - BindOnce(&VoidPolymorphic, int>::Run, - std::make_unique(0)); - BindOnce(std::move(cb4), 1).Run(); -} - -// Callback construction and assignment tests. -// - Construction from an InvokerStorageHolder should not cause ref/deref. -// - Assignment from other callback should only cause one ref -// -// TODO(ajwong): Is there actually a way to test this? - -#if defined(OS_WIN) -int __fastcall FastCallFunc(int n) { - return n; -} - -int __stdcall StdCallFunc(int n) { - return n; -} - -// Windows specific calling convention support. -// - Can bind a __fastcall function. -// - Can bind a __stdcall function. -TEST_F(BindTest, WindowsCallingConventions) { - Callback fastcall_cb = Bind(&FastCallFunc, 1); - EXPECT_EQ(1, fastcall_cb.Run()); - - Callback stdcall_cb = Bind(&StdCallFunc, 2); - EXPECT_EQ(2, stdcall_cb.Run()); -} -#endif - -// Test unwrapping the various wrapping functions. - -TEST_F(BindTest, UnwrapUnretained) { - int i = 0; - auto unretained = Unretained(&i); - EXPECT_EQ(&i, internal::Unwrap(unretained)); - EXPECT_EQ(&i, internal::Unwrap(std::move(unretained))); -} - -TEST_F(BindTest, UnwrapConstRef) { - int p = 0; - auto const_ref = ConstRef(p); - EXPECT_EQ(&p, &internal::Unwrap(const_ref)); - EXPECT_EQ(&p, &internal::Unwrap(std::move(const_ref))); -} - -TEST_F(BindTest, UnwrapRetainedRef) { - auto p = MakeRefCounted>(); - auto retained_ref = RetainedRef(p); - EXPECT_EQ(p.get(), internal::Unwrap(retained_ref)); - EXPECT_EQ(p.get(), internal::Unwrap(std::move(retained_ref))); -} - -TEST_F(BindTest, UnwrapOwned) { - int* p = new int; - auto owned = Owned(p); - EXPECT_EQ(p, internal::Unwrap(owned)); - EXPECT_EQ(p, internal::Unwrap(std::move(owned))); -} - -TEST_F(BindTest, UnwrapPassed) { - int* p = new int; - auto passed = Passed(WrapUnique(p)); - EXPECT_EQ(p, internal::Unwrap(passed).get()); - - p = new int; - EXPECT_EQ(p, internal::Unwrap(Passed(WrapUnique(p))).get()); -} - -TEST_F(BindTest, BindNoexcept) { - EXPECT_EQ(42, base::BindOnce(&Noexcept).Run()); - EXPECT_EQ( - 42, - base::BindOnce(&BindTest::NoexceptMethod, base::Unretained(this)).Run()); - EXPECT_EQ( - 42, base::BindOnce(&BindTest::ConstNoexceptMethod, base::Unretained(this)) - .Run()); -} - -// Test null callbacks cause a DCHECK. -TEST(BindDeathTest, NullCallback) { - base::Callback null_cb; - ASSERT_TRUE(null_cb.is_null()); - EXPECT_DCHECK_DEATH(base::Bind(null_cb, 42)); -} - -} // namespace -} // namespace base diff --git a/bind_unittest.nc b/bind_unittest.nc deleted file mode 100644 index d549d2e8a..000000000 --- a/bind_unittest.nc +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a "No Compile Test" suite. -// http://dev.chromium.org/developers/testing/no-compile-tests - -#include - -#include "base/bind.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/test/bind_test_util.h" - -namespace base { - -// Do not put everything inside an anonymous namespace. If you do, many of the -// helper function declarations will generate unused definition warnings. - -static const int kParentValue = 1; -static const int kChildValue = 2; - -class NoRef { - public: - void VoidMethod0() {} - void VoidConstMethod0() const {} - int IntMethod0() { return 1; } -}; - -class HasRef : public NoRef, public base::RefCounted { -}; - -class Parent { - public: - void AddRef() const {} - void Release() const {} - virtual void VirtualSet() { value = kParentValue; } - void NonVirtualSet() { value = kParentValue; } - int value; -}; - -class Child : public Parent { - public: - virtual void VirtualSet() { value = kChildValue; } - void NonVirtualSet() { value = kChildValue; } -}; - -class NoRefParent { - public: - virtual void VirtualSet() { value = kParentValue; } - void NonVirtualSet() { value = kParentValue; } - int value; -}; - -class NoRefChild : public NoRefParent { - virtual void VirtualSet() { value = kChildValue; } - void NonVirtualSet() { value = kChildValue; } -}; - -template -T PolymorphicIdentity(T t) { - return t; -} - -int UnwrapParentRef(Parent& p) { - return p.value; -} - -template -void VoidPolymorphic1(T t) { -} - -void TakesMoveOnly(std::unique_ptr) { -} - -struct NonEmptyFunctor { - int x; - void operator()() const {} -}; - -// TODO(hans): Remove .* and update the static_assert expectations once we roll -// past Clang r313315. https://crbug.com/765692. - -#if defined(NCTEST_METHOD_ON_CONST_OBJECT) // [r"fatal error: static_assert failed .*\"Bound argument \|i\| of type \|Arg\| cannot be forwarded as \|Unwrapped\| to the bound functor, which declares it as \|Param\|\.\""] - -// Method bound to const-object. -// -// Only const methods should be allowed to work with const objects. -void WontCompile() { - HasRef has_ref; - const HasRef* const_has_ref_ptr_ = &has_ref; - Callback method_to_const_cb = - Bind(&HasRef::VoidMethod0, const_has_ref_ptr_); - method_to_const_cb.Run(); -} - -#elif defined(NCTEST_METHOD_BIND_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed \"Receivers may not be raw pointers\."] - - -// Method bound to non-refcounted object. -// -// We require refcounts unless you have Unretained(). -void WontCompile() { - NoRef no_ref; - Callback no_ref_cb = - Bind(&NoRef::VoidMethod0, &no_ref); - no_ref_cb.Run(); -} - -#elif defined(NCTEST_CONST_METHOD_NEEDS_REFCOUNTED_OBJECT) // [r"fatal error: static_assert failed \"Receivers may not be raw pointers\."] - -// Const Method bound to non-refcounted object. -// -// We require refcounts unless you have Unretained(). -void WontCompile() { - NoRef no_ref; - Callback no_ref_const_cb = - Bind(&NoRef::VoidConstMethod0, &no_ref); - no_ref_const_cb.Run(); -} - -#elif defined(NCTEST_CONST_POINTER) // [r"fatal error: static_assert failed .*\"Bound argument \|i\| of type \|Arg\| cannot be forwarded as \|Unwrapped\| to the bound functor, which declares it as \|Param\|\.\""] - -// Const argument used with non-const pointer parameter of same type. -// -// This is just a const-correctness check. -void WontCompile() { - const NoRef* const_no_ref_ptr; - Callback pointer_same_cb = - Bind(&PolymorphicIdentity, const_no_ref_ptr); - pointer_same_cb.Run(); -} - -#elif defined(NCTEST_CONST_POINTER_SUBTYPE) // [r"fatal error: static_assert failed .*\"Bound argument \|i\| of type \|Arg\| cannot be forwarded as \|Unwrapped\| to the bound functor, which declares it as \|Param\|\.\""] - -// Const argument used with non-const pointer parameter of super type. -// -// This is just a const-correctness check. -void WontCompile() { - const NoRefChild* const_child_ptr; - Callback pointer_super_cb = - Bind(&PolymorphicIdentity, const_child_ptr); - pointer_super_cb.Run(); -} - -#elif defined(DISABLED_NCTEST_DISALLOW_NON_CONST_REF_PARAM) // [r"fatal error: no member named 'AddRef' in 'base::NoRef'"] -// TODO(dcheng): I think there's a type safety promotion issue here where we can -// pass a const ref to a non const-ref function, or vice versa accidentally. Or -// we make a copy accidentally. Check. - -// Functions with reference parameters, unsupported. -// -// First, non-const reference parameters are disallowed by the Google -// style guide. Second, since we are doing argument forwarding it becomes -// very tricky to avoid copies, maintain const correctness, and not -// accidentally have the function be modifying a temporary, or a copy. -void WontCompile() { - Parent p; - Callback ref_arg_cb = Bind(&UnwrapParentRef); - ref_arg_cb.Run(p); -} - -#elif defined(NCTEST_DISALLOW_BIND_TO_NON_CONST_REF_PARAM) // [r"fatal error: static_assert failed .*\"Bound argument \|i\| of type \|Arg\| cannot be forwarded as \|Unwrapped\| to the bound functor, which declares it as \|Param\|\.\""] - -// Binding functions with reference parameters, unsupported. -// -// See comment in NCTEST_DISALLOW_NON_CONST_REF_PARAM -void WontCompile() { - Parent p; - Callback ref_cb = Bind(&UnwrapParentRef, p); - ref_cb.Run(); -} - -#elif defined(NCTEST_NO_IMPLICIT_ARRAY_PTR_CONVERSION) // [r"fatal error: static_assert failed .*\"First bound argument to a method cannot be an array\.\""] - -// A method should not be bindable with an array of objects. -// -// This is likely not wanted behavior. We specifically check for it though -// because it is possible, depending on how you implement prebinding, to -// implicitly convert an array type to a pointer type. -void WontCompile() { - HasRef p[10]; - Callback method_bound_to_array_cb = - Bind(&HasRef::VoidMethod0, p); - method_bound_to_array_cb.Run(); -} - -#elif defined(NCTEST_NO_RVALUE_RAW_PTR_FOR_REFCOUNTED_TYPES) // [r"fatal error: static_assert failed .*\"A parameter is a refcounted type and needs scoped_refptr\.\""] - -// Refcounted types should not be bound as a raw pointer. -void WontCompile() { - HasRef for_raw_ptr; - int a; - Callback ref_count_as_raw_ptr_a = - Bind(&VoidPolymorphic1, &a); - Callback ref_count_as_raw_ptr = - Bind(&VoidPolymorphic1, &for_raw_ptr); -} - -#elif defined(NCTEST_NO_LVALUE_RAW_PTR_FOR_REFCOUNTED_TYPES) // [r"fatal error: static_assert failed .*\"A parameter is a refcounted type and needs scoped_refptr\.\""] - -// Refcounted types should not be bound as a raw pointer. -void WontCompile() { - HasRef* for_raw_ptr = nullptr; - Callback ref_count_as_raw_ptr = - Bind(&VoidPolymorphic1, for_raw_ptr); -} - -#elif defined(NCTEST_NO_RVALUE_CONST_RAW_PTR_FOR_REFCOUNTED_TYPES) // [r"fatal error: static_assert failed .*\"A parameter is a refcounted type and needs scoped_refptr\.\""] - -// Refcounted types should not be bound as a raw pointer. -void WontCompile() { - const HasRef for_raw_ptr; - Callback ref_count_as_raw_ptr = - Bind(&VoidPolymorphic1, &for_raw_ptr); -} - -#elif defined(NCTEST_NO_LVALUE_CONST_RAW_PTR_FOR_REFCOUNTED_TYPES) // [r"fatal error: static_assert failed .*\"A parameter is a refcounted type and needs scoped_refptr\.\""] - -// Refcounted types should not be bound as a raw pointer. -void WontCompile() { - const HasRef* for_raw_ptr = nullptr; - Callback ref_count_as_raw_ptr = - Bind(&VoidPolymorphic1, for_raw_ptr); -} - -#elif defined(NCTEST_WEAKPTR_BIND_MUST_RETURN_VOID) // [r"fatal error: static_assert failed .*\"weak_ptrs can only bind to methods without return values\""] - -// WeakPtrs cannot be bound to methods with return types. -void WontCompile() { - NoRef no_ref; - WeakPtrFactory weak_factory(&no_ref); - Callback weak_ptr_with_non_void_return_type = - Bind(&NoRef::IntMethod0, weak_factory.GetWeakPtr()); - weak_ptr_with_non_void_return_type.Run(); -} - -#elif defined(NCTEST_DISALLOW_ASSIGN_DIFFERENT_TYPES) // [r"fatal error: no viable conversion from 'Callback>' to 'Callback'"] - -// Bind result cannot be assigned to Callbacks with a mismatching type. -void WontCompile() { - Closure callback_mismatches_bind_type = Bind(&VoidPolymorphic1); -} - -#elif defined(NCTEST_DISALLOW_CAPTURING_LAMBDA) // [r"fatal error: implicit instantiation of undefined template 'base::internal::FunctorTraits<\(lambda at (\.\./)+base/bind_unittest.nc:[0-9]+:[0-9]+\), void>'"] - -void WontCompile() { - int i = 0, j = 0; - Bind([i,&j]() {j = i;}); -} - -#elif defined(NCTEST_DISALLOW_ONCECALLBACK_RUN_ON_LVALUE) // [r"static_assert failed .*\"OnceCallback::Run\(\) may only be invoked on a non-const rvalue, i\.e\. std::move\(callback\)\.Run\(\)\.\""] - -void WontCompile() { - OnceClosure cb = Bind([] {}); - cb.Run(); -} - -#elif defined(NCTEST_DISALLOW_ONCECALLBACK_RUN_ON_CONST_LVALUE) // [r"static_assert failed .*\"OnceCallback::Run\(\) may only be invoked on a non-const rvalue, i\.e\. std::move\(callback\)\.Run\(\)\.\""] - -void WontCompile() { - const OnceClosure cb = Bind([] {}); - cb.Run(); -} - -#elif defined(NCTEST_DISALLOW_ONCECALLBACK_RUN_ON_CONST_RVALUE) // [r"static_assert failed .*\"OnceCallback::Run\(\) may only be invoked on a non-const rvalue, i\.e\. std::move\(callback\)\.Run\(\)\.\""] - -void WontCompile() { - const OnceClosure cb = Bind([] {}); - std::move(cb).Run(); -} - -#elif defined(NCTEST_DISALLOW_BIND_ONCECALLBACK) // [r"fatal error: static_assert failed .*\"BindRepeating cannot bind OnceCallback. Use BindOnce with std::move\(\)\.\""] - -void WontCompile() { - Bind(BindOnce([](int) {}), 42); -} - -#elif defined(NCTEST_DISALLOW_BINDONCE_LVALUE_ONCECALLBACK) // [r"fatal error: static_assert failed .*\"BindOnce requires non-const rvalue for OnceCallback binding\."] -void WontCompile() { - auto cb = BindOnce([](int) {}); - BindOnce(cb, 42); -} - -#elif defined(NCTEST_DISALLOW_BINDONCE_RVALUE_CONST_ONCECALLBACK) // [r"fatal error: static_assert failed .*\"BindOnce requires non-const rvalue for OnceCallback binding\."] - -void WontCompile() { - const auto cb = BindOnce([](int) {}); - BindOnce(std::move(cb), 42); -} - -#elif defined(NCTEST_BINDONCE_MOVEONLY_TYPE_BY_VALUE) // [r"fatal error: static_assert failed .*\"Bound argument \|i\| is move-only but will be bound by copy\. Ensure \|Arg\| is mutable and bound using std::move\(\)\.\""] - -void WontCompile() { - std::unique_ptr x; - BindOnce(&TakesMoveOnly, x); -} - -#elif defined(NCTEST_BIND_MOVEONLY_TYPE_BY_VALUE) // [r"Bound argument \|i\| is move-only but will be forwarded by copy\. Ensure \|Arg\| is bound using base::Passed\(\), not std::move\(\)."] - -void WontCompile() { - std::unique_ptr x; - Bind(&TakesMoveOnly, x); -} - -#elif defined(NCTEST_BIND_MOVEONLY_TYPE_WITH_STDMOVE) // [r"Bound argument \|i\| is move-only but will be forwarded by copy\. Ensure \|Arg\| is bound using base::Passed\(\), not std::move\(\)."] - -void WontCompile() { - std::unique_ptr x; - Bind(&TakesMoveOnly, std::move(x)); -} - -#elif defined(NCTEST_BIND_NON_EMPTY_FUNCTOR) // [r"fatal error: implicit instantiation of undefined template 'base::internal::FunctorTraits'"] - -void WontCompile() { - Bind(NonEmptyFunctor()); -} - -#endif - -} // namespace base diff --git a/bit_cast.h b/bit_cast.h deleted file mode 100644 index 90dd925e8..000000000 --- a/bit_cast.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2016 The Chromium 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_BIT_CAST_H_ -#define BASE_BIT_CAST_H_ - -#include -#include - -#include "base/compiler_specific.h" -#include "base/template_util.h" -#include "build/build_config.h" - -// bit_cast is a template function that implements the equivalent -// of "*reinterpret_cast(&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(f); -// // i = 0x40490fdb -// -// The classical address-casting method is: -// -// // WRONG -// float f = 3.14159265358979; // WRONG -// int i = * reinterpret_cast(&f); // WRONG -// -// The address-casting method actually produces undefined behavior according to -// the ISO C++98 specification, section 3.10 ("basic.lval"), paragraph 15. -// (This did not substantially change in C++11.) 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(&f). And it is particularly true for conversions -// between integral lvalues and floating-point lvalues. -// -// The purpose of this paragraph is to allow optimizing compilers to assume that -// expressions with different types refer to different memory. Compilers are -// known to take 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, compilers replace -// calls to memcpy() with inline object code when the size argument is a -// compile-time constant. 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. - -template -inline Dest bit_cast(const Source& source) { - static_assert(sizeof(Dest) == sizeof(Source), - "bit_cast requires source and destination to be the same size"); - static_assert(base::is_trivially_copyable::value, - "bit_cast requires the destination type to be copyable"); - static_assert(base::is_trivially_copyable::value, - "bit_cast requires the source type to be copyable"); - - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -} - -#endif // BASE_BIT_CAST_H_ diff --git a/bit_cast_unittest.cc b/bit_cast_unittest.cc deleted file mode 100644 index f36d3fe64..000000000 --- a/bit_cast_unittest.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2016 The Chromium 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/bit_cast.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -TEST(BitCastTest, FloatIntFloat) { - float f = 3.1415926f; - int i = bit_cast(f); - float f2 = bit_cast(i); - EXPECT_EQ(f, f2); -} - -struct A { - int x; -}; - -TEST(BitCastTest, StructureInt) { - A a = { 1 }; - int b = bit_cast(a); - EXPECT_EQ(1, b); -} - -} // namespace -} // namespace base diff --git a/bits.h b/bits.h deleted file mode 100644 index a1c8b5de5..000000000 --- a/bits.h +++ /dev/null @@ -1,184 +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 defines some bit utilities. - -#ifndef BASE_BITS_H_ -#define BASE_BITS_H_ - -#include -#include - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "build/build_config.h" - -#if defined(COMPILER_MSVC) -#include -#endif - -namespace base { -namespace bits { - -// Returns true iff |value| is a power of 2. -template ::value>> -constexpr inline bool IsPowerOfTwo(T value) { - // From "Hacker's Delight": Section 2.1 Manipulating Rightmost Bits. - // - // Only positive integers with a single bit set are powers of two. If only one - // bit is set in x (e.g. 0b00000100000000) then |x-1| will have that bit set - // to zero and all bits to its right set to 1 (e.g. 0b00000011111111). Hence - // |x & (x-1)| is 0 iff x is a power of two. - return value > 0 && (value & (value - 1)) == 0; -} - -// Round up |size| to a multiple of alignment, which must be a power of two. -inline size_t Align(size_t size, size_t alignment) { - DCHECK(IsPowerOfTwo(alignment)); - return (size + alignment - 1) & ~(alignment - 1); -} - -// CountLeadingZeroBits(value) returns the number of zero bits following the -// most significant 1 bit in |value| if |value| is non-zero, otherwise it -// returns {sizeof(T) * 8}. -// Example: 00100010 -> 2 -// -// CountTrailingZeroBits(value) returns the number of zero bits preceding the -// least significant 1 bit in |value| if |value| is non-zero, otherwise it -// returns {sizeof(T) * 8}. -// Example: 00100010 -> 1 -// -// C does not have an operator to do this, but fortunately the various -// compilers have built-ins that map to fast underlying processor instructions. -#if defined(COMPILER_MSVC) - -template -ALWAYS_INLINE - typename std::enable_if::value && sizeof(T) <= 4, - unsigned>::type - CountLeadingZeroBits(T x) { - static_assert(bits > 0, "invalid instantiation"); - unsigned long index; - return LIKELY(_BitScanReverse(&index, static_cast(x))) - ? (31 - index - (32 - bits)) - : bits; -} - -template -ALWAYS_INLINE - typename std::enable_if::value && sizeof(T) == 8, - unsigned>::type - CountLeadingZeroBits(T x) { - static_assert(bits > 0, "invalid instantiation"); - unsigned long index; - return LIKELY(_BitScanReverse64(&index, static_cast(x))) - ? (63 - index) - : 64; -} - -template -ALWAYS_INLINE - typename std::enable_if::value && sizeof(T) <= 4, - unsigned>::type - CountTrailingZeroBits(T x) { - static_assert(bits > 0, "invalid instantiation"); - unsigned long index; - return LIKELY(_BitScanForward(&index, static_cast(x))) ? index - : bits; -} - -template -ALWAYS_INLINE - typename std::enable_if::value && sizeof(T) == 8, - unsigned>::type - CountTrailingZeroBits(T x) { - static_assert(bits > 0, "invalid instantiation"); - unsigned long index; - return LIKELY(_BitScanForward64(&index, static_cast(x))) ? index - : 64; -} - -ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) { - return CountLeadingZeroBits(x); -} - -#if defined(ARCH_CPU_64_BITS) - -// MSVC only supplies _BitScanForward64 when building for a 64-bit target. -ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) { - return CountLeadingZeroBits(x); -} - -#endif - -#elif defined(COMPILER_GCC) - -// __builtin_clz has undefined behaviour for an input of 0, even though there's -// clearly a return value that makes sense, and even though some processor clz -// instructions have defined behaviour for 0. We could drop to raw __asm__ to -// do better, but we'll avoid doing that unless we see proof that we need to. -template -ALWAYS_INLINE - typename std::enable_if::value && sizeof(T) <= 8, - unsigned>::type - CountLeadingZeroBits(T value) { - static_assert(bits > 0, "invalid instantiation"); - return LIKELY(value) - ? bits == 64 - ? __builtin_clzll(static_cast(value)) - : __builtin_clz(static_cast(value)) - (32 - bits) - : bits; -} - -template -ALWAYS_INLINE - typename std::enable_if::value && sizeof(T) <= 8, - unsigned>::type - CountTrailingZeroBits(T value) { - return LIKELY(value) ? bits == 64 - ? __builtin_ctzll(static_cast(value)) - : __builtin_ctz(static_cast(value)) - : bits; -} - -ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) { - return CountLeadingZeroBits(x); -} - -#if defined(ARCH_CPU_64_BITS) - -ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) { - return CountLeadingZeroBits(x); -} - -#endif - -#endif - -ALWAYS_INLINE size_t CountLeadingZeroBitsSizeT(size_t x) { - return CountLeadingZeroBits(x); -} - -ALWAYS_INLINE size_t CountTrailingZeroBitsSizeT(size_t x) { - return CountTrailingZeroBits(x); -} - -// Returns the integer i such as 2^i <= n < 2^(i+1) -inline int Log2Floor(uint32_t n) { - return 31 - CountLeadingZeroBits(n); -} - -// Returns the integer i such as 2^(i-1) < n <= 2^i -inline int Log2Ceiling(uint32_t n) { - // When n == 0, we want the function to return -1. - // When n == 0, (n - 1) will underflow to 0xFFFFFFFF, which is - // why the statement below starts with (n ? 32 : -1). - return (n ? 32 : -1) - CountLeadingZeroBits(n - 1); -} - -} // namespace bits -} // namespace base - -#endif // BASE_BITS_H_ diff --git a/bits_unittest.cc b/bits_unittest.cc deleted file mode 100644 index 98b9c08c4..000000000 --- a/bits_unittest.cc +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains the unit tests for the bit utilities. - -#include "base/bits.h" -#include "build/build_config.h" - -#include - -#include - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace bits { - -TEST(BitsTest, Log2Floor) { - EXPECT_EQ(-1, Log2Floor(0)); - EXPECT_EQ(0, Log2Floor(1)); - EXPECT_EQ(1, Log2Floor(2)); - EXPECT_EQ(1, Log2Floor(3)); - EXPECT_EQ(2, Log2Floor(4)); - for (int i = 3; i < 31; ++i) { - unsigned int value = 1U << i; - EXPECT_EQ(i, Log2Floor(value)); - EXPECT_EQ(i, Log2Floor(value + 1)); - EXPECT_EQ(i, Log2Floor(value + 2)); - EXPECT_EQ(i - 1, Log2Floor(value - 1)); - EXPECT_EQ(i - 1, Log2Floor(value - 2)); - } - EXPECT_EQ(31, Log2Floor(0xffffffffU)); -} - -TEST(BitsTest, Log2Ceiling) { - EXPECT_EQ(-1, Log2Ceiling(0)); - EXPECT_EQ(0, Log2Ceiling(1)); - EXPECT_EQ(1, Log2Ceiling(2)); - EXPECT_EQ(2, Log2Ceiling(3)); - EXPECT_EQ(2, Log2Ceiling(4)); - for (int i = 3; i < 31; ++i) { - unsigned int value = 1U << i; - EXPECT_EQ(i, Log2Ceiling(value)); - EXPECT_EQ(i + 1, Log2Ceiling(value + 1)); - EXPECT_EQ(i + 1, Log2Ceiling(value + 2)); - EXPECT_EQ(i, Log2Ceiling(value - 1)); - EXPECT_EQ(i, Log2Ceiling(value - 2)); - } - EXPECT_EQ(32, Log2Ceiling(0xffffffffU)); -} - -TEST(BitsTest, Align) { - const size_t kSizeTMax = std::numeric_limits::max(); - EXPECT_EQ(0ul, Align(0, 4)); - EXPECT_EQ(4ul, Align(1, 4)); - EXPECT_EQ(4096ul, Align(1, 4096)); - EXPECT_EQ(4096ul, Align(4096, 4096)); - EXPECT_EQ(4096ul, Align(4095, 4096)); - EXPECT_EQ(8192ul, Align(4097, 4096)); - EXPECT_EQ(kSizeTMax - 31, Align(kSizeTMax - 62, 32)); - EXPECT_EQ(kSizeTMax / 2 + 1, Align(1, kSizeTMax / 2 + 1)); -} - -TEST(BitsTest, CountLeadingZeroBits8) { - EXPECT_EQ(8u, CountLeadingZeroBits(uint8_t{0})); - EXPECT_EQ(7u, CountLeadingZeroBits(uint8_t{1})); - for (uint8_t shift = 0; shift <= 7; shift++) { - EXPECT_EQ(7u - shift, - CountLeadingZeroBits(static_cast(1 << shift))); - } - EXPECT_EQ(4u, CountLeadingZeroBits(uint8_t{0x0f})); -} - -TEST(BitsTest, CountLeadingZeroBits16) { - EXPECT_EQ(16u, CountLeadingZeroBits(uint16_t{0})); - EXPECT_EQ(15u, CountLeadingZeroBits(uint16_t{1})); - for (uint16_t shift = 0; shift <= 15; shift++) { - EXPECT_EQ(15u - shift, - CountLeadingZeroBits(static_cast(1 << shift))); - } - EXPECT_EQ(4u, CountLeadingZeroBits(uint16_t{0x0f0f})); -} - -TEST(BitsTest, CountLeadingZeroBits32) { - EXPECT_EQ(32u, CountLeadingZeroBits(uint32_t{0})); - EXPECT_EQ(31u, CountLeadingZeroBits(uint32_t{1})); - for (uint32_t shift = 0; shift <= 31; shift++) { - EXPECT_EQ(31u - shift, CountLeadingZeroBits(uint32_t{1} << shift)); - } - EXPECT_EQ(4u, CountLeadingZeroBits(uint32_t{0x0f0f0f0f})); -} - -TEST(BitsTest, CountTrailingeZeroBits8) { - EXPECT_EQ(8u, CountTrailingZeroBits(uint8_t{0})); - EXPECT_EQ(7u, CountTrailingZeroBits(uint8_t{128})); - for (uint8_t shift = 0; shift <= 7; shift++) { - EXPECT_EQ(shift, CountTrailingZeroBits(static_cast(1 << shift))); - } - EXPECT_EQ(4u, CountTrailingZeroBits(uint8_t{0xf0})); -} - -TEST(BitsTest, CountTrailingeZeroBits16) { - EXPECT_EQ(16u, CountTrailingZeroBits(uint16_t{0})); - EXPECT_EQ(15u, CountTrailingZeroBits(uint16_t{32768})); - for (uint16_t shift = 0; shift <= 15; shift++) { - EXPECT_EQ(shift, CountTrailingZeroBits(static_cast(1 << shift))); - } - EXPECT_EQ(4u, CountTrailingZeroBits(uint16_t{0xf0f0})); -} - -TEST(BitsTest, CountTrailingeZeroBits32) { - EXPECT_EQ(32u, CountTrailingZeroBits(uint32_t{0})); - EXPECT_EQ(31u, CountTrailingZeroBits(uint32_t{1} << 31)); - for (uint32_t shift = 0; shift <= 31; shift++) { - EXPECT_EQ(shift, CountTrailingZeroBits(uint32_t{1} << shift)); - } - EXPECT_EQ(4u, CountTrailingZeroBits(uint32_t{0xf0f0f0f0})); -} - -#if defined(ARCH_CPU_64_BITS) - -TEST(BitsTest, CountLeadingZeroBits64) { - EXPECT_EQ(64u, CountLeadingZeroBits(uint64_t{0})); - EXPECT_EQ(63u, CountLeadingZeroBits(uint64_t{1})); - for (uint64_t shift = 0; shift <= 63; shift++) { - EXPECT_EQ(63u - shift, CountLeadingZeroBits(uint64_t{1} << shift)); - } - EXPECT_EQ(4u, CountLeadingZeroBits(uint64_t{0x0f0f0f0f0f0f0f0f})); -} - -TEST(BitsTest, CountTrailingeZeroBits64) { - EXPECT_EQ(64u, CountTrailingZeroBits(uint64_t{0})); - EXPECT_EQ(63u, CountTrailingZeroBits(uint64_t{1} << 63)); - for (uint64_t shift = 0; shift <= 31; shift++) { - EXPECT_EQ(shift, CountTrailingZeroBits(uint64_t{1} << shift)); - } - EXPECT_EQ(4u, CountTrailingZeroBits(uint64_t{0xf0f0f0f0f0f0f0f0})); -} - -#endif // ARCH_CPU_64_BITS - -TEST(BitsTest, CountLeadingZeroBitsSizeT) { -#if defined(ARCH_CPU_64_BITS) - EXPECT_EQ(64u, CountLeadingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(63u, CountLeadingZeroBitsSizeT(size_t{1})); - EXPECT_EQ(32u, CountLeadingZeroBitsSizeT(size_t{1} << 31)); - EXPECT_EQ(1u, CountLeadingZeroBitsSizeT(size_t{1} << 62)); - EXPECT_EQ(0u, CountLeadingZeroBitsSizeT(size_t{1} << 63)); -#else - EXPECT_EQ(32u, CountLeadingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(31u, CountLeadingZeroBitsSizeT(size_t{1})); - EXPECT_EQ(1u, CountLeadingZeroBitsSizeT(size_t{1} << 30)); - EXPECT_EQ(0u, CountLeadingZeroBitsSizeT(size_t{1} << 31)); -#endif // ARCH_CPU_64_BITS -} - -TEST(BitsTest, CountTrailingZeroBitsSizeT) { -#if defined(ARCH_CPU_64_BITS) - EXPECT_EQ(64u, CountTrailingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(63u, CountTrailingZeroBitsSizeT(size_t{1} << 63)); - EXPECT_EQ(31u, CountTrailingZeroBitsSizeT(size_t{1} << 31)); - EXPECT_EQ(1u, CountTrailingZeroBitsSizeT(size_t{2})); - EXPECT_EQ(0u, CountTrailingZeroBitsSizeT(size_t{1})); -#else - EXPECT_EQ(32u, CountTrailingZeroBitsSizeT(size_t{0})); - EXPECT_EQ(31u, CountTrailingZeroBitsSizeT(size_t{1} << 31)); - EXPECT_EQ(1u, CountTrailingZeroBitsSizeT(size_t{2})); - EXPECT_EQ(0u, CountTrailingZeroBitsSizeT(size_t{1})); -#endif // ARCH_CPU_64_BITS -} - -TEST(BitsTest, PowerOfTwo) { - EXPECT_FALSE(IsPowerOfTwo(-1)); - EXPECT_FALSE(IsPowerOfTwo(0)); - EXPECT_TRUE(IsPowerOfTwo(1)); - EXPECT_TRUE(IsPowerOfTwo(2)); - // Unsigned 64 bit cases. - for (uint32_t i = 2; i < 64; i++) { - const uint64_t val = uint64_t{1} << i; - EXPECT_FALSE(IsPowerOfTwo(val - 1)); - EXPECT_TRUE(IsPowerOfTwo(val)); - EXPECT_FALSE(IsPowerOfTwo(val + 1)); - } - // Signed 64 bit cases. - for (uint32_t i = 2; i < 63; i++) { - const int64_t val = int64_t{1} << i; - EXPECT_FALSE(IsPowerOfTwo(val - 1)); - EXPECT_TRUE(IsPowerOfTwo(val)); - EXPECT_FALSE(IsPowerOfTwo(val + 1)); - } - // Signed integers with only the last bit set are negative, not powers of two. - EXPECT_FALSE(IsPowerOfTwo(int64_t{1} << 63)); -} - -} // namespace bits -} // namespace base diff --git a/build_time.cc b/build_time.cc deleted file mode 100644 index 834b04120..000000000 --- a/build_time.cc +++ /dev/null @@ -1,25 +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/build_time.h" - -// Imports the generated build date, i.e. BUILD_DATE. -#include "base/generated_build_date.h" - -#include "base/logging.h" -#include "base/time/time.h" - -namespace base { - -Time GetBuildTime() { - Time integral_build_time; - // BUILD_DATE is exactly "Mmm DD YYYY HH:MM:SS". - // See //build/write_build_date_header.py. "HH:MM:SS" is normally expected to - // be "05:00:00" but is not enforced here. - bool result = Time::FromUTCString(BUILD_DATE, &integral_build_time); - DCHECK(result); - return integral_build_time; -} - -} // namespace base diff --git a/build_time.h b/build_time.h deleted file mode 100644 index 83c9875d5..000000000 --- a/build_time.h +++ /dev/null @@ -1,30 +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_BUILD_TIME_H_ -#define BASE_BUILD_TIME_H_ - -#include "base/base_export.h" -#include "base/time/time.h" - -namespace base { - -// GetBuildTime returns the time at which the current binary was built, -// rounded down to 5:00:00am at the start of the day in UTC. -// -// This uses a generated file, which doesn't trigger a rebuild when the time -// changes. It will, however, be updated whenever //build/util/LASTCHANGE -// changes. -// -// This value should only be considered accurate to within a day. -// It will always be in the past. -// -// Note: If the build is not official (i.e. is_official_build = false) -// this time will be set to 5:00:00am on the most recent first Sunday -// of a month. -Time BASE_EXPORT GetBuildTime(); - -} // namespace base - -#endif // BASE_BUILD_TIME_H_ diff --git a/build_time_unittest.cc b/build_time_unittest.cc deleted file mode 100644 index 3a3573635..000000000 --- a/build_time_unittest.cc +++ /dev/null @@ -1,37 +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/build_time.h" -#include "base/generated_build_date.h" -#include "base/time/time.h" - -#include "testing/gtest/include/gtest/gtest.h" - -TEST(BuildTime, DateLooksValid) { - char build_date[] = BUILD_DATE; - - EXPECT_EQ(20u, strlen(build_date)); - EXPECT_EQ(' ', build_date[3]); - EXPECT_EQ(' ', build_date[6]); - EXPECT_EQ(' ', build_date[11]); - EXPECT_EQ('0', build_date[12]); - EXPECT_EQ('5', build_date[13]); - EXPECT_EQ(':', build_date[14]); - EXPECT_EQ('0', build_date[15]); - EXPECT_EQ('0', build_date[16]); - EXPECT_EQ(':', build_date[17]); - EXPECT_EQ('0', build_date[18]); - EXPECT_EQ('0', build_date[19]); -} - -TEST(BuildTime, InThePast) { - EXPECT_LT(base::GetBuildTime(), base::Time::Now()); - EXPECT_LT(base::GetBuildTime(), base::Time::NowFromSystemTime()); -} - -TEST(BuildTime, NotTooFar) { - // BuildTime must be less than 45 days old. - base::Time cutoff(base::Time::Now() - base::TimeDelta::FromDays(45)); - EXPECT_GT(base::GetBuildTime(), cutoff); -} diff --git a/callback.h b/callback.h deleted file mode 100644 index bcda5af58..000000000 --- a/callback.h +++ /dev/null @@ -1,146 +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. -// -// NOTE: Header files that do not require the full definition of Callback or -// Closure should #include "base/callback_forward.h" instead of this file. - -#ifndef BASE_CALLBACK_H_ -#define BASE_CALLBACK_H_ - -#include - -#include "base/callback_forward.h" -#include "base/callback_internal.h" - -// ----------------------------------------------------------------------------- -// Usage documentation -// ----------------------------------------------------------------------------- -// -// Overview: -// A callback is similar in concept to a function pointer: it wraps a runnable -// object such as a function, method, lambda, or even another callback, allowing -// the runnable object to be invoked later via the callback object. -// -// Unlike function pointers, callbacks are created with base::BindOnce() or -// base::BindRepeating() and support partial function application. -// -// A base::OnceCallback may be Run() at most once; a base::RepeatingCallback may -// be Run() any number of times. |is_null()| is guaranteed to return true for a -// moved-from callback. -// -// // The lambda takes two arguments, but the first argument |x| is bound at -// // callback creation. -// base::OnceCallback cb = base::BindOnce([] (int x, int y) { -// return x + y; -// }, 1); -// // Run() only needs the remaining unbound argument |y|. -// printf("1 + 2 = %d\n", std::move(cb).Run(2)); // Prints 3 -// printf("cb is null? %s\n", -// cb.is_null() ? "true" : "false"); // Prints true -// std::move(cb).Run(2); // Crashes since |cb| has already run. -// -// Callbacks also support cancellation. A common use is binding the receiver -// object as a WeakPtr. If that weak pointer is invalidated, calling Run() -// will be a no-op. Note that |is_cancelled()| and |is_null()| are distinct: -// simply cancelling a callback will not also make it null. -// -// base::Callback is currently a type alias for base::RepeatingCallback. In the -// future, we expect to flip this to default to base::OnceCallback. -// -// See //docs/callback.md for the full documentation. - -namespace base { - -template -class OnceCallback : public internal::CallbackBase { - public: - using RunType = R(Args...); - using PolymorphicInvoke = R (*)(internal::BindStateBase*, - internal::PassingType...); - - constexpr OnceCallback() = default; - OnceCallback(std::nullptr_t) = delete; - - explicit OnceCallback(internal::BindStateBase* bind_state) - : internal::CallbackBase(bind_state) {} - - OnceCallback(const OnceCallback&) = delete; - OnceCallback& operator=(const OnceCallback&) = delete; - - OnceCallback(OnceCallback&&) noexcept = default; - OnceCallback& operator=(OnceCallback&&) noexcept = default; - - OnceCallback(RepeatingCallback other) - : internal::CallbackBase(std::move(other)) {} - - OnceCallback& operator=(RepeatingCallback other) { - static_cast(*this) = std::move(other); - return *this; - } - - bool Equals(const OnceCallback& other) const { return EqualsInternal(other); } - - R Run(Args... args) const & { - static_assert(!sizeof(*this), - "OnceCallback::Run() may only be invoked on a non-const " - "rvalue, i.e. std::move(callback).Run()."); - NOTREACHED(); - } - - R Run(Args... args) && { - // Move the callback instance into a local variable before the invocation, - // that ensures the internal state is cleared after the invocation. - // It's not safe to touch |this| after the invocation, since running the - // bound function may destroy |this|. - OnceCallback cb = std::move(*this); - PolymorphicInvoke f = - reinterpret_cast(cb.polymorphic_invoke()); - return f(cb.bind_state_.get(), std::forward(args)...); - } -}; - -template -class RepeatingCallback : public internal::CallbackBaseCopyable { - public: - using RunType = R(Args...); - using PolymorphicInvoke = R (*)(internal::BindStateBase*, - internal::PassingType...); - - constexpr RepeatingCallback() = default; - RepeatingCallback(std::nullptr_t) = delete; - - explicit RepeatingCallback(internal::BindStateBase* bind_state) - : internal::CallbackBaseCopyable(bind_state) {} - - // Copyable and movable. - RepeatingCallback(const RepeatingCallback&) = default; - RepeatingCallback& operator=(const RepeatingCallback&) = default; - RepeatingCallback(RepeatingCallback&&) noexcept = default; - RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default; - - bool Equals(const RepeatingCallback& other) const { - return EqualsInternal(other); - } - - R Run(Args... args) const & { - PolymorphicInvoke f = - reinterpret_cast(this->polymorphic_invoke()); - return f(this->bind_state_.get(), std::forward(args)...); - } - - R Run(Args... args) && { - // Move the callback instance into a local variable before the invocation, - // that ensures the internal state is cleared after the invocation. - // It's not safe to touch |this| after the invocation, since running the - // bound function may destroy |this|. - RepeatingCallback cb = std::move(*this); - PolymorphicInvoke f = - reinterpret_cast(cb.polymorphic_invoke()); - return f(cb.bind_state_.get(), std::forward(args)...); - } -}; - -} // namespace base - -#endif // BASE_CALLBACK_H_ diff --git a/callback_forward.h b/callback_forward.h deleted file mode 100644 index f1851c4fb..000000000 --- a/callback_forward.h +++ /dev/null @@ -1,27 +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_CALLBACK_FORWARD_H_ -#define BASE_CALLBACK_FORWARD_H_ - -namespace base { - -template -class OnceCallback; - -template -class RepeatingCallback; - -template -using Callback = RepeatingCallback; - -// Syntactic sugar to make Callback easier to declare since it -// will be used in a lot of APIs with delayed execution. -using OnceClosure = OnceCallback; -using RepeatingClosure = RepeatingCallback; -using Closure = Callback; - -} // namespace base - -#endif // BASE_CALLBACK_FORWARD_H_ diff --git a/callback_helpers.cc b/callback_helpers.cc deleted file mode 100644 index 90867310c..000000000 --- a/callback_helpers.cc +++ /dev/null @@ -1,40 +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/callback_helpers.h" - -namespace base { - -ScopedClosureRunner::ScopedClosureRunner() = default; - -ScopedClosureRunner::ScopedClosureRunner(OnceClosure closure) - : closure_(std::move(closure)) {} - -ScopedClosureRunner::~ScopedClosureRunner() { - if (!closure_.is_null()) - std::move(closure_).Run(); -} - -ScopedClosureRunner::ScopedClosureRunner(ScopedClosureRunner&& other) - : closure_(other.Release()) {} - -ScopedClosureRunner& ScopedClosureRunner::operator=( - ScopedClosureRunner&& other) { - ReplaceClosure(other.Release()); - return *this; -} - -void ScopedClosureRunner::RunAndReset() { - std::move(closure_).Run(); -} - -void ScopedClosureRunner::ReplaceClosure(OnceClosure closure) { - closure_ = std::move(closure); -} - -OnceClosure ScopedClosureRunner::Release() { - return std::move(closure_); -} - -} // namespace base diff --git a/callback_helpers.h b/callback_helpers.h deleted file mode 100644 index 0cdda6de4..000000000 --- a/callback_helpers.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This defines helpful methods for dealing with Callbacks. Because Callbacks -// are implemented using templates, with a class per callback signature, adding -// methods to Callback<> itself is unattractive (lots of extra code gets -// generated). Instead, consider adding methods here. - -#ifndef BASE_CALLBACK_HELPERS_H_ -#define BASE_CALLBACK_HELPERS_H_ - -#include - -#include "base/atomicops.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" - -namespace base { - -// Prefer std::move() over ResetAndReturn(). -template -CallbackType ResetAndReturn(CallbackType* cb) { - CallbackType ret(std::move(*cb)); - DCHECK(!*cb); - return ret; -} - -namespace internal { - -template -class AdaptCallbackForRepeatingHelper final { - public: - explicit AdaptCallbackForRepeatingHelper(OnceCallback callback) - : callback_(std::move(callback)) { - DCHECK(callback_); - } - - void Run(Args... args) { - if (subtle::NoBarrier_AtomicExchange(&has_run_, 1)) - return; - DCHECK(callback_); - std::move(callback_).Run(std::forward(args)...); - } - - private: - volatile subtle::Atomic32 has_run_ = 0; - base::OnceCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(AdaptCallbackForRepeatingHelper); -}; - -} // namespace internal - -// Wraps the given OnceCallback into a RepeatingCallback that relays its -// invocation to the original OnceCallback on the first invocation. The -// following invocations are just ignored. -// -// Note that this deliberately subverts the Once/Repeating paradigm of Callbacks -// but helps ease the migration from old-style Callbacks. Avoid if possible; use -// if necessary for migration. TODO(tzik): Remove it. https://crbug.com/730593 -template -RepeatingCallback AdaptCallbackForRepeating( - OnceCallback callback) { - using Helper = internal::AdaptCallbackForRepeatingHelper; - return base::BindRepeating(&Helper::Run, - std::make_unique(std::move(callback))); -} - -// ScopedClosureRunner is akin to std::unique_ptr<> for Closures. It ensures -// that the Closure is executed no matter how the current scope exits. -class BASE_EXPORT ScopedClosureRunner { - public: - ScopedClosureRunner(); - explicit ScopedClosureRunner(OnceClosure closure); - ~ScopedClosureRunner(); - - ScopedClosureRunner(ScopedClosureRunner&& other); - - // Releases the current closure if it's set and replaces it with the closure - // from |other|. - ScopedClosureRunner& operator=(ScopedClosureRunner&& other); - - // Calls the current closure and resets it, so it wont be called again. - void RunAndReset(); - - // Replaces closure with the new one releasing the old one without calling it. - void ReplaceClosure(OnceClosure closure); - - // Releases the Closure without calling. - OnceClosure Release() WARN_UNUSED_RESULT; - - private: - OnceClosure closure_; - - DISALLOW_COPY_AND_ASSIGN(ScopedClosureRunner); -}; - -} // namespace base - -#endif // BASE_CALLBACK_HELPERS_H_ diff --git a/callback_helpers_unittest.cc b/callback_helpers_unittest.cc deleted file mode 100644 index 1c1102db9..000000000 --- a/callback_helpers_unittest.cc +++ /dev/null @@ -1,127 +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/callback_helpers.h" - -#include "base/bind.h" -#include "base/callback.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -void Increment(int* value) { - (*value)++; -} - -TEST(CallbackHelpersTest, TestResetAndReturn) { - int run_count = 0; - - base::Closure cb = base::Bind(&Increment, &run_count); - EXPECT_EQ(0, run_count); - base::ResetAndReturn(&cb).Run(); - EXPECT_EQ(1, run_count); - EXPECT_FALSE(cb); - - run_count = 0; - - base::OnceClosure cb2 = base::BindOnce(&Increment, &run_count); - EXPECT_EQ(0, run_count); - base::ResetAndReturn(&cb2).Run(); - EXPECT_EQ(1, run_count); - EXPECT_FALSE(cb2); -} - -TEST(CallbackHelpersTest, TestScopedClosureRunnerExitScope) { - int run_count = 0; - { - base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count)); - EXPECT_EQ(0, run_count); - } - EXPECT_EQ(1, run_count); -} - -TEST(CallbackHelpersTest, TestScopedClosureRunnerRelease) { - int run_count = 0; - base::OnceClosure c; - { - base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count)); - c = runner.Release(); - EXPECT_EQ(0, run_count); - } - EXPECT_EQ(0, run_count); - std::move(c).Run(); - EXPECT_EQ(1, run_count); -} - -TEST(CallbackHelpersTest, TestScopedClosureRunnerReplaceClosure) { - int run_count_1 = 0; - int run_count_2 = 0; - { - base::ScopedClosureRunner runner; - runner.ReplaceClosure(base::Bind(&Increment, &run_count_1)); - runner.ReplaceClosure(base::Bind(&Increment, &run_count_2)); - EXPECT_EQ(0, run_count_1); - EXPECT_EQ(0, run_count_2); - } - EXPECT_EQ(0, run_count_1); - EXPECT_EQ(1, run_count_2); -} - -TEST(CallbackHelpersTest, TestScopedClosureRunnerRunAndReset) { - int run_count_3 = 0; - { - base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count_3)); - EXPECT_EQ(0, run_count_3); - runner.RunAndReset(); - EXPECT_EQ(1, run_count_3); - } - EXPECT_EQ(1, run_count_3); -} - -TEST(CallbackHelpersTest, TestScopedClosureRunnerMoveConstructor) { - int run_count = 0; - { - std::unique_ptr runner( - new base::ScopedClosureRunner(base::Bind(&Increment, &run_count))); - base::ScopedClosureRunner runner2(std::move(*runner)); - runner.reset(); - EXPECT_EQ(0, run_count); - } - EXPECT_EQ(1, run_count); -} - -TEST(CallbackHelpersTest, TestScopedClosureRunnerMoveAssignment) { - int run_count_1 = 0; - int run_count_2 = 0; - { - base::ScopedClosureRunner runner(base::Bind(&Increment, &run_count_1)); - { - base::ScopedClosureRunner runner2(base::Bind(&Increment, &run_count_2)); - runner = std::move(runner2); - EXPECT_EQ(0, run_count_1); - EXPECT_EQ(0, run_count_2); - } - EXPECT_EQ(0, run_count_1); - EXPECT_EQ(0, run_count_2); - } - EXPECT_EQ(0, run_count_1); - EXPECT_EQ(1, run_count_2); -} - -TEST(CallbackHelpersTest, TestAdaptCallbackForRepeating) { - int count = 0; - base::OnceCallback cb = - base::BindOnce([](int* count) { ++*count; }); - - base::RepeatingCallback wrapped = - base::AdaptCallbackForRepeating(std::move(cb)); - - EXPECT_EQ(0, count); - wrapped.Run(&count); - EXPECT_EQ(1, count); - wrapped.Run(&count); - EXPECT_EQ(1, count); -} - -} // namespace diff --git a/callback_internal.cc b/callback_internal.cc deleted file mode 100644 index dd000ca8e..000000000 --- a/callback_internal.cc +++ /dev/null @@ -1,84 +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/callback_internal.h" - -#include "base/logging.h" - -namespace base { -namespace internal { - -namespace { - -bool ReturnFalse(const BindStateBase*) { - return false; -} - -} // namespace - -void BindStateBaseRefCountTraits::Destruct(const BindStateBase* bind_state) { - bind_state->destructor_(bind_state); -} - -BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke, - void (*destructor)(const BindStateBase*)) - : BindStateBase(polymorphic_invoke, destructor, &ReturnFalse) { -} - -BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke, - void (*destructor)(const BindStateBase*), - bool (*is_cancelled)(const BindStateBase*)) - : polymorphic_invoke_(polymorphic_invoke), - destructor_(destructor), - is_cancelled_(is_cancelled) {} - -CallbackBase& CallbackBase::operator=(CallbackBase&& c) noexcept = default; -CallbackBase::CallbackBase(const CallbackBaseCopyable& c) - : bind_state_(c.bind_state_) {} - -CallbackBase& CallbackBase::operator=(const CallbackBaseCopyable& c) { - bind_state_ = c.bind_state_; - return *this; -} - -CallbackBase::CallbackBase(CallbackBaseCopyable&& c) noexcept - : bind_state_(std::move(c.bind_state_)) {} - -CallbackBase& CallbackBase::operator=(CallbackBaseCopyable&& c) noexcept { - bind_state_ = std::move(c.bind_state_); - return *this; -} - -void CallbackBase::Reset() { - // NULL the bind_state_ last, since it may be holding the last ref to whatever - // object owns us, and we may be deleted after that. - bind_state_ = nullptr; -} - -bool CallbackBase::IsCancelled() const { - DCHECK(bind_state_); - return bind_state_->IsCancelled(); -} - -bool CallbackBase::EqualsInternal(const CallbackBase& other) const { - return bind_state_ == other.bind_state_; -} - -CallbackBase::~CallbackBase() = default; - -CallbackBaseCopyable::CallbackBaseCopyable(const CallbackBaseCopyable& c) { - bind_state_ = c.bind_state_; -} - -CallbackBaseCopyable& CallbackBaseCopyable::operator=( - const CallbackBaseCopyable& c) { - bind_state_ = c.bind_state_; - return *this; -} - -CallbackBaseCopyable& CallbackBaseCopyable::operator=( - CallbackBaseCopyable&& c) noexcept = default; - -} // namespace internal -} // namespace base diff --git a/callback_internal.h b/callback_internal.h deleted file mode 100644 index 1215e3e87..000000000 --- a/callback_internal.h +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains utility functions and classes that help the -// implementation, and management of the Callback objects. - -#ifndef BASE_CALLBACK_INTERNAL_H_ -#define BASE_CALLBACK_INTERNAL_H_ - -#include "base/base_export.h" -#include "base/callback_forward.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" - -namespace base { - -struct FakeBindState; - -namespace internal { - -class CallbackBase; -class CallbackBaseCopyable; - -class BindStateBase; - -template -struct BindState; - -struct BindStateBaseRefCountTraits { - static void Destruct(const BindStateBase*); -}; - -template -using PassingType = std::conditional_t::value, T, T&&>; - -// BindStateBase is used to provide an opaque handle that the Callback -// class can use to represent a function object with bound arguments. It -// behaves as an existential type that is used by a corresponding -// DoInvoke function to perform the function execution. This allows -// us to shield the Callback class from the types of the bound argument via -// "type erasure." -// At the base level, the only task is to add reference counting data. Don't use -// RefCountedThreadSafe since it requires the destructor to be a virtual method. -// Creating a vtable for every BindState template instantiation results in a lot -// of bloat. Its only task is to call the destructor which can be done with a -// function pointer. -class BASE_EXPORT BindStateBase - : public RefCountedThreadSafe { - public: - REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); - - using InvokeFuncStorage = void(*)(); - - private: - BindStateBase(InvokeFuncStorage polymorphic_invoke, - void (*destructor)(const BindStateBase*)); - BindStateBase(InvokeFuncStorage polymorphic_invoke, - void (*destructor)(const BindStateBase*), - bool (*is_cancelled)(const BindStateBase*)); - - ~BindStateBase() = default; - - friend struct BindStateBaseRefCountTraits; - friend class RefCountedThreadSafe; - - friend class CallbackBase; - friend class CallbackBaseCopyable; - - // Whitelist subclasses that access the destructor of BindStateBase. - template - friend struct BindState; - friend struct ::base::FakeBindState; - - bool IsCancelled() const { - return is_cancelled_(this); - } - - // In C++, it is safe to cast function pointers to function pointers of - // another type. It is not okay to use void*. We create a InvokeFuncStorage - // that that can store our function pointer, and then cast it back to - // the original type on usage. - InvokeFuncStorage polymorphic_invoke_; - - // Pointer to a function that will properly destroy |this|. - void (*destructor_)(const BindStateBase*); - bool (*is_cancelled_)(const BindStateBase*); - - DISALLOW_COPY_AND_ASSIGN(BindStateBase); -}; - -// Holds the Callback methods that don't require specialization to reduce -// template bloat. -// CallbackBase is a direct base class of MoveOnly callbacks, and -// CallbackBase uses CallbackBase for its implementation. -class BASE_EXPORT CallbackBase { - public: - inline CallbackBase(CallbackBase&& c) noexcept; - CallbackBase& operator=(CallbackBase&& c) noexcept; - - explicit CallbackBase(const CallbackBaseCopyable& c); - CallbackBase& operator=(const CallbackBaseCopyable& c); - - explicit CallbackBase(CallbackBaseCopyable&& c) noexcept; - CallbackBase& operator=(CallbackBaseCopyable&& c) noexcept; - - // Returns true if Callback is null (doesn't refer to anything). - bool is_null() const { return !bind_state_; } - explicit operator bool() const { return !is_null(); } - - // Returns true if the callback invocation will be nop due to an cancellation. - // It's invalid to call this on uninitialized callback. - bool IsCancelled() const; - - // Returns the Callback into an uninitialized state. - void Reset(); - - protected: - using InvokeFuncStorage = BindStateBase::InvokeFuncStorage; - - // Returns true if this callback equals |other|. |other| may be null. - bool EqualsInternal(const CallbackBase& other) const; - - constexpr inline CallbackBase(); - - // Allow initializing of |bind_state_| via the constructor to avoid default - // initialization of the scoped_refptr. - explicit inline CallbackBase(BindStateBase* bind_state); - - InvokeFuncStorage polymorphic_invoke() const { - return bind_state_->polymorphic_invoke_; - } - - // Force the destructor to be instantiated inside this translation unit so - // that our subclasses will not get inlined versions. Avoids more template - // bloat. - ~CallbackBase(); - - scoped_refptr bind_state_; -}; - -constexpr CallbackBase::CallbackBase() = default; -CallbackBase::CallbackBase(CallbackBase&&) noexcept = default; -CallbackBase::CallbackBase(BindStateBase* bind_state) - : bind_state_(AdoptRef(bind_state)) {} - -// CallbackBase is a direct base class of Copyable Callbacks. -class BASE_EXPORT CallbackBaseCopyable : public CallbackBase { - public: - CallbackBaseCopyable(const CallbackBaseCopyable& c); - CallbackBaseCopyable(CallbackBaseCopyable&& c) noexcept = default; - CallbackBaseCopyable& operator=(const CallbackBaseCopyable& c); - CallbackBaseCopyable& operator=(CallbackBaseCopyable&& c) noexcept; - - protected: - constexpr CallbackBaseCopyable() = default; - explicit CallbackBaseCopyable(BindStateBase* bind_state) - : CallbackBase(bind_state) {} - ~CallbackBaseCopyable() = default; -}; - -} // namespace internal -} // namespace base - -#endif // BASE_CALLBACK_INTERNAL_H_ diff --git a/callback_list.h b/callback_list.h deleted file mode 100644 index f455c6573..000000000 --- a/callback_list.h +++ /dev/null @@ -1,226 +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_CALLBACK_LIST_H_ -#define BASE_CALLBACK_LIST_H_ - -#include -#include - -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/macros.h" - -// OVERVIEW: -// -// A container for a list of (repeating) callbacks. Unlike a normal vector or -// list, this container can be modified during iteration without invalidating -// the iterator. It safely handles the case of a callback removing itself or -// another callback from the list while callbacks are being run. -// -// TYPICAL USAGE: -// -// class MyWidget { -// public: -// ... -// -// std::unique_ptr::Subscription> -// RegisterCallback(const base::RepeatingCallback& cb) { -// return callback_list_.Add(cb); -// } -// -// private: -// void NotifyFoo(const Foo& foo) { -// callback_list_.Notify(foo); -// } -// -// base::CallbackList callback_list_; -// -// DISALLOW_COPY_AND_ASSIGN(MyWidget); -// }; -// -// -// class MyWidgetListener { -// public: -// MyWidgetListener::MyWidgetListener() { -// foo_subscription_ = MyWidget::GetCurrent()->RegisterCallback( -// base::BindRepeating(&MyWidgetListener::OnFoo, this))); -// } -// -// MyWidgetListener::~MyWidgetListener() { -// // Subscription gets deleted automatically and will deregister -// // the callback in the process. -// } -// -// private: -// void OnFoo(const Foo& foo) { -// // Do something. -// } -// -// std::unique_ptr::Subscription> -// foo_subscription_; -// -// DISALLOW_COPY_AND_ASSIGN(MyWidgetListener); -// }; - -namespace base { - -namespace internal { - -template -class CallbackListBase { - public: - class Subscription { - public: - Subscription(CallbackListBase* list, - typename std::list::iterator iter) - : list_(list), - iter_(iter) { - } - - ~Subscription() { - if (list_->active_iterator_count_) { - iter_->Reset(); - } else { - list_->callbacks_.erase(iter_); - if (!list_->removal_callback_.is_null()) - list_->removal_callback_.Run(); - } - } - - private: - CallbackListBase* list_; - typename std::list::iterator iter_; - - DISALLOW_COPY_AND_ASSIGN(Subscription); - }; - - // Add a callback to the list. The callback will remain registered until the - // returned Subscription is destroyed, which must occur before the - // CallbackList is destroyed. - std::unique_ptr Add(const CallbackType& cb) WARN_UNUSED_RESULT { - DCHECK(!cb.is_null()); - return std::make_unique( - this, callbacks_.insert(callbacks_.end(), cb)); - } - - // Sets a callback which will be run when a subscription list is changed. - void set_removal_callback(const RepeatingClosure& callback) { - removal_callback_ = callback; - } - - // Returns true if there are no subscriptions. This is only valid to call when - // not looping through the list. - bool empty() { - DCHECK_EQ(0, active_iterator_count_); - return callbacks_.empty(); - } - - protected: - // An iterator class that can be used to access the list of callbacks. - class Iterator { - public: - explicit Iterator(CallbackListBase* list) - : list_(list), - list_iter_(list_->callbacks_.begin()) { - ++list_->active_iterator_count_; - } - - Iterator(const Iterator& iter) - : list_(iter.list_), - list_iter_(iter.list_iter_) { - ++list_->active_iterator_count_; - } - - ~Iterator() { - if (list_ && --list_->active_iterator_count_ == 0) { - list_->Compact(); - } - } - - CallbackType* GetNext() { - while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null()) - ++list_iter_; - - CallbackType* cb = nullptr; - if (list_iter_ != list_->callbacks_.end()) { - cb = &(*list_iter_); - ++list_iter_; - } - return cb; - } - - private: - CallbackListBase* list_; - typename std::list::iterator list_iter_; - }; - - CallbackListBase() : active_iterator_count_(0) {} - - ~CallbackListBase() { - DCHECK_EQ(0, active_iterator_count_); - DCHECK_EQ(0U, callbacks_.size()); - } - - // Returns an instance of a CallbackListBase::Iterator which can be used - // to run callbacks. - Iterator GetIterator() { - return Iterator(this); - } - - // Compact the list: remove any entries which were nulled out during - // iteration. - void Compact() { - auto it = callbacks_.begin(); - bool updated = false; - while (it != callbacks_.end()) { - if ((*it).is_null()) { - updated = true; - it = callbacks_.erase(it); - } else { - ++it; - } - } - - if (updated && !removal_callback_.is_null()) - removal_callback_.Run(); - } - - private: - std::list callbacks_; - int active_iterator_count_; - RepeatingClosure removal_callback_; - - DISALLOW_COPY_AND_ASSIGN(CallbackListBase); -}; - -} // namespace internal - -template class CallbackList; - -template -class CallbackList - : public internal::CallbackListBase> { - public: - using CallbackType = RepeatingCallback; - - CallbackList() = default; - - template - void Notify(RunArgs&&... args) { - auto it = this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != nullptr) { - cb->Run(args...); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -} // namespace base - -#endif // BASE_CALLBACK_LIST_H_ diff --git a/callback_list_unittest.cc b/callback_list_unittest.cc deleted file mode 100644 index 6eb5ff7f8..000000000 --- a/callback_list_unittest.cc +++ /dev/null @@ -1,339 +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/callback_list.h" - -#include -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/macros.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -class Listener { - public: - Listener() : total_(0), scaler_(1) {} - explicit Listener(int scaler) : total_(0), scaler_(scaler) {} - void IncrementTotal() { total_++; } - void IncrementByMultipleOfScaler(int x) { total_ += x * scaler_; } - - int total() const { return total_; } - - private: - int total_; - int scaler_; - DISALLOW_COPY_AND_ASSIGN(Listener); -}; - -class Remover { - public: - Remover() : total_(0) {} - void IncrementTotalAndRemove() { - total_++; - removal_subscription_.reset(); - } - void SetSubscriptionToRemove( - std::unique_ptr::Subscription> sub) { - removal_subscription_ = std::move(sub); - } - - int total() const { return total_; } - - private: - int total_; - std::unique_ptr::Subscription> removal_subscription_; - DISALLOW_COPY_AND_ASSIGN(Remover); -}; - -class Adder { - public: - explicit Adder(CallbackList* cb_reg) - : added_(false), - total_(0), - cb_reg_(cb_reg) { - } - void AddCallback() { - if (!added_) { - added_ = true; - subscription_ = - cb_reg_->Add(Bind(&Adder::IncrementTotal, Unretained(this))); - } - } - void IncrementTotal() { total_++; } - - bool added() const { return added_; } - - int total() const { return total_; } - - private: - bool added_; - int total_; - CallbackList* cb_reg_; - std::unique_ptr::Subscription> subscription_; - DISALLOW_COPY_AND_ASSIGN(Adder); -}; - -class Summer { - public: - Summer() : value_(0) {} - - void AddOneParam(int a) { value_ = a; } - void AddTwoParam(int a, int b) { value_ = a + b; } - void AddThreeParam(int a, int b, int c) { value_ = a + b + c; } - void AddFourParam(int a, int b, int c, int d) { value_ = a + b + c + d; } - void AddFiveParam(int a, int b, int c, int d, int e) { - value_ = a + b + c + d + e; - } - void AddSixParam(int a, int b, int c, int d, int e , int f) { - value_ = a + b + c + d + e + f; - } - - int value() const { return value_; } - - private: - int value_; - DISALLOW_COPY_AND_ASSIGN(Summer); -}; - -class Counter { - public: - Counter() : value_(0) {} - - void Increment() { value_++; } - - int value() const { return value_; } - - private: - int value_; - DISALLOW_COPY_AND_ASSIGN(Counter); -}; - -// Sanity check that we can instantiate a CallbackList for each arity. -TEST(CallbackListTest, ArityTest) { - Summer s; - - CallbackList c1; - std::unique_ptr::Subscription> subscription1 = - c1.Add(Bind(&Summer::AddOneParam, Unretained(&s))); - - c1.Notify(1); - EXPECT_EQ(1, s.value()); - - CallbackList c2; - std::unique_ptr::Subscription> subscription2 = - c2.Add(Bind(&Summer::AddTwoParam, Unretained(&s))); - - c2.Notify(1, 2); - EXPECT_EQ(3, s.value()); - - CallbackList c3; - std::unique_ptr::Subscription> - subscription3 = c3.Add(Bind(&Summer::AddThreeParam, Unretained(&s))); - - c3.Notify(1, 2, 3); - EXPECT_EQ(6, s.value()); - - CallbackList c4; - std::unique_ptr::Subscription> - subscription4 = c4.Add(Bind(&Summer::AddFourParam, Unretained(&s))); - - c4.Notify(1, 2, 3, 4); - EXPECT_EQ(10, s.value()); - - CallbackList c5; - std::unique_ptr::Subscription> - subscription5 = c5.Add(Bind(&Summer::AddFiveParam, Unretained(&s))); - - c5.Notify(1, 2, 3, 4, 5); - EXPECT_EQ(15, s.value()); - - CallbackList c6; - std::unique_ptr< - CallbackList::Subscription> - subscription6 = c6.Add(Bind(&Summer::AddSixParam, Unretained(&s))); - - c6.Notify(1, 2, 3, 4, 5, 6); - EXPECT_EQ(21, s.value()); -} - -// Sanity check that closures added to the list will be run, and those removed -// from the list will not be run. -TEST(CallbackListTest, BasicTest) { - CallbackList cb_reg; - Listener a, b, c; - - std::unique_ptr::Subscription> a_subscription = - cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a))); - std::unique_ptr::Subscription> b_subscription = - cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b))); - - EXPECT_TRUE(a_subscription.get()); - EXPECT_TRUE(b_subscription.get()); - - cb_reg.Notify(); - - EXPECT_EQ(1, a.total()); - EXPECT_EQ(1, b.total()); - - b_subscription.reset(); - - std::unique_ptr::Subscription> c_subscription = - cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&c))); - - cb_reg.Notify(); - - EXPECT_EQ(2, a.total()); - EXPECT_EQ(1, b.total()); - EXPECT_EQ(1, c.total()); - - a_subscription.reset(); - b_subscription.reset(); - c_subscription.reset(); -} - -// Sanity check that callbacks with details added to the list will be run, with -// the correct details, and those removed from the list will not be run. -TEST(CallbackListTest, BasicTestWithParams) { - CallbackList cb_reg; - Listener a(1), b(-1), c(1); - - std::unique_ptr::Subscription> a_subscription = - cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&a))); - std::unique_ptr::Subscription> b_subscription = - cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&b))); - - EXPECT_TRUE(a_subscription.get()); - EXPECT_TRUE(b_subscription.get()); - - cb_reg.Notify(10); - - EXPECT_EQ(10, a.total()); - EXPECT_EQ(-10, b.total()); - - b_subscription.reset(); - - std::unique_ptr::Subscription> c_subscription = - cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&c))); - - cb_reg.Notify(10); - - EXPECT_EQ(20, a.total()); - EXPECT_EQ(-10, b.total()); - EXPECT_EQ(10, c.total()); - - a_subscription.reset(); - b_subscription.reset(); - c_subscription.reset(); -} - -// Test the a callback can remove itself or a different callback from the list -// during iteration without invalidating the iterator. -TEST(CallbackListTest, RemoveCallbacksDuringIteration) { - CallbackList cb_reg; - Listener a, b; - Remover remover_1, remover_2; - - std::unique_ptr::Subscription> remover_1_sub = - cb_reg.Add( - Bind(&Remover::IncrementTotalAndRemove, Unretained(&remover_1))); - std::unique_ptr::Subscription> remover_2_sub = - cb_reg.Add( - Bind(&Remover::IncrementTotalAndRemove, Unretained(&remover_2))); - std::unique_ptr::Subscription> a_subscription = - cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a))); - std::unique_ptr::Subscription> b_subscription = - cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b))); - - // |remover_1| will remove itself. - remover_1.SetSubscriptionToRemove(std::move(remover_1_sub)); - // |remover_2| will remove a. - remover_2.SetSubscriptionToRemove(std::move(a_subscription)); - - cb_reg.Notify(); - - // |remover_1| runs once (and removes itself), |remover_2| runs once (and - // removes a), |a| never runs, and |b| runs once. - EXPECT_EQ(1, remover_1.total()); - EXPECT_EQ(1, remover_2.total()); - EXPECT_EQ(0, a.total()); - EXPECT_EQ(1, b.total()); - - cb_reg.Notify(); - - // Only |remover_2| and |b| run this time. - EXPECT_EQ(1, remover_1.total()); - EXPECT_EQ(2, remover_2.total()); - EXPECT_EQ(0, a.total()); - EXPECT_EQ(2, b.total()); -} - -// Test that a callback can add another callback to the list durning iteration -// without invalidating the iterator. The newly added callback should be run on -// the current iteration as will all other callbacks in the list. -TEST(CallbackListTest, AddCallbacksDuringIteration) { - CallbackList cb_reg; - Adder a(&cb_reg); - Listener b; - std::unique_ptr::Subscription> a_subscription = - cb_reg.Add(Bind(&Adder::AddCallback, Unretained(&a))); - std::unique_ptr::Subscription> b_subscription = - cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b))); - - cb_reg.Notify(); - - EXPECT_EQ(1, a.total()); - EXPECT_EQ(1, b.total()); - EXPECT_TRUE(a.added()); - - cb_reg.Notify(); - - EXPECT_EQ(2, a.total()); - EXPECT_EQ(2, b.total()); -} - -// Sanity check: notifying an empty list is a no-op. -TEST(CallbackListTest, EmptyList) { - CallbackList cb_reg; - - cb_reg.Notify(); -} - -TEST(CallbackList, RemovalCallback) { - Counter remove_count; - CallbackList cb_reg; - cb_reg.set_removal_callback( - Bind(&Counter::Increment, Unretained(&remove_count))); - - std::unique_ptr::Subscription> subscription = - cb_reg.Add(DoNothing()); - - // Removing a subscription outside of iteration signals the callback. - EXPECT_EQ(0, remove_count.value()); - subscription.reset(); - EXPECT_EQ(1, remove_count.value()); - - // Configure two subscriptions to remove themselves. - Remover remover_1, remover_2; - std::unique_ptr::Subscription> remover_1_sub = - cb_reg.Add( - Bind(&Remover::IncrementTotalAndRemove, Unretained(&remover_1))); - std::unique_ptr::Subscription> remover_2_sub = - cb_reg.Add( - Bind(&Remover::IncrementTotalAndRemove, Unretained(&remover_2))); - remover_1.SetSubscriptionToRemove(std::move(remover_1_sub)); - remover_2.SetSubscriptionToRemove(std::move(remover_2_sub)); - - // The callback should be signaled exactly once. - EXPECT_EQ(1, remove_count.value()); - cb_reg.Notify(); - EXPECT_EQ(2, remove_count.value()); - EXPECT_TRUE(cb_reg.empty()); -} - -} // namespace -} // namespace base diff --git a/callback_list_unittest.nc b/callback_list_unittest.nc deleted file mode 100644 index 7347f765d..000000000 --- a/callback_list_unittest.nc +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a "No Compile Test" suite. -// http://dev.chromium.org/developers/testing/no-compile-tests - -#include "base/callback_list.h" - -#include -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/macros.h" - -namespace base { - -class Foo { - public: - Foo() {} - ~Foo() {} -}; - -class FooListener { - public: - FooListener() {} - - void GotAScopedFoo(std::unique_ptr f) { foo_ = std::move(f); } - - std::unique_ptr foo_; - - private: - DISALLOW_COPY_AND_ASSIGN(FooListener); -}; - - -#if defined(NCTEST_MOVE_ONLY_TYPE_PARAMETER) // [r"fatal error: call to (implicitly-)?deleted( copy)? constructor"] - -// Callbacks run with a move-only typed parameter. -// -// CallbackList does not support move-only typed parameters. Notify() is -// designed to take zero or more parameters, and run each registered callback -// with them. With move-only types, the parameter will be set to NULL after the -// first callback has been run. -void WontCompile() { - FooListener f; - CallbackList)> c1; - std::unique_ptr)>::Subscription> sub = - c1.Add(Bind(&FooListener::GotAScopedFoo, Unretained(&f))); - c1.Notify(std::unique_ptr(new Foo())); -} - -#endif - -} // namespace base diff --git a/callback_unittest.cc b/callback_unittest.cc deleted file mode 100644 index c07d3ee20..000000000 --- a/callback_unittest.cc +++ /dev/null @@ -1,187 +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/callback.h" - -#include - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/callback_internal.h" -#include "base/memory/ref_counted.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -void NopInvokeFunc() {} - -// White-box testpoints to inject into a Callback<> object for checking -// comparators and emptiness APIs. Use a BindState that is specialized -// based on a type we declared in the anonymous namespace above to remove any -// chance of colliding with another instantiation and breaking the -// one-definition-rule. -struct FakeBindState : internal::BindStateBase { - FakeBindState() : BindStateBase(&NopInvokeFunc, &Destroy, &IsCancelled) {} - - private: - ~FakeBindState() = default; - static void Destroy(const internal::BindStateBase* self) { - delete static_cast(self); - } - static bool IsCancelled(const internal::BindStateBase*) { - return false; - } -}; - -namespace { - -class CallbackTest : public ::testing::Test { - public: - CallbackTest() - : callback_a_(new FakeBindState()), callback_b_(new FakeBindState()) {} - - ~CallbackTest() override = default; - - protected: - Callback callback_a_; - const Callback callback_b_; // Ensure APIs work with const. - Callback null_callback_; -}; - -// Ensure we can create unbound callbacks. We need this to be able to store -// them in class members that can be initialized later. -TEST_F(CallbackTest, DefaultConstruction) { - Callback c0; - Callback c1; - Callback c2; - Callback c3; - Callback c4; - Callback c5; - Callback c6; - - EXPECT_TRUE(c0.is_null()); - EXPECT_TRUE(c1.is_null()); - EXPECT_TRUE(c2.is_null()); - EXPECT_TRUE(c3.is_null()); - EXPECT_TRUE(c4.is_null()); - EXPECT_TRUE(c5.is_null()); - EXPECT_TRUE(c6.is_null()); -} - -TEST_F(CallbackTest, IsNull) { - EXPECT_TRUE(null_callback_.is_null()); - EXPECT_FALSE(callback_a_.is_null()); - EXPECT_FALSE(callback_b_.is_null()); -} - -TEST_F(CallbackTest, Equals) { - EXPECT_TRUE(callback_a_.Equals(callback_a_)); - EXPECT_FALSE(callback_a_.Equals(callback_b_)); - EXPECT_FALSE(callback_b_.Equals(callback_a_)); - - // We should compare based on instance, not type. - Callback callback_c(new FakeBindState()); - Callback callback_a2 = callback_a_; - EXPECT_TRUE(callback_a_.Equals(callback_a2)); - EXPECT_FALSE(callback_a_.Equals(callback_c)); - - // Empty, however, is always equal to empty. - Callback empty2; - EXPECT_TRUE(null_callback_.Equals(empty2)); -} - -TEST_F(CallbackTest, Reset) { - // Resetting should bring us back to empty. - ASSERT_FALSE(callback_a_.is_null()); - ASSERT_FALSE(callback_a_.Equals(null_callback_)); - - callback_a_.Reset(); - - EXPECT_TRUE(callback_a_.is_null()); - EXPECT_TRUE(callback_a_.Equals(null_callback_)); -} - -TEST_F(CallbackTest, Move) { - // Moving should reset the callback. - ASSERT_FALSE(callback_a_.is_null()); - ASSERT_FALSE(callback_a_.Equals(null_callback_)); - - auto tmp = std::move(callback_a_); - - EXPECT_TRUE(callback_a_.is_null()); - EXPECT_TRUE(callback_a_.Equals(null_callback_)); -} - -struct TestForReentrancy { - TestForReentrancy() - : cb_already_run(false), - cb(Bind(&TestForReentrancy::AssertCBIsNull, Unretained(this))) { - } - void AssertCBIsNull() { - ASSERT_TRUE(cb.is_null()); - cb_already_run = true; - } - bool cb_already_run; - Closure cb; -}; - -TEST_F(CallbackTest, ResetAndReturn) { - TestForReentrancy tfr; - ASSERT_FALSE(tfr.cb.is_null()); - ASSERT_FALSE(tfr.cb_already_run); - ResetAndReturn(&tfr.cb).Run(); - ASSERT_TRUE(tfr.cb.is_null()); - ASSERT_TRUE(tfr.cb_already_run); -} - -TEST_F(CallbackTest, NullAfterMoveRun) { - Closure cb = Bind([] {}); - ASSERT_TRUE(cb); - std::move(cb).Run(); - ASSERT_FALSE(cb); - - const Closure cb2 = Bind([] {}); - ASSERT_TRUE(cb2); - std::move(cb2).Run(); - ASSERT_TRUE(cb2); - - OnceClosure cb3 = BindOnce([] {}); - ASSERT_TRUE(cb3); - std::move(cb3).Run(); - ASSERT_FALSE(cb3); -} - -class CallbackOwner : public base::RefCounted { - public: - explicit CallbackOwner(bool* deleted) { - callback_ = Bind(&CallbackOwner::Unused, this); - deleted_ = deleted; - } - void Reset() { - callback_.Reset(); - // We are deleted here if no-one else had a ref to us. - } - - private: - friend class base::RefCounted; - virtual ~CallbackOwner() { - *deleted_ = true; - } - void Unused() { - FAIL() << "Should never be called"; - } - - Closure callback_; - bool* deleted_; -}; - -TEST_F(CallbackTest, CallbackHasLastRefOnContainingObject) { - bool deleted = false; - CallbackOwner* owner = new CallbackOwner(&deleted); - owner->Reset(); - ASSERT_TRUE(deleted); -} - -} // namespace -} // namespace base diff --git a/callback_unittest.nc b/callback_unittest.nc deleted file mode 100644 index 326152934..000000000 --- a/callback_unittest.nc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a "No Compile Test" suite. -// http://dev.chromium.org/developers/testing/no-compile-tests - -#include "base/callback.h" - -namespace base { - -class Parent { -}; - -class Child : Parent { -}; - -#if defined(NCTEST_EQUALS_REQUIRES_SAMETYPE) // [r"fatal error: no viable conversion from 'RepeatingCallback' to 'const RepeatingCallback'"] - -// Attempting to call comparison function on two callbacks of different type. -// -// This should be a compile time failure because each callback type should be -// considered distinct. -void WontCompile() { - Closure c1; - Callback c2; - c1.Equals(c2); -} - -#elif defined(NCTEST_CONSTRUCTION_FROM_SUBTYPE) // [r"fatal error: no viable conversion from 'Callback' to 'Callback'"] - -// Construction of Callback from Callback if A is supertype of B. -// -// While this is technically safe, most people aren't used to it when coding -// C++ so if this is happening, it is almost certainly an error. -void WontCompile() { - Callback cb_a; - Callback cb_b = cb_a; -} - -#elif defined(NCTEST_ASSIGNMENT_FROM_SUBTYPE) // [r"fatal error: no viable overloaded '='"] - -// Assignment of Callback from Callback if A is supertype of B. -// See explanation for NCTEST_CONSTRUCTION_FROM_SUBTYPE -void WontCompile() { - Callback cb_a; - Callback cb_b; - cb_a = cb_b; -} - -#endif - -} // namespace base diff --git a/cancelable_callback.h b/cancelable_callback.h deleted file mode 100644 index a98101a16..000000000 --- a/cancelable_callback.h +++ /dev/null @@ -1,156 +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. -// -// CancelableCallback is a wrapper around base::Callback that allows -// cancellation of a callback. CancelableCallback takes a reference on the -// wrapped callback until this object is destroyed or Reset()/Cancel() are -// called. -// -// NOTE: -// -// Calling CancelableCallback::Cancel() brings the object back to its natural, -// default-constructed state, i.e., CancelableCallback::callback() will return -// a null callback. -// -// THREAD-SAFETY: -// -// CancelableCallback objects must be created on, posted to, cancelled on, and -// destroyed on the same thread. -// -// -// EXAMPLE USAGE: -// -// In the following example, the test is verifying that RunIntensiveTest() -// Quit()s the message loop within 4 seconds. The cancelable callback is posted -// to the message loop, the intensive test runs, the message loop is run, -// then the callback is cancelled. -// -// RunLoop run_loop; -// -// void TimeoutCallback(const std::string& timeout_message) { -// FAIL() << timeout_message; -// run_loop.QuitWhenIdle(); -// } -// -// CancelableClosure timeout(base::Bind(&TimeoutCallback, "Test timed out.")); -// ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, timeout.callback(), -// TimeDelta::FromSeconds(4)); -// RunIntensiveTest(); -// run_loop.Run(); -// timeout.Cancel(); // Hopefully this is hit before the timeout callback runs. -// - -#ifndef BASE_CANCELABLE_CALLBACK_H_ -#define BASE_CANCELABLE_CALLBACK_H_ - -#include - -#include "base/base_export.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/callback_internal.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" - -namespace base { -namespace internal { - -template -class CancelableCallbackImpl { - public: - CancelableCallbackImpl() : weak_ptr_factory_(this) {} - - // |callback| must not be null. - explicit CancelableCallbackImpl(CallbackType callback) - : callback_(std::move(callback)), weak_ptr_factory_(this) { - DCHECK(callback_); - } - - ~CancelableCallbackImpl() = default; - - // Cancels and drops the reference to the wrapped callback. - void Cancel() { - weak_ptr_factory_.InvalidateWeakPtrs(); - callback_.Reset(); - } - - // Returns true if the wrapped callback has been cancelled. - bool IsCancelled() const { - return callback_.is_null(); - } - - // Sets |callback| as the closure that may be cancelled. |callback| may not - // be null. Outstanding and any previously wrapped callbacks are cancelled. - void Reset(CallbackType callback) { - DCHECK(callback); - // Outstanding tasks (e.g., posted to a message loop) must not be called. - Cancel(); - callback_ = std::move(callback); - } - - // Returns a callback that can be disabled by calling Cancel(). - CallbackType callback() const { - if (!callback_) - return CallbackType(); - CallbackType forwarder; - MakeForwarder(&forwarder); - return forwarder; - } - - private: - template - void MakeForwarder(RepeatingCallback* out) const { - using ForwarderType = void (CancelableCallbackImpl::*)(Args...); - ForwarderType forwarder = &CancelableCallbackImpl::ForwardRepeating; - *out = BindRepeating(forwarder, weak_ptr_factory_.GetWeakPtr()); - } - - template - void MakeForwarder(OnceCallback* out) const { - using ForwarderType = void (CancelableCallbackImpl::*)(Args...); - ForwarderType forwarder = &CancelableCallbackImpl::ForwardOnce; - *out = BindOnce(forwarder, weak_ptr_factory_.GetWeakPtr()); - } - - template - void ForwardRepeating(Args... args) { - callback_.Run(std::forward(args)...); - } - - template - void ForwardOnce(Args... args) { - weak_ptr_factory_.InvalidateWeakPtrs(); - std::move(callback_).Run(std::forward(args)...); - } - - // The stored closure that may be cancelled. - CallbackType callback_; - mutable base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(CancelableCallbackImpl); -}; - -} // namespace internal - -// Consider using base::WeakPtr directly instead of base::CancelableCallback for -// the task cancellation. -template -using CancelableOnceCallback = - internal::CancelableCallbackImpl>; -using CancelableOnceClosure = CancelableOnceCallback; - -template -using CancelableRepeatingCallback = - internal::CancelableCallbackImpl>; -using CancelableRepeatingClosure = CancelableOnceCallback; - -template -using CancelableCallback = CancelableRepeatingCallback; -using CancelableClosure = CancelableCallback; - -} // namespace base - -#endif // BASE_CANCELABLE_CALLBACK_H_ diff --git a/cancelable_callback_unittest.cc b/cancelable_callback_unittest.cc deleted file mode 100644 index 373498cbd..000000000 --- a/cancelable_callback_unittest.cc +++ /dev/null @@ -1,207 +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/cancelable_callback.h" - -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/location.h" -#include "base/memory/ptr_util.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -class TestRefCounted : public RefCountedThreadSafe { - private: - friend class RefCountedThreadSafe; - ~TestRefCounted() = default; - ; -}; - -void Increment(int* count) { (*count)++; } -void IncrementBy(int* count, int n) { (*count) += n; } -void RefCountedParam(const scoped_refptr& ref_counted) {} - -void OnMoveOnlyReceived(int* value, std::unique_ptr result) { - *value = *result; -} - -// Cancel(). -// - Callback can be run multiple times. -// - After Cancel(), Run() completes but has no effect. -TEST(CancelableCallbackTest, Cancel) { - int count = 0; - CancelableClosure cancelable( - base::Bind(&Increment, base::Unretained(&count))); - - base::Closure callback = cancelable.callback(); - callback.Run(); - EXPECT_EQ(1, count); - - callback.Run(); - EXPECT_EQ(2, count); - - cancelable.Cancel(); - callback.Run(); - EXPECT_EQ(2, count); -} - -// Cancel() called multiple times. -// - Cancel() cancels all copies of the wrapped callback. -// - Calling Cancel() more than once has no effect. -// - After Cancel(), callback() returns a null callback. -TEST(CancelableCallbackTest, MultipleCancel) { - int count = 0; - CancelableClosure cancelable( - base::Bind(&Increment, base::Unretained(&count))); - - base::Closure callback1 = cancelable.callback(); - base::Closure callback2 = cancelable.callback(); - cancelable.Cancel(); - - callback1.Run(); - EXPECT_EQ(0, count); - - callback2.Run(); - EXPECT_EQ(0, count); - - // Calling Cancel() again has no effect. - cancelable.Cancel(); - - // callback() of a cancelled callback is null. - base::Closure callback3 = cancelable.callback(); - EXPECT_TRUE(callback3.is_null()); -} - -// CancelableCallback destroyed before callback is run. -// - Destruction of CancelableCallback cancels outstanding callbacks. -TEST(CancelableCallbackTest, CallbackCanceledOnDestruction) { - int count = 0; - base::Closure callback; - - { - CancelableClosure cancelable( - base::Bind(&Increment, base::Unretained(&count))); - - callback = cancelable.callback(); - callback.Run(); - EXPECT_EQ(1, count); - } - - callback.Run(); - EXPECT_EQ(1, count); -} - -// Cancel() called on bound closure with a RefCounted parameter. -// - Cancel drops wrapped callback (and, implicitly, its bound arguments). -TEST(CancelableCallbackTest, CancelDropsCallback) { - scoped_refptr ref_counted = new TestRefCounted; - EXPECT_TRUE(ref_counted->HasOneRef()); - - CancelableClosure cancelable(base::Bind(RefCountedParam, ref_counted)); - EXPECT_FALSE(cancelable.IsCancelled()); - EXPECT_TRUE(ref_counted.get()); - EXPECT_FALSE(ref_counted->HasOneRef()); - - // There is only one reference to |ref_counted| after the Cancel(). - cancelable.Cancel(); - EXPECT_TRUE(cancelable.IsCancelled()); - EXPECT_TRUE(ref_counted.get()); - EXPECT_TRUE(ref_counted->HasOneRef()); -} - -// Reset(). -// - Reset() replaces the existing wrapped callback with a new callback. -// - Reset() deactivates outstanding callbacks. -TEST(CancelableCallbackTest, Reset) { - int count = 0; - CancelableClosure cancelable( - base::Bind(&Increment, base::Unretained(&count))); - - base::Closure callback = cancelable.callback(); - callback.Run(); - EXPECT_EQ(1, count); - - callback.Run(); - EXPECT_EQ(2, count); - - cancelable.Reset( - base::Bind(&IncrementBy, base::Unretained(&count), 3)); - EXPECT_FALSE(cancelable.IsCancelled()); - - // The stale copy of the cancelable callback is non-null. - ASSERT_FALSE(callback.is_null()); - - // The stale copy of the cancelable callback is no longer active. - callback.Run(); - EXPECT_EQ(2, count); - - base::Closure callback2 = cancelable.callback(); - ASSERT_FALSE(callback2.is_null()); - - callback2.Run(); - EXPECT_EQ(5, count); -} - -// IsCanceled(). -// - Cancel() transforms the CancelableCallback into a cancelled state. -TEST(CancelableCallbackTest, IsNull) { - CancelableClosure cancelable; - EXPECT_TRUE(cancelable.IsCancelled()); - - int count = 0; - cancelable.Reset(base::Bind(&Increment, - base::Unretained(&count))); - EXPECT_FALSE(cancelable.IsCancelled()); - - cancelable.Cancel(); - EXPECT_TRUE(cancelable.IsCancelled()); -} - -// CancelableCallback posted to a MessageLoop with PostTask. -// - Callbacks posted to a MessageLoop can be cancelled. -TEST(CancelableCallbackTest, PostTask) { - MessageLoop loop; - - int count = 0; - CancelableClosure cancelable(base::Bind(&Increment, - base::Unretained(&count))); - - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, cancelable.callback()); - RunLoop().RunUntilIdle(); - - EXPECT_EQ(1, count); - - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, cancelable.callback()); - - // Cancel before running the message loop. - cancelable.Cancel(); - RunLoop().RunUntilIdle(); - - // Callback never ran due to cancellation; count is the same. - EXPECT_EQ(1, count); -} - -// CancelableCallback can be used with move-only types. -TEST(CancelableCallbackTest, MoveOnlyType) { - const int kExpectedResult = 42; - - int result = 0; - CancelableCallback)> cb( - base::Bind(&OnMoveOnlyReceived, base::Unretained(&result))); - cb.callback().Run(base::WrapUnique(new int(kExpectedResult))); - - EXPECT_EQ(kExpectedResult, result); -} - -} // namespace -} // namespace base diff --git a/check_example.cc b/check_example.cc deleted file mode 100644 index 7b9d8e6a8..000000000 --- a/check_example.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file is meant for analyzing the code generated by the CHECK -// macros in a small executable file that's easy to disassemble. - -#include "base/compiler_specific.h" -#include "base/logging.h" - -// An official build shouldn't generate code to print out messages for -// the CHECK* macros, nor should it have the strings in the -// executable. It is also important that the CHECK() function collapse to the -// same implementation as RELEASE_ASSERT(), in particular on Windows x86. -// Historically, the stream eating caused additional unnecessary instructions. -// See https://crbug.com/672699. - -#define BLINK_RELEASE_ASSERT_EQUIVALENT(assertion) \ - (UNLIKELY(!(assertion)) ? (IMMEDIATE_CRASH()) : (void)0) - -void DoCheck(bool b) { - CHECK(b) << "DoCheck " << b; -} - -void DoBlinkReleaseAssert(bool b) { - BLINK_RELEASE_ASSERT_EQUIVALENT(b); -} - -void DoCheckEq(int x, int y) { - CHECK_EQ(x, y); -} - -int main(int argc, const char* argv[]) { - DoCheck(argc > 1); - DoCheckEq(argc, 1); - DoBlinkReleaseAssert(argc > 1); -} diff --git a/command_line.cc b/command_line.cc deleted file mode 100644 index aec89f563..000000000 --- a/command_line.cc +++ /dev/null @@ -1,487 +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/command_line.h" - -#include -#include - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/stl_util.h" -#include "base/strings/string_split.h" -#include "base/strings/string_tokenizer.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#include -#endif - -namespace base { - -CommandLine* CommandLine::current_process_commandline_ = nullptr; - -namespace { - -const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); -const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); - -// Since we use a lazy match, make sure that longer versions (like "--") are -// listed before shorter versions (like "-") of similar prefixes. -#if defined(OS_WIN) -// By putting slash last, we can control whether it is treaded as a switch -// value by changing the value of switch_prefix_count to be one less than -// the array size. -const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -// Unixes don't use slash as a switch. -const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"}; -#endif -size_t switch_prefix_count = arraysize(kSwitchPrefixes); - -size_t GetSwitchPrefixLength(const CommandLine::StringType& string) { - for (size_t i = 0; i < switch_prefix_count; ++i) { - CommandLine::StringType prefix(kSwitchPrefixes[i]); - if (string.compare(0, prefix.length(), prefix) == 0) - return prefix.length(); - } - return 0; -} - -// Fills in |switch_string| and |switch_value| if |string| is a switch. -// This will preserve the input switch prefix in the output |switch_string|. -bool IsSwitch(const CommandLine::StringType& string, - CommandLine::StringType* switch_string, - CommandLine::StringType* switch_value) { - switch_string->clear(); - switch_value->clear(); - size_t prefix_length = GetSwitchPrefixLength(string); - if (prefix_length == 0 || prefix_length == string.length()) - return false; - - const size_t equals_position = string.find(kSwitchValueSeparator); - *switch_string = string.substr(0, equals_position); - if (equals_position != CommandLine::StringType::npos) - *switch_value = string.substr(equals_position + 1); - return true; -} - -// Append switches and arguments, keeping switches before arguments. -void AppendSwitchesAndArguments(CommandLine* command_line, - const CommandLine::StringVector& argv) { - bool parse_switches = true; - for (size_t i = 1; i < argv.size(); ++i) { - CommandLine::StringType arg = argv[i]; -#if defined(OS_WIN) - TrimWhitespace(arg, TRIM_ALL, &arg); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - TrimWhitespaceASCII(arg, TRIM_ALL, &arg); -#endif - - CommandLine::StringType switch_string; - CommandLine::StringType switch_value; - parse_switches &= (arg != kSwitchTerminator); - if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { -#if defined(OS_WIN) - command_line->AppendSwitchNative(UTF16ToASCII(switch_string), - switch_value); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - command_line->AppendSwitchNative(switch_string, switch_value); -#else -#error Unsupported platform -#endif - } else { - command_line->AppendArgNative(arg); - } - } -} - -#if defined(OS_WIN) -// Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*. -string16 QuoteForCommandLineToArgvW(const string16& arg, - bool quote_placeholders) { - // We follow the quoting rules of CommandLineToArgvW. - // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx - string16 quotable_chars(L" \\\""); - // We may also be required to quote '%', which is commonly used in a command - // line as a placeholder. (It may be substituted for a string with spaces.) - if (quote_placeholders) - quotable_chars.push_back(L'%'); - if (arg.find_first_of(quotable_chars) == string16::npos) { - // No quoting necessary. - return arg; - } - - string16 out; - out.push_back(L'"'); - for (size_t i = 0; i < arg.size(); ++i) { - if (arg[i] == '\\') { - // Find the extent of this run of backslashes. - size_t start = i, end = start + 1; - for (; end < arg.size() && arg[end] == '\\'; ++end) {} - size_t backslash_count = end - start; - - // Backslashes are escapes only if the run is followed by a double quote. - // Since we also will end the string with a double quote, we escape for - // either a double quote or the end of the string. - if (end == arg.size() || arg[end] == '"') { - // To quote, we need to output 2x as many backslashes. - backslash_count *= 2; - } - for (size_t j = 0; j < backslash_count; ++j) - out.push_back('\\'); - - // Advance i to one before the end to balance i++ in loop. - i = end - 1; - } else if (arg[i] == '"') { - out.push_back('\\'); - out.push_back('"'); - } else { - out.push_back(arg[i]); - } - } - out.push_back('"'); - - return out; -} -#endif - -} // namespace - -CommandLine::CommandLine(NoProgram no_program) - : argv_(1), - begin_args_(1) { -} - -CommandLine::CommandLine(const FilePath& program) - : argv_(1), - begin_args_(1) { - SetProgram(program); -} - -CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv) - : argv_(1), - begin_args_(1) { - InitFromArgv(argc, argv); -} - -CommandLine::CommandLine(const StringVector& argv) - : argv_(1), - begin_args_(1) { - InitFromArgv(argv); -} - -CommandLine::CommandLine(const CommandLine& other) = default; - -CommandLine& CommandLine::operator=(const CommandLine& other) = default; - -CommandLine::~CommandLine() = default; - -#if defined(OS_WIN) -// static -void CommandLine::set_slash_is_not_a_switch() { - // The last switch prefix should be slash, so adjust the size to skip it. - DCHECK_EQ(wcscmp(kSwitchPrefixes[arraysize(kSwitchPrefixes) - 1], L"/"), 0); - switch_prefix_count = arraysize(kSwitchPrefixes) - 1; -} - -// static -void CommandLine::InitUsingArgvForTesting(int argc, const char* const* argv) { - DCHECK(!current_process_commandline_); - current_process_commandline_ = new CommandLine(NO_PROGRAM); - // On Windows we need to convert the command line arguments to string16. - base::CommandLine::StringVector argv_vector; - for (int i = 0; i < argc; ++i) - argv_vector.push_back(UTF8ToUTF16(argv[i])); - current_process_commandline_->InitFromArgv(argv_vector); -} -#endif - -// static -bool CommandLine::Init(int argc, const char* const* argv) { - if (current_process_commandline_) { - // If this is intentional, Reset() must be called first. If we are using - // the shared build mode, we have to share a single object across multiple - // shared libraries. - return false; - } - - current_process_commandline_ = new CommandLine(NO_PROGRAM); -#if defined(OS_WIN) - current_process_commandline_->ParseFromString(::GetCommandLineW()); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - current_process_commandline_->InitFromArgv(argc, argv); -#else -#error Unsupported platform -#endif - - return true; -} - -// static -void CommandLine::Reset() { - DCHECK(current_process_commandline_); - delete current_process_commandline_; - current_process_commandline_ = nullptr; -} - -// static -CommandLine* CommandLine::ForCurrentProcess() { - DCHECK(current_process_commandline_); - return current_process_commandline_; -} - -// static -bool CommandLine::InitializedForCurrentProcess() { - return !!current_process_commandline_; -} - -#if defined(OS_WIN) -// static -CommandLine CommandLine::FromString(const string16& command_line) { - CommandLine cmd(NO_PROGRAM); - cmd.ParseFromString(command_line); - return cmd; -} -#endif - -void CommandLine::InitFromArgv(int argc, - const CommandLine::CharType* const* argv) { - StringVector new_argv; - for (int i = 0; i < argc; ++i) - new_argv.push_back(argv[i]); - InitFromArgv(new_argv); -} - -void CommandLine::InitFromArgv(const StringVector& argv) { - argv_ = StringVector(1); - switches_.clear(); - begin_args_ = 1; - SetProgram(argv.empty() ? FilePath() : FilePath(argv[0])); - AppendSwitchesAndArguments(this, argv); -} - -FilePath CommandLine::GetProgram() const { - return FilePath(argv_[0]); -} - -void CommandLine::SetProgram(const FilePath& program) { -#if defined(OS_WIN) - TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - TrimWhitespaceASCII(program.value(), TRIM_ALL, &argv_[0]); -#else -#error Unsupported platform -#endif -} - -bool CommandLine::HasSwitch(const base::StringPiece& switch_string) const { - DCHECK_EQ(ToLowerASCII(switch_string), switch_string); - return ContainsKey(switches_, switch_string); -} - -bool CommandLine::HasSwitch(const char switch_constant[]) const { - return HasSwitch(base::StringPiece(switch_constant)); -} - -std::string CommandLine::GetSwitchValueASCII( - const base::StringPiece& switch_string) const { - StringType value = GetSwitchValueNative(switch_string); - if (!IsStringASCII(value)) { - DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII."; - return std::string(); - } -#if defined(OS_WIN) - return UTF16ToASCII(value); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - return value; -#endif -} - -FilePath CommandLine::GetSwitchValuePath( - const base::StringPiece& switch_string) const { - return FilePath(GetSwitchValueNative(switch_string)); -} - -CommandLine::StringType CommandLine::GetSwitchValueNative( - const base::StringPiece& switch_string) const { - DCHECK_EQ(ToLowerASCII(switch_string), switch_string); - auto result = switches_.find(switch_string); - return result == switches_.end() ? StringType() : result->second; -} - -void CommandLine::AppendSwitch(const std::string& switch_string) { - AppendSwitchNative(switch_string, StringType()); -} - -void CommandLine::AppendSwitchPath(const std::string& switch_string, - const FilePath& path) { - AppendSwitchNative(switch_string, path.value()); -} - -void CommandLine::AppendSwitchNative(const std::string& switch_string, - const CommandLine::StringType& value) { -#if defined(OS_WIN) - const std::string switch_key = ToLowerASCII(switch_string); - StringType combined_switch_string(ASCIIToUTF16(switch_key)); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - const std::string& switch_key = switch_string; - StringType combined_switch_string(switch_key); -#endif - size_t prefix_length = GetSwitchPrefixLength(combined_switch_string); - auto insertion = - switches_.insert(make_pair(switch_key.substr(prefix_length), value)); - if (!insertion.second) - insertion.first->second = value; - // Preserve existing switch prefixes in |argv_|; only append one if necessary. - if (prefix_length == 0) - combined_switch_string = kSwitchPrefixes[0] + combined_switch_string; - if (!value.empty()) - combined_switch_string += kSwitchValueSeparator + value; - // Append the switch and update the switches/arguments divider |begin_args_|. - argv_.insert(argv_.begin() + begin_args_++, combined_switch_string); -} - -void CommandLine::AppendSwitchASCII(const std::string& switch_string, - const std::string& value_string) { -#if defined(OS_WIN) - AppendSwitchNative(switch_string, ASCIIToUTF16(value_string)); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - AppendSwitchNative(switch_string, value_string); -#else -#error Unsupported platform -#endif -} - -void CommandLine::CopySwitchesFrom(const CommandLine& source, - const char* const switches[], - size_t count) { - for (size_t i = 0; i < count; ++i) { - if (source.HasSwitch(switches[i])) - AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i])); - } -} - -CommandLine::StringVector CommandLine::GetArgs() const { - // Gather all arguments after the last switch (may include kSwitchTerminator). - StringVector args(argv_.begin() + begin_args_, argv_.end()); - // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?) - StringVector::iterator switch_terminator = - std::find(args.begin(), args.end(), kSwitchTerminator); - if (switch_terminator != args.end()) - args.erase(switch_terminator); - return args; -} - -void CommandLine::AppendArg(const std::string& value) { -#if defined(OS_WIN) - DCHECK(IsStringUTF8(value)); - AppendArgNative(UTF8ToWide(value)); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - AppendArgNative(value); -#else -#error Unsupported platform -#endif -} - -void CommandLine::AppendArgPath(const FilePath& path) { - AppendArgNative(path.value()); -} - -void CommandLine::AppendArgNative(const CommandLine::StringType& value) { - argv_.push_back(value); -} - -void CommandLine::AppendArguments(const CommandLine& other, - bool include_program) { - if (include_program) - SetProgram(other.GetProgram()); - AppendSwitchesAndArguments(this, other.argv()); -} - -void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { - if (wrapper.empty()) - return; - // Split the wrapper command based on whitespace (with quoting). - using CommandLineTokenizer = - StringTokenizerT; - CommandLineTokenizer tokenizer(wrapper, FILE_PATH_LITERAL(" ")); - tokenizer.set_quote_chars(FILE_PATH_LITERAL("'\"")); - std::vector wrapper_argv; - while (tokenizer.GetNext()) - wrapper_argv.emplace_back(tokenizer.token()); - - // 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(); -} - -#if defined(OS_WIN) -void CommandLine::ParseFromString(const string16& command_line) { - string16 command_line_string; - TrimWhitespace(command_line, TRIM_ALL, &command_line_string); - if (command_line_string.empty()) - return; - - int num_args = 0; - wchar_t** args = NULL; - args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args); - - DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: " - << UTF16ToUTF8(command_line); - InitFromArgv(num_args, args); - LocalFree(args); -} -#endif - -CommandLine::StringType CommandLine::GetCommandLineStringInternal( - bool quote_placeholders) const { - StringType string(argv_[0]); -#if defined(OS_WIN) - string = QuoteForCommandLineToArgvW(string, quote_placeholders); -#endif - StringType params(GetArgumentsStringInternal(quote_placeholders)); - if (!params.empty()) { - string.append(StringType(FILE_PATH_LITERAL(" "))); - string.append(params); - } - return string; -} - -CommandLine::StringType CommandLine::GetArgumentsStringInternal( - bool quote_placeholders) const { - StringType params; - // Append switches and arguments. - bool parse_switches = true; - for (size_t i = 1; i < argv_.size(); ++i) { - StringType arg = argv_[i]; - StringType switch_string; - StringType switch_value; - parse_switches &= arg != kSwitchTerminator; - if (i > 1) - params.append(StringType(FILE_PATH_LITERAL(" "))); - if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { - params.append(switch_string); - if (!switch_value.empty()) { -#if defined(OS_WIN) - switch_value = - QuoteForCommandLineToArgvW(switch_value, quote_placeholders); -#endif - params.append(kSwitchValueSeparator + switch_value); - } - } else { -#if defined(OS_WIN) - arg = QuoteForCommandLineToArgvW(arg, quote_placeholders); -#endif - params.append(arg); - } - } - return params; -} - -} // namespace base diff --git a/command_line.h b/command_line.h deleted file mode 100644 index 25fd7d919..000000000 --- a/command_line.h +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This class works with command lines: building and parsing. -// Arguments with prefixes ('--', '-', and on Windows, '/') are switches. -// Switches will precede all other arguments without switch prefixes. -// Switches can optionally have values, delimited by '=', e.g., "-switch=value". -// An argument of "--" will terminate switch parsing during initialization, -// interpreting subsequent tokens as non-switch arguments, regardless of prefix. - -// There is a singleton read-only CommandLine that represents the command line -// that the current process was started with. It must be initialized in main(). - -#ifndef BASE_COMMAND_LINE_H_ -#define BASE_COMMAND_LINE_H_ - -#include -#include -#include -#include - -#include "base/base_export.h" -#include "base/strings/string16.h" -#include "base/strings/string_piece.h" -#include "build/build_config.h" - -namespace base { - -class FilePath; - -class BASE_EXPORT CommandLine { - public: -#if defined(OS_WIN) - // The native command line string type. - using StringType = string16; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - using StringType = std::string; -#endif - - using CharType = StringType::value_type; - using StringVector = std::vector; - using SwitchMap = std::map>; - - // A constructor for CommandLines that only carry switches and arguments. - enum NoProgram { NO_PROGRAM }; - explicit CommandLine(NoProgram no_program); - - // Construct a new command line with |program| as argv[0]. - explicit CommandLine(const FilePath& program); - - // Construct a new command line from an argument list. - CommandLine(int argc, const CharType* const* argv); - explicit CommandLine(const StringVector& argv); - - // Override copy and assign to ensure |switches_by_stringpiece_| is valid. - CommandLine(const CommandLine& other); - CommandLine& operator=(const CommandLine& other); - - ~CommandLine(); - -#if defined(OS_WIN) - // By default this class will treat command-line arguments beginning with - // slashes as switches on Windows, but not other platforms. - // - // If this behavior is inappropriate for your application, you can call this - // function BEFORE initializing the current process' global command line - // object and the behavior will be the same as Posix systems (only hyphens - // begin switches, everything else will be an arg). - static void set_slash_is_not_a_switch(); - - // Normally when the CommandLine singleton is initialized it gets the command - // line via the GetCommandLineW API and then uses the shell32 API - // CommandLineToArgvW to parse the command line and convert it back to - // argc and argv. Tests who don't want this dependency on shell32 and need - // to honor the arguments passed in should use this function. - static void InitUsingArgvForTesting(int argc, const char* const* argv); -#endif - - // Initialize the current process CommandLine singleton. On Windows, ignores - // its arguments (we instead parse GetCommandLineW() directly) because we - // don't trust the CRT's parsing of the command line, but it still must be - // called to set up the command line. Returns false if initialization has - // already occurred, and true otherwise. Only the caller receiving a 'true' - // return value should take responsibility for calling Reset. - static bool Init(int argc, const char* const* argv); - - // Destroys the current process CommandLine singleton. This is necessary if - // you want to reset the base library to its initial state (for example, in an - // outer library that needs to be able to terminate, and be re-initialized). - // If Init is called only once, as in main(), Reset() is not necessary. - // Do not call this in tests. Use base::test::ScopedCommandLine instead. - static void Reset(); - - // Get the singleton CommandLine representing the current process's - // command line. Note: returned value is mutable, but not thread safe; - // only mutate if you know what you're doing! - static CommandLine* ForCurrentProcess(); - - // Returns true if the CommandLine has been initialized for the given process. - static bool InitializedForCurrentProcess(); - -#if defined(OS_WIN) - static CommandLine FromString(const string16& command_line); -#endif - - // Initialize from an argv vector. - void InitFromArgv(int argc, const CharType* const* argv); - void InitFromArgv(const StringVector& argv); - - // Constructs and returns the represented command line string. - // CAUTION! This should be avoided on POSIX because quoting behavior is - // unclear. - StringType GetCommandLineString() const { - return GetCommandLineStringInternal(false); - } - -#if defined(OS_WIN) - // Constructs and returns the represented command line string. Assumes the - // command line contains placeholders (eg, %1) and quotes any program or - // argument with a '%' in it. This should be avoided unless the placeholder is - // required by an external interface (eg, the Windows registry), because it is - // not generally safe to replace it with an arbitrary string. If possible, - // placeholders should be replaced *before* converting the command line to a - // string. - StringType GetCommandLineStringWithPlaceholders() const { - return GetCommandLineStringInternal(true); - } -#endif - - // Constructs and returns the represented arguments string. - // CAUTION! This should be avoided on POSIX because quoting behavior is - // unclear. - StringType GetArgumentsString() const { - return GetArgumentsStringInternal(false); - } - -#if defined(OS_WIN) - // Constructs and returns the represented arguments string. Assumes the - // command line contains placeholders (eg, %1) and quotes any argument with a - // '%' in it. This should be avoided unless the placeholder is required by an - // external interface (eg, the Windows registry), because it is not generally - // safe to replace it with an arbitrary string. If possible, placeholders - // should be replaced *before* converting the arguments to a string. - StringType GetArgumentsStringWithPlaceholders() const { - return GetArgumentsStringInternal(true); - } -#endif - - // Returns the original command line string as a vector of strings. - const StringVector& argv() const { return argv_; } - - // Get and Set the program part of the command line string (the first item). - FilePath GetProgram() const; - void SetProgram(const FilePath& program); - - // Returns true if this command line contains the given switch. - // Switch names must be lowercase. - // The second override provides an optimized version to avoid inlining codegen - // at every callsite to find the length of the constant and construct a - // StringPiece. - bool HasSwitch(const StringPiece& switch_string) const; - bool HasSwitch(const char switch_constant[]) const; - - // Returns the value associated with the given switch. If the switch has no - // value or isn't present, this method returns the empty string. - // Switch names must be lowercase. - std::string GetSwitchValueASCII(const StringPiece& switch_string) const; - FilePath GetSwitchValuePath(const StringPiece& switch_string) const; - StringType GetSwitchValueNative(const StringPiece& switch_string) const; - - // Get a copy of all switches, along with their values. - const SwitchMap& GetSwitches() const { return switches_; } - - // Append a switch [with optional value] to the command line. - // Note: Switches will precede arguments regardless of appending order. - void AppendSwitch(const std::string& switch_string); - void AppendSwitchPath(const std::string& switch_string, - const FilePath& path); - void AppendSwitchNative(const std::string& switch_string, - const StringType& value); - void AppendSwitchASCII(const std::string& switch_string, - const std::string& value); - - // Copy a set of switches (and any values) from another command line. - // Commonly used when launching a subprocess. - void CopySwitchesFrom(const CommandLine& source, - const char* const switches[], - size_t count); - - // Get the remaining arguments to the command. - StringVector GetArgs() const; - - // Append an argument to the command line. Note that the argument is quoted - // properly such that it is interpreted as one argument to the target command. - // 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 FilePath& value); - void AppendArgNative(const StringType& value); - - // Append the switches and arguments from another command line to this one. - // If |include_program| is true, include |other|'s program as well. - void AppendArguments(const CommandLine& other, bool include_program); - - // Insert a command before the current command. - // Common for debuggers, like "gdb --args". - void PrependWrapper(const StringType& wrapper); - -#if defined(OS_WIN) - // Initialize by parsing the given command line string. - // The program name is assumed to be the first item in the string. - void ParseFromString(const string16& command_line); -#endif - - private: - // Disallow default constructor; a program name must be explicitly specified. - CommandLine() = delete; - // Allow the copy constructor. A common pattern is to copy of the current - // process's command line and then add some flags to it. For example: - // CommandLine cl(*CommandLine::ForCurrentProcess()); - // cl.AppendSwitch(...); - - // Internal version of GetCommandLineString. If |quote_placeholders| is true, - // also quotes parts with '%' in them. - StringType GetCommandLineStringInternal(bool quote_placeholders) const; - - // Internal version of GetArgumentsString. If |quote_placeholders| is true, - // also quotes parts with '%' in them. - StringType GetArgumentsStringInternal(bool quote_placeholders) const; - - // The singleton CommandLine representing the current process's command line. - static CommandLine* current_process_commandline_; - - // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* } - StringVector argv_; - - // Parsed-out switch keys and values. - SwitchMap switches_; - - // The index after the program and switches, any arguments start here. - size_t begin_args_; -}; - -} // namespace base - -#endif // BASE_COMMAND_LINE_H_ diff --git a/command_line_unittest.cc b/command_line_unittest.cc deleted file mode 100644 index 3718cd999..000000000 --- a/command_line_unittest.cc +++ /dev/null @@ -1,440 +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/command_line.h" - -#include -#include -#include - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -// To test Windows quoting behavior, we use a string that has some backslashes -// and quotes. -// Consider the command-line argument: q\"bs1\bs2\\bs3q\\\" -// Here it is with C-style escapes. -static const CommandLine::StringType kTrickyQuoted = - FILE_PATH_LITERAL("q\\\"bs1\\bs2\\\\bs3q\\\\\\\""); -// It should be parsed by Windows as: q"bs1\bs2\\bs3q\" -// Here that is with C-style escapes. -static const CommandLine::StringType kTricky = - FILE_PATH_LITERAL("q\"bs1\\bs2\\\\bs3q\\\""); - -TEST(CommandLineTest, CommandLineConstructor) { - const CommandLine::CharType* argv[] = { - FILE_PATH_LITERAL("program"), - FILE_PATH_LITERAL("--foo="), - FILE_PATH_LITERAL("-bAr"), - FILE_PATH_LITERAL("-spaetzel=pierogi"), - FILE_PATH_LITERAL("-baz"), - FILE_PATH_LITERAL("flim"), - FILE_PATH_LITERAL("--other-switches=--dog=canine --cat=feline"), - FILE_PATH_LITERAL("-spaetzle=Crepe"), - FILE_PATH_LITERAL("-=loosevalue"), - FILE_PATH_LITERAL("-"), - FILE_PATH_LITERAL("FLAN"), - FILE_PATH_LITERAL("a"), - FILE_PATH_LITERAL("--input-translation=45--output-rotation"), - FILE_PATH_LITERAL("--"), - FILE_PATH_LITERAL("--"), - FILE_PATH_LITERAL("--not-a-switch"), - FILE_PATH_LITERAL("\"in the time of submarines...\""), - FILE_PATH_LITERAL("unquoted arg-with-space")}; - CommandLine cl(arraysize(argv), argv); - - EXPECT_FALSE(cl.GetCommandLineString().empty()); - EXPECT_FALSE(cl.HasSwitch("cruller")); - EXPECT_FALSE(cl.HasSwitch("flim")); - EXPECT_FALSE(cl.HasSwitch("program")); - EXPECT_FALSE(cl.HasSwitch("dog")); - EXPECT_FALSE(cl.HasSwitch("cat")); - EXPECT_FALSE(cl.HasSwitch("output-rotation")); - EXPECT_FALSE(cl.HasSwitch("not-a-switch")); - EXPECT_FALSE(cl.HasSwitch("--")); - - EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(), - cl.GetProgram().value()); - - EXPECT_TRUE(cl.HasSwitch("foo")); -#if defined(OS_WIN) - EXPECT_TRUE(cl.HasSwitch("bar")); -#else - EXPECT_FALSE(cl.HasSwitch("bar")); -#endif - EXPECT_TRUE(cl.HasSwitch("baz")); - EXPECT_TRUE(cl.HasSwitch("spaetzle")); - EXPECT_TRUE(cl.HasSwitch("other-switches")); - EXPECT_TRUE(cl.HasSwitch("input-translation")); - - EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle")); - EXPECT_EQ("", cl.GetSwitchValueASCII("foo")); - EXPECT_EQ("", cl.GetSwitchValueASCII("bar")); - EXPECT_EQ("", cl.GetSwitchValueASCII("cruller")); - EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII( - "other-switches")); - EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation")); - - const CommandLine::StringVector& args = cl.GetArgs(); - ASSERT_EQ(8U, args.size()); - - std::vector::const_iterator iter = args.begin(); - EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("-"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("a"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("\"in the time of submarines...\""), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("unquoted arg-with-space"), *iter); - ++iter; - EXPECT_TRUE(iter == args.end()); -} - -TEST(CommandLineTest, CommandLineFromString) { -#if defined(OS_WIN) - CommandLine cl = CommandLine::FromString( - L"program --foo= -bAr /Spaetzel=pierogi /Baz flim " - L"--other-switches=\"--dog=canine --cat=feline\" " - L"-spaetzle=Crepe -=loosevalue FLAN " - L"--input-translation=\"45\"--output-rotation " - L"--quotes=" + kTrickyQuoted + L" " - L"-- -- --not-a-switch " - L"\"in the time of submarines...\""); - - EXPECT_FALSE(cl.GetCommandLineString().empty()); - EXPECT_FALSE(cl.HasSwitch("cruller")); - EXPECT_FALSE(cl.HasSwitch("flim")); - EXPECT_FALSE(cl.HasSwitch("program")); - EXPECT_FALSE(cl.HasSwitch("dog")); - EXPECT_FALSE(cl.HasSwitch("cat")); - EXPECT_FALSE(cl.HasSwitch("output-rotation")); - EXPECT_FALSE(cl.HasSwitch("not-a-switch")); - EXPECT_FALSE(cl.HasSwitch("--")); - - EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(), - cl.GetProgram().value()); - - EXPECT_TRUE(cl.HasSwitch("foo")); - EXPECT_TRUE(cl.HasSwitch("bar")); - EXPECT_TRUE(cl.HasSwitch("baz")); - EXPECT_TRUE(cl.HasSwitch("spaetzle")); - EXPECT_TRUE(cl.HasSwitch("other-switches")); - EXPECT_TRUE(cl.HasSwitch("input-translation")); - EXPECT_TRUE(cl.HasSwitch("quotes")); - - EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle")); - EXPECT_EQ("", cl.GetSwitchValueASCII("foo")); - EXPECT_EQ("", cl.GetSwitchValueASCII("bar")); - EXPECT_EQ("", cl.GetSwitchValueASCII("cruller")); - EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII( - "other-switches")); - EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation")); - EXPECT_EQ(kTricky, cl.GetSwitchValueNative("quotes")); - - const CommandLine::StringVector& args = cl.GetArgs(); - ASSERT_EQ(5U, args.size()); - - std::vector::const_iterator iter = args.begin(); - EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("in the time of submarines..."), *iter); - ++iter; - EXPECT_TRUE(iter == args.end()); - - // Check that a generated string produces an equivalent command line. - CommandLine cl_duplicate = CommandLine::FromString(cl.GetCommandLineString()); - EXPECT_EQ(cl.GetCommandLineString(), cl_duplicate.GetCommandLineString()); -#endif -} - -// Tests behavior with an empty input string. -TEST(CommandLineTest, EmptyString) { -#if defined(OS_WIN) - CommandLine cl_from_string = CommandLine::FromString(L""); - EXPECT_TRUE(cl_from_string.GetCommandLineString().empty()); - EXPECT_TRUE(cl_from_string.GetProgram().empty()); - EXPECT_EQ(1U, cl_from_string.argv().size()); - EXPECT_TRUE(cl_from_string.GetArgs().empty()); -#endif - CommandLine cl_from_argv(0, nullptr); - EXPECT_TRUE(cl_from_argv.GetCommandLineString().empty()); - EXPECT_TRUE(cl_from_argv.GetProgram().empty()); - EXPECT_EQ(1U, cl_from_argv.argv().size()); - EXPECT_TRUE(cl_from_argv.GetArgs().empty()); -} - -TEST(CommandLineTest, GetArgumentsString) { - static const FilePath::CharType kPath1[] = - FILE_PATH_LITERAL("C:\\Some File\\With Spaces.ggg"); - static const FilePath::CharType kPath2[] = - FILE_PATH_LITERAL("C:\\no\\spaces.ggg"); - - static const char kFirstArgName[] = "first-arg"; - static const char kSecondArgName[] = "arg2"; - static const char kThirdArgName[] = "arg with space"; - static const char kFourthArgName[] = "nospace"; - static const char kFifthArgName[] = "%1"; - - CommandLine cl(CommandLine::NO_PROGRAM); - cl.AppendSwitchPath(kFirstArgName, FilePath(kPath1)); - cl.AppendSwitchPath(kSecondArgName, FilePath(kPath2)); - cl.AppendArg(kThirdArgName); - cl.AppendArg(kFourthArgName); - cl.AppendArg(kFifthArgName); - -#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_fifth_arg(UTF8ToUTF16(kFifthArgName)); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - CommandLine::StringType expected_first_arg(kFirstArgName); - CommandLine::StringType expected_second_arg(kSecondArgName); - CommandLine::StringType expected_third_arg(kThirdArgName); - CommandLine::StringType expected_fourth_arg(kFourthArgName); - CommandLine::StringType expected_fifth_arg(kFifthArgName); -#endif - -#if defined(OS_WIN) -#define QUOTE_ON_WIN FILE_PATH_LITERAL("\"") -#else -#define QUOTE_ON_WIN FILE_PATH_LITERAL("") -#endif // OS_WIN - - CommandLine::StringType expected_str; - expected_str.append(FILE_PATH_LITERAL("--")) - .append(expected_first_arg) - .append(FILE_PATH_LITERAL("=")) - .append(QUOTE_ON_WIN) - .append(kPath1) - .append(QUOTE_ON_WIN) - .append(FILE_PATH_LITERAL(" ")) - .append(FILE_PATH_LITERAL("--")) - .append(expected_second_arg) - .append(FILE_PATH_LITERAL("=")) - .append(QUOTE_ON_WIN) - .append(kPath2) - .append(QUOTE_ON_WIN) - .append(FILE_PATH_LITERAL(" ")) - .append(QUOTE_ON_WIN) - .append(expected_third_arg) - .append(QUOTE_ON_WIN) - .append(FILE_PATH_LITERAL(" ")) - .append(expected_fourth_arg) - .append(FILE_PATH_LITERAL(" ")); - - CommandLine::StringType expected_str_no_quote_placeholders(expected_str); - expected_str_no_quote_placeholders.append(expected_fifth_arg); - EXPECT_EQ(expected_str_no_quote_placeholders, cl.GetArgumentsString()); - -#if defined(OS_WIN) - CommandLine::StringType expected_str_quote_placeholders(expected_str); - expected_str_quote_placeholders.append(QUOTE_ON_WIN) - .append(expected_fifth_arg) - .append(QUOTE_ON_WIN); - EXPECT_EQ(expected_str_quote_placeholders, - cl.GetArgumentsStringWithPlaceholders()); -#endif -} - -// Test methods for appending switches to a command line. -TEST(CommandLineTest, AppendSwitches) { - std::string switch1 = "switch1"; - std::string switch2 = "switch2"; - std::string value2 = "value"; - std::string switch3 = "switch3"; - std::string value3 = "a value with spaces"; - std::string switch4 = "switch4"; - std::string value4 = "\"a value with quotes\""; - std::string switch5 = "quotes"; - CommandLine::StringType value5 = kTricky; - - CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); - - cl.AppendSwitch(switch1); - cl.AppendSwitchASCII(switch2, value2); - cl.AppendSwitchASCII(switch3, value3); - cl.AppendSwitchASCII(switch4, value4); - cl.AppendSwitchASCII(switch5, value4); - cl.AppendSwitchNative(switch5, value5); - - EXPECT_TRUE(cl.HasSwitch(switch1)); - EXPECT_TRUE(cl.HasSwitch(switch2)); - EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2)); - EXPECT_TRUE(cl.HasSwitch(switch3)); - EXPECT_EQ(value3, cl.GetSwitchValueASCII(switch3)); - EXPECT_TRUE(cl.HasSwitch(switch4)); - EXPECT_EQ(value4, cl.GetSwitchValueASCII(switch4)); - EXPECT_TRUE(cl.HasSwitch(switch5)); - EXPECT_EQ(value5, cl.GetSwitchValueNative(switch5)); - -#if defined(OS_WIN) - EXPECT_EQ(L"Program " - L"--switch1 " - L"--switch2=value " - L"--switch3=\"a value with spaces\" " - L"--switch4=\"\\\"a value with quotes\\\"\" " - // Even though the switches are unique, appending can add repeat - // switches to argv. - L"--quotes=\"\\\"a value with quotes\\\"\" " - L"--quotes=\"" + kTrickyQuoted + L"\"", - cl.GetCommandLineString()); -#endif -} - -TEST(CommandLineTest, AppendSwitchesDashDash) { - const CommandLine::CharType* raw_argv[] = { FILE_PATH_LITERAL("prog"), - FILE_PATH_LITERAL("--"), - FILE_PATH_LITERAL("--arg1") }; - CommandLine cl(arraysize(raw_argv), raw_argv); - - cl.AppendSwitch("switch1"); - cl.AppendSwitchASCII("switch2", "foo"); - - cl.AppendArg("--arg2"); - - EXPECT_EQ(FILE_PATH_LITERAL("prog --switch1 --switch2=foo -- --arg1 --arg2"), - cl.GetCommandLineString()); - CommandLine::StringVector cl_argv = cl.argv(); - EXPECT_EQ(FILE_PATH_LITERAL("prog"), cl_argv[0]); - EXPECT_EQ(FILE_PATH_LITERAL("--switch1"), cl_argv[1]); - EXPECT_EQ(FILE_PATH_LITERAL("--switch2=foo"), cl_argv[2]); - EXPECT_EQ(FILE_PATH_LITERAL("--"), cl_argv[3]); - EXPECT_EQ(FILE_PATH_LITERAL("--arg1"), cl_argv[4]); - EXPECT_EQ(FILE_PATH_LITERAL("--arg2"), cl_argv[5]); -} - -// Tests that when AppendArguments is called that the program is set correctly -// on the target CommandLine object and the switches from the source -// CommandLine are added to the target. -TEST(CommandLineTest, AppendArguments) { - CommandLine cl1(FilePath(FILE_PATH_LITERAL("Program"))); - cl1.AppendSwitch("switch1"); - cl1.AppendSwitchASCII("switch2", "foo"); - - CommandLine cl2(CommandLine::NO_PROGRAM); - cl2.AppendArguments(cl1, true); - EXPECT_EQ(cl1.GetProgram().value(), cl2.GetProgram().value()); - EXPECT_EQ(cl1.GetCommandLineString(), cl2.GetCommandLineString()); - - CommandLine c1(FilePath(FILE_PATH_LITERAL("Program1"))); - c1.AppendSwitch("switch1"); - CommandLine c2(FilePath(FILE_PATH_LITERAL("Program2"))); - c2.AppendSwitch("switch2"); - - c1.AppendArguments(c2, true); - EXPECT_EQ(c1.GetProgram().value(), c2.GetProgram().value()); - EXPECT_TRUE(c1.HasSwitch("switch1")); - EXPECT_TRUE(c1.HasSwitch("switch2")); -} - -#if defined(OS_WIN) -// Make sure that the command line string program paths are quoted as necessary. -// This only makes sense on Windows and the test is basically here to guard -// against regressions. -TEST(CommandLineTest, ProgramQuotes) { - // Check that quotes are not added for paths without spaces. - const FilePath kProgram(L"Program"); - CommandLine cl_program(kProgram); - EXPECT_EQ(kProgram.value(), cl_program.GetProgram().value()); - EXPECT_EQ(kProgram.value(), cl_program.GetCommandLineString()); - - const FilePath kProgramPath(L"Program Path"); - - // Check that quotes are not returned from GetProgram(). - CommandLine cl_program_path(kProgramPath); - EXPECT_EQ(kProgramPath.value(), cl_program_path.GetProgram().value()); - - // Check that quotes are added to command line string paths containing spaces. - CommandLine::StringType cmd_string(cl_program_path.GetCommandLineString()); - EXPECT_EQ(L"\"Program Path\"", cmd_string); - - // Check the optional quoting of placeholders in programs. - CommandLine cl_quote_placeholder(FilePath(L"%1")); - EXPECT_EQ(L"%1", cl_quote_placeholder.GetCommandLineString()); - EXPECT_EQ(L"\"%1\"", - cl_quote_placeholder.GetCommandLineStringWithPlaceholders()); -} -#endif - -// Calling Init multiple times should not modify the previous CommandLine. -TEST(CommandLineTest, Init) { - // Call Init without checking output once so we know it's been called - // whether or not the test runner does so. - CommandLine::Init(0, nullptr); - CommandLine* initial = CommandLine::ForCurrentProcess(); - EXPECT_FALSE(CommandLine::Init(0, nullptr)); - CommandLine* current = CommandLine::ForCurrentProcess(); - EXPECT_EQ(initial, current); -} - -// Test that copies of CommandLine have a valid StringPiece map. -TEST(CommandLineTest, Copy) { - std::unique_ptr initial( - new CommandLine(CommandLine::NO_PROGRAM)); - initial->AppendSwitch("a"); - initial->AppendSwitch("bbbbbbbbbbbbbbb"); - initial->AppendSwitch("c"); - CommandLine copy_constructed(*initial); - CommandLine assigned = *initial; - CommandLine::SwitchMap switch_map = initial->GetSwitches(); - initial.reset(); - for (const auto& pair : switch_map) - EXPECT_TRUE(copy_constructed.HasSwitch(pair.first)); - for (const auto& pair : switch_map) - EXPECT_TRUE(assigned.HasSwitch(pair.first)); -} - -TEST(CommandLineTest, PrependSimpleWrapper) { - CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); - cl.AppendSwitch("a"); - cl.AppendSwitch("b"); - cl.PrependWrapper(FILE_PATH_LITERAL("wrapper --foo --bar")); - - EXPECT_EQ(6u, cl.argv().size()); - EXPECT_EQ(FILE_PATH_LITERAL("wrapper"), cl.argv()[0]); - EXPECT_EQ(FILE_PATH_LITERAL("--foo"), cl.argv()[1]); - EXPECT_EQ(FILE_PATH_LITERAL("--bar"), cl.argv()[2]); - EXPECT_EQ(FILE_PATH_LITERAL("Program"), cl.argv()[3]); - EXPECT_EQ(FILE_PATH_LITERAL("--a"), cl.argv()[4]); - EXPECT_EQ(FILE_PATH_LITERAL("--b"), cl.argv()[5]); -} - -TEST(CommandLineTest, PrependComplexWrapper) { - CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); - cl.AppendSwitch("a"); - cl.AppendSwitch("b"); - cl.PrependWrapper( - FILE_PATH_LITERAL("wrapper --foo='hello world' --bar=\"let's go\"")); - - EXPECT_EQ(6u, cl.argv().size()); - EXPECT_EQ(FILE_PATH_LITERAL("wrapper"), cl.argv()[0]); - EXPECT_EQ(FILE_PATH_LITERAL("--foo='hello world'"), cl.argv()[1]); - EXPECT_EQ(FILE_PATH_LITERAL("--bar=\"let's go\""), cl.argv()[2]); - EXPECT_EQ(FILE_PATH_LITERAL("Program"), cl.argv()[3]); - EXPECT_EQ(FILE_PATH_LITERAL("--a"), cl.argv()[4]); - EXPECT_EQ(FILE_PATH_LITERAL("--b"), cl.argv()[5]); -} - -} // namespace base diff --git a/component_export.h b/component_export.h deleted file mode 100644 index b5cb364f0..000000000 --- a/component_export.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018 The Chromium 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_COMPONENT_EXPORT_H_ -#define BASE_COMPONENT_EXPORT_H_ - -#include "build/build_config.h" - -// Used to annotate symbols which are exported by the component named -// |component|. Note that this only does the right thing if the corresponding -// component target's sources are compiled with |IS_$component_IMPL| defined -// as 1. For example: -// -// class COMPONENT_EXPORT(FOO) Bar {}; -// -// If IS_FOO_IMPL=1 at compile time, then Bar will be annotated using the -// COMPONENT_EXPORT_ANNOTATION macro defined below. Otherwise it will be -// annotated using the COMPONENT_IMPORT_ANNOTATION macro. -#define COMPONENT_EXPORT(component) \ - COMPONENT_MACRO_CONDITIONAL_(IS_##component##_IMPL, \ - COMPONENT_EXPORT_ANNOTATION, \ - COMPONENT_IMPORT_ANNOTATION) - -// Indicates whether the current compilation unit is being compiled as part of -// the implementation of the component named |component|. Expands to |1| if -// |IS_$component_IMPL| is defined as |1|; expands to |0| otherwise. -// -// Note in particular that if |IS_$component_IMPL| is not defined at all, it is -// still fine to test INSIDE_COMPONENT_IMPL(component), which expands to |0| as -// expected. -#define INSIDE_COMPONENT_IMPL(component) \ - COMPONENT_MACRO_CONDITIONAL_(IS_##component##_IMPL, 1, 0) - -// Compiler-specific macros to annotate for export or import of a symbol. No-op -// in non-component builds. These should not see much if any direct use. -// Instead use the COMPONENT_EXPORT macro defined above. -#if defined(COMPONENT_BUILD) -#if defined(WIN32) -#define COMPONENT_EXPORT_ANNOTATION __declspec(dllexport) -#define COMPONENT_IMPORT_ANNOTATION __declspec(dllimport) -#else // defined(WIN32) -#define COMPONENT_EXPORT_ANNOTATION __attribute__((visibility("default"))) -#define COMPONENT_IMPORT_ANNOTATION -#endif // defined(WIN32) -#else // defined(COMPONENT_BUILD) -#define COMPONENT_EXPORT_ANNOTATION -#define COMPONENT_IMPORT_ANNOTATION -#endif // defined(COMPONENT_BUILD) - -// Below this point are several internal utility macros used for the -// implementation of the above macros. Not intended for external use. - -// Helper for conditional expansion to one of two token strings. If |condition| -// expands to |1| then this macro expands to |consequent|; otherwise it expands -// to |alternate|. -#define COMPONENT_MACRO_CONDITIONAL_(condition, consequent, alternate) \ - COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_( \ - COMPONENT_MACRO_CONDITIONAL_COMMA_(condition), consequent, alternate) - -// Expands to a comma (,) iff its first argument expands to |1|. Used in -// conjunction with |COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_()|, as the presence -// or absense of an extra comma can be used to conditionally shift subsequent -// argument positions and thus influence which argument is selected. -#define COMPONENT_MACRO_CONDITIONAL_COMMA_(...) \ - COMPONENT_MACRO_CONDITIONAL_COMMA_IMPL_(__VA_ARGS__,) -#define COMPONENT_MACRO_CONDITIONAL_COMMA_IMPL_(x, ...) \ - COMPONENT_MACRO_CONDITIONAL_COMMA_##x##_ -#define COMPONENT_MACRO_CONDITIONAL_COMMA_1_ , - -// Helper which simply selects its third argument. Used in conjunction with -// |COMPONENT_MACRO_CONDITIONAL_COMMA_()| above to implement conditional macro -// expansion. -#define COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_(...) \ - COMPONENT_MACRO_EXPAND_( \ - COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(__VA_ARGS__)) -#define COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(a, b, c, ...) c - -// Helper to work around MSVC quirkiness wherein a macro expansion like |,| -// within a parameter list will be treated as a single macro argument. This is -// needed to ensure that |COMPONENT_MACRO_CONDITIONAL_COMMA_()| above can expand -// to multiple separate positional arguments in the affirmative case, thus -// elliciting the desired conditional behavior with -// |COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_()|. -#define COMPONENT_MACRO_EXPAND_(x) x - -#endif // BASE_COMPONENT_EXPORT_H_ diff --git a/component_export_unittest.cc b/component_export_unittest.cc deleted file mode 100644 index e9943537c..000000000 --- a/component_export_unittest.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2018 The Chromium 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/component_export.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -using ComponentExportTest = testing::Test; - -#define IS_TEST_COMPONENT_A_IMPL 1 -#define IS_TEST_COMPONENT_B_IMPL -#define IS_TEST_COMPONENT_C_IMPL 0 -#define IS_TEST_COMPONENT_D_IMPL 2 -#define IS_TEST_COMPONENT_E_IMPL xyz - -TEST(ComponentExportTest, ImportExport) { - // Defined as 1. Treat as export. - EXPECT_EQ(1, INSIDE_COMPONENT_IMPL(TEST_COMPONENT_A)); - - // Defined, but empty. Treat as import. - EXPECT_EQ(0, INSIDE_COMPONENT_IMPL(TEST_COMPONENT_B)); - - // Defined, but 0. Treat as import. - EXPECT_EQ(0, INSIDE_COMPONENT_IMPL(TEST_COMPONENT_C)); - - // Defined, but some other arbitrary thing that isn't 1. Treat as import. - EXPECT_EQ(0, INSIDE_COMPONENT_IMPL(TEST_COMPONENT_D)); - EXPECT_EQ(0, INSIDE_COMPONENT_IMPL(TEST_COMPONENT_E)); - - // Undefined. Treat as import. - EXPECT_EQ(0, INSIDE_COMPONENT_IMPL(TEST_COMPONENT_F)); - - // And just for good measure, ensure that the macros evaluate properly in the - // context of preprocessor #if blocks. -#if INSIDE_COMPONENT_IMPL(TEST_COMPONENT_A) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif - -#if !INSIDE_COMPONENT_IMPL(TEST_COMPONENT_B) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif - -#if !INSIDE_COMPONENT_IMPL(TEST_COMPONENT_C) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif - -#if !INSIDE_COMPONENT_IMPL(TEST_COMPONENT_D) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif - -#if !INSIDE_COMPONENT_IMPL(TEST_COMPONENT_E) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif - -#if !INSIDE_COMPONENT_IMPL(TEST_COMPONENT_F) - EXPECT_TRUE(true); -#else - EXPECT_TRUE(false); -#endif -} - -#undef IS_TEST_COMPONENT_A_IMPL -#undef IS_TEST_COMPONENT_B_IMPL -#undef IS_TEST_COMPONENT_C_IMPL -#undef IS_TEST_COMPONENT_D_IMPL -#undef IS_TEST_COMPONENT_E_IMPL - -} // namespace -} // namespace base diff --git a/containers/OWNERS b/containers/OWNERS deleted file mode 100644 index cc39b28a1..000000000 --- a/containers/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -danakj@chromium.org -dcheng@chromium.org -vmpstr@chromium.org diff --git a/containers/README.md b/containers/README.md deleted file mode 100644 index 092a264c4..000000000 --- a/containers/README.md +++ /dev/null @@ -1,295 +0,0 @@ -# base/containers library - -## What goes here - -This directory contains some STL-like containers. - -Things should be moved here that are generally applicable across the code base. -Don't add things here just because you need them in one place and think others -may someday want something similar. You can put specialized containers in -your component's directory and we can promote them here later if we feel there -is broad applicability. - -### Design and naming - -Containers should adhere as closely to STL as possible. Functions and behaviors -not present in STL should only be added when they are related to the specific -data structure implemented by the container. - -For STL-like containers our policy is that they should use STL-like naming even -when it may conflict with the style guide. So functions and class names should -be lower case with underscores. Non-STL-like classes and functions should use -Google naming. Be sure to use the base namespace. - -## Map and set selection - -### Usage advice - - * Generally avoid **std::unordered\_set** and **std::unordered\_map**. In the - common case, query performance is unlikely to be sufficiently higher than - std::map to make a difference, insert performance is slightly worse, and - the memory overhead is high. This makes sense mostly for large tables where - you expect a lot of lookups. - - * Most maps and sets in Chrome are small and contain objects that can be - moved efficiently. In this case, consider **base::flat\_map** and - **base::flat\_set**. You need to be aware of the maximum expected size of - the container since individual inserts and deletes are O(n), giving O(n^2) - construction time for the entire map. But because it avoids mallocs in most - cases, inserts are better or comparable to other containers even for - several dozen items, and efficiently-moved types are unlikely to have - performance problems for most cases until you have hundreds of items. If - your container can be constructed in one shot, the constructor from vector - gives O(n log n) construction times and it should be strictly better than - a std::map. - - * **base::small\_map** has better runtime memory usage without the poor - mutation performance of large containers that base::flat\_map has. But this - advantage is partially offset by additional code size. Prefer in cases - where you make many objects so that the code/heap tradeoff is good. - - * Use **std::map** and **std::set** if you can't decide. Even if they're not - great, they're unlikely to be bad or surprising. - -### Map and set details - -Sizes are on 64-bit platforms. Stable iterators aren't invalidated when the -container is mutated. - -| Container | Empty size | Per-item overhead | Stable iterators? | -|:---------------------------------------- |:--------------------- |:----------------- |:----------------- | -| std::map, std::set | 16 bytes | 32 bytes | Yes | -| std::unordered\_map, std::unordered\_set | 128 bytes | 16-24 bytes | No | -| base::flat\_map and base::flat\_set | 24 bytes | 0 (see notes) | No | -| base::small\_map | 24 bytes (see notes) | 32 bytes | No | - -**Takeaways:** std::unordered\_map and std::unordered\_map have high -overhead for small container sizes, prefer these only for larger workloads. - -Code size comparisons for a block of code (see appendix) on Windows using -strings as keys. - -| Container | Code size | -|:------------------- |:---------- | -| std::unordered\_map | 1646 bytes | -| std::map | 1759 bytes | -| base::flat\_map | 1872 bytes | -| base::small\_map | 2410 bytes | - -**Takeaways:** base::small\_map generates more code because of the inlining of -both brute-force and red-black tree searching. This makes it less attractive -for random one-off uses. But if your code is called frequently, the runtime -memory benefits will be more important. The code sizes of the other maps are -close enough it's not worth worrying about. - -### std::map and std::set - -A red-black tree. Each inserted item requires the memory allocation of a node -on the heap. Each node contains a left pointer, a right pointer, a parent -pointer, and a "color" for the red-black tree (32-bytes per item on 64-bits). - -### std::unordered\_map and std::unordered\_set - -A hash table. Implemented on Windows as a std::vector + std::list and in libc++ -as the equivalent of a std::vector + a std::forward\_list. Both implementations -allocate an 8-entry hash table (containing iterators into the list) on -initialization, and grow to 64 entries once 8 items are inserted. Above 64 -items, the size doubles every time the load factor exceeds 1. - -The empty size is sizeof(std::unordered\_map) = 64 + -the initial hash table size which is 8 pointers. The per-item overhead in the -table above counts the list node (2 pointers on Windows, 1 pointer in libc++), -plus amortizes the hash table assuming a 0.5 load factor on average. - -In a microbenchmark on Windows, inserts of 1M integers into a -std::unordered\_set took 1.07x the time of std::set, and queries took 0.67x the -time of std::set. For a typical 4-entry set (the statistical mode of map sizes -in the browser), query performance is identical to std::set and base::flat\_set. -On ARM, unordered\_set performance can be worse because integer division to -compute the bucket is slow, and a few "less than" operations can be faster than -computing a hash depending on the key type. The takeaway is that you should not -default to using unordered maps because "they're faster." - -### base::flat\_map and base::flat\_set - -A sorted std::vector. Seached via binary search, inserts in the middle require -moving elements to make room. Good cache locality. For large objects and large -set sizes, std::vector's doubling-when-full strategy can waste memory. - -Supports efficient construction from a vector of items which avoids the O(n^2) -insertion time of each element separately. - -The per-item overhead will depend on the underlying std::vector's reallocation -strategy and the memory access pattern. Assuming items are being linearly added, -one would expect it to be 3/4 full, so per-item overhead will be 0.25 * -sizeof(T). - - -flat\_set/flat\_map support a notion of transparent comparisons. Therefore you -can, for example, lookup base::StringPiece in a set of std::strings without -constructing a temporary std::string. This functionality is based on C++14 -extensions to std::set/std::map interface. - -You can find more information about transparent comparisons here: -http://en.cppreference.com/w/cpp/utility/functional/less_void - -Example, smart pointer set: - -```cpp -// Declare a type alias using base::UniquePtrComparator. -template -using UniquePtrSet = base::flat_set, - base::UniquePtrComparator>; - -// ... -// Collect data. -std::vector> ptr_vec; -ptr_vec.reserve(5); -std::generate_n(std::back_inserter(ptr_vec), 5, []{ - return std::make_unique(0); -}); - -// Construct a set. -UniquePtrSet ptr_set(std::move(ptr_vec), base::KEEP_FIRST_OF_DUPES); - -// Use raw pointers to lookup keys. -int* ptr = ptr_set.begin()->get(); -EXPECT_TRUE(ptr_set.find(ptr) == ptr_set.begin()); -``` - -Example flat_map: - -```cpp -base::flat_map str_to_int({{"a", 1}, {"c", 2},{"b", 2}}, - base::KEEP_FIRST_OF_DUPES); - -// Does not construct temporary strings. -str_to_int.find("c")->second = 3; -str_to_int.erase("c"); -EXPECT_EQ(str_to_int.end(), str_to_int.find("c")->second); - -// NOTE: This does construct a temporary string. This happens since if the -// item is not in the container, then it needs to be constructed, which is -// something that transparent comparators don't have to guarantee. -str_to_int["c"] = 3; -``` - -### base::small\_map - -A small inline buffer that is brute-force searched that overflows into a full -std::map or std::unordered\_map. This gives the memory benefit of -base::flat\_map for small data sizes without the degenerate insertion -performance for large container sizes. - -Since instantiations require both code for a std::map and a brute-force search -of the inline container, plus a fancy iterator to cover both cases, code size -is larger. - -The initial size in the above table is assuming a very small inline table. The -actual size will be sizeof(int) + min(sizeof(std::map), sizeof(T) * -inline\_size). - -# Deque - -### Usage advice - -Chromium code should always use `base::circular_deque` or `base::queue` in -preference to `std::deque` or `std::queue` due to memory usage and platform -variation. - -The `base::circular_deque` implementation (and the `base::queue` which uses it) -provide performance consistent across platforms that better matches most -programmer's expectations on performance (it doesn't waste as much space as -libc++ and doesn't do as many heap allocations as MSVC). It also generates less -code tham `std::queue`: using it across the code base saves several hundred -kilobytes. - -Since `base::deque` does not have stable iterators and it will move the objects -it contains, it may not be appropriate for all uses. If you need these, -consider using a `std::list` which will provide constant time insert and erase. - -### std::deque and std::queue - -The implementation of `std::deque` varies considerably which makes it hard to -reason about. All implementations use a sequence of data blocks referenced by -an array of pointers. The standard guarantees random access, amortized -constant operations at the ends, and linear mutations in the middle. - -In Microsoft's implementation, each block is the smaller of 16 bytes or the -size of the contained element. This means in practice that every expansion of -the deque of non-trivial classes requires a heap allocation. libc++ (on Android -and Mac) uses 4K blocks which elimiates the problem of many heap allocations, -but generally wastes a large amount of space (an Android analysis revealed more -than 2.5MB wasted space from deque alone, resulting in some optimizations). -libstdc++ uses an intermediate-size 512 byte buffer. - -Microsoft's implementation never shrinks the deque capacity, so the capacity -will always be the maximum number of elements ever contained. libstdc++ -deallocates blocks as they are freed. libc++ keeps up to two empty blocks. - -### base::circular_deque and base::queue - -A deque implemented as a circular buffer in an array. The underlying array will -grow like a `std::vector` while the beginning and end of the deque will move -around. The items will wrap around the underlying buffer so the storage will -not be contiguous, but fast random access iterators are still possible. - -When the underlying buffer is filled, it will be reallocated and the constents -moved (like a `std::vector`). The underlying buffer will be shrunk if there is -too much wasted space (_unlike_ a `std::vector`). As a result, iterators are -not stable across mutations. - -# Stack - -`std::stack` is like `std::queue` in that it is a wrapper around an underlying -container. The default container is `std::deque` so everything from the deque -section applies. - -Chromium provides `base/containers/stack.h` which defines `base::stack` that -should be used in preference to std::stack. This changes the underlying -container to `base::circular_deque`. The result will be very similar to -manually specifying a `std::vector` for the underlying implementation except -that the storage will shrink when it gets too empty (vector will never -reallocate to a smaller size). - -Watch out: with some stack usage patterns it's easy to depend on unstable -behavior: - -```cpp -base::stack stack; -for (...) { - Foo& current = stack.top(); - DoStuff(); // May call stack.push(), say if writing a parser. - current.done = true; // Current may reference deleted item! -} -``` - -## Appendix - -### Code for map code size comparison - -This just calls insert and query a number of times, with printfs that prevent -things from being dead-code eliminated. - -```cpp -TEST(Foo, Bar) { - base::small_map> foo; - foo.insert(std::make_pair("foo", Flubber(8, "bar"))); - foo.insert(std::make_pair("bar", Flubber(8, "bar"))); - foo.insert(std::make_pair("foo1", Flubber(8, "bar"))); - foo.insert(std::make_pair("bar1", Flubber(8, "bar"))); - foo.insert(std::make_pair("foo", Flubber(8, "bar"))); - foo.insert(std::make_pair("bar", Flubber(8, "bar"))); - auto found = foo.find("asdf"); - printf("Found is %d\n", (int)(found == foo.end())); - found = foo.find("foo"); - printf("Found is %d\n", (int)(found == foo.end())); - found = foo.find("bar"); - printf("Found is %d\n", (int)(found == foo.end())); - found = foo.find("asdfhf"); - printf("Found is %d\n", (int)(found == foo.end())); - found = foo.find("bar1"); - printf("Found is %d\n", (int)(found == foo.end())); -} -``` - diff --git a/containers/adapters.h b/containers/adapters.h deleted file mode 100644 index fa671b46d..000000000 --- a/containers/adapters.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_ADAPTERS_H_ -#define BASE_CONTAINERS_ADAPTERS_H_ - -#include - -#include - -#include "base/macros.h" - -namespace base { - -namespace internal { - -// Internal adapter class for implementing base::Reversed. -template -class ReversedAdapter { - public: - using Iterator = decltype(static_cast(nullptr)->rbegin()); - - explicit ReversedAdapter(T& t) : t_(t) {} - ReversedAdapter(const ReversedAdapter& ra) : t_(ra.t_) {} - - // TODO(mdempsky): Once we can use C++14 library features, use std::rbegin - // and std::rend instead, so we can remove the specialization below. - Iterator begin() const { return t_.rbegin(); } - Iterator end() const { return t_.rend(); } - - private: - T& t_; - - DISALLOW_ASSIGN(ReversedAdapter); -}; - -template -class ReversedAdapter { - public: - using Iterator = std::reverse_iterator; - - explicit ReversedAdapter(T (&t)[N]) : t_(t) {} - ReversedAdapter(const ReversedAdapter& ra) : t_(ra.t_) {} - - Iterator begin() const { return Iterator(&t_[N]); } - Iterator end() const { return Iterator(&t_[0]); } - - private: - T (&t_)[N]; - - DISALLOW_ASSIGN(ReversedAdapter); -}; - -} // namespace internal - -// Reversed returns a container adapter usable in a range-based "for" statement -// for iterating a reversible container in reverse order. -// -// Example: -// -// std::vector v = ...; -// for (int i : base::Reversed(v)) { -// // iterates through v from back to front -// } -template -internal::ReversedAdapter Reversed(T& t) { - return internal::ReversedAdapter(t); -} - -} // namespace base - -#endif // BASE_CONTAINERS_ADAPTERS_H_ diff --git a/containers/adapters_unittest.cc b/containers/adapters_unittest.cc deleted file mode 100644 index 92554b7e1..000000000 --- a/containers/adapters_unittest.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/containers/adapters.h" - -#include - -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -TEST(AdaptersTest, Reversed) { - std::vector v; - v.push_back(3); - v.push_back(2); - v.push_back(1); - int j = 0; - for (int& i : base::Reversed(v)) { - EXPECT_EQ(++j, i); - i += 100; - } - EXPECT_EQ(103, v[0]); - EXPECT_EQ(102, v[1]); - EXPECT_EQ(101, v[2]); -} - -TEST(AdaptersTest, ReversedArray) { - int v[3] = {3, 2, 1}; - int j = 0; - for (int& i : base::Reversed(v)) { - EXPECT_EQ(++j, i); - i += 100; - } - EXPECT_EQ(103, v[0]); - EXPECT_EQ(102, v[1]); - EXPECT_EQ(101, v[2]); -} - -TEST(AdaptersTest, ReversedConst) { - std::vector v; - v.push_back(3); - v.push_back(2); - v.push_back(1); - const std::vector& cv = v; - int j = 0; - for (int i : base::Reversed(cv)) { - EXPECT_EQ(++j, i); - } -} - -} // namespace diff --git a/containers/circular_deque.h b/containers/circular_deque.h deleted file mode 100644 index bf42a9584..000000000 --- a/containers/circular_deque.h +++ /dev/null @@ -1,1111 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_CIRCULAR_DEQUE_H_ -#define BASE_CONTAINERS_CIRCULAR_DEQUE_H_ - -#include -#include -#include -#include -#include - -#include "base/containers/vector_buffer.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/template_util.h" - -// base::circular_deque is similar to std::deque. Unlike std::deque, the -// storage is provided in a flat circular buffer conceptually similar to a -// vector. The beginning and end will wrap around as necessary so that -// pushes and pops will be constant time as long as a capacity expansion is -// not required. -// -// The API should be identical to std::deque with the following differences: -// -// - ITERATORS ARE NOT STABLE. Mutating the container will invalidate all -// iterators. -// -// - Insertions may resize the vector and so are not constant time (std::deque -// guarantees constant time for insertions at the ends). -// -// - Container-wide comparisons are not implemented. If you want to compare -// two containers, use an algorithm so the expensive iteration is explicit. -// -// If you want a similar container with only a queue API, use base::queue in -// base/containers/queue.h. -// -// Constructors: -// circular_deque(); -// circular_deque(size_t count); -// circular_deque(size_t count, const T& value); -// circular_deque(InputIterator first, InputIterator last); -// circular_deque(const circular_deque&); -// circular_deque(circular_deque&&); -// circular_deque(std::initializer_list); -// -// Assignment functions: -// circular_deque& operator=(const circular_deque&); -// circular_deque& operator=(circular_deque&&); -// circular_deque& operator=(std::initializer_list); -// void assign(size_t count, const T& value); -// void assign(InputIterator first, InputIterator last); -// void assign(std::initializer_list value); -// -// Random accessors: -// T& at(size_t); -// const T& at(size_t) const; -// T& operator[](size_t); -// const T& operator[](size_t) const; -// -// End accessors: -// T& front(); -// const T& front() const; -// T& back(); -// const T& back() const; -// -// Iterator functions: -// iterator begin(); -// const_iterator begin() const; -// const_iterator cbegin() const; -// iterator end(); -// const_iterator end() const; -// const_iterator cend() const; -// reverse_iterator rbegin(); -// const_reverse_iterator rbegin() const; -// const_reverse_iterator crbegin() const; -// reverse_iterator rend(); -// const_reverse_iterator rend() const; -// const_reverse_iterator crend() const; -// -// Memory management: -// void reserve(size_t); // SEE IMPLEMENTATION FOR SOME GOTCHAS. -// size_t capacity() const; -// void shrink_to_fit(); -// -// Size management: -// void clear(); -// bool empty() const; -// size_t size() const; -// void resize(size_t); -// void resize(size_t count, const T& value); -// -// Positional insert and erase: -// void insert(const_iterator pos, size_type count, const T& value); -// void insert(const_iterator pos, -// InputIterator first, InputIterator last); -// iterator insert(const_iterator pos, const T& value); -// iterator insert(const_iterator pos, T&& value); -// iterator emplace(const_iterator pos, Args&&... args); -// iterator erase(const_iterator pos); -// iterator erase(const_iterator first, const_iterator last); -// -// End insert and erase: -// void push_front(const T&); -// void push_front(T&&); -// void push_back(const T&); -// void push_back(T&&); -// T& emplace_front(Args&&...); -// T& emplace_back(Args&&...); -// void pop_front(); -// void pop_back(); -// -// General: -// void swap(circular_deque&); - -namespace base { - -template -class circular_deque; - -namespace internal { - -// Start allocating nonempty buffers with this many entries. This is the -// external capacity so the internal buffer will be one larger (= 4) which is -// more even for the allocator. See the descriptions of internal vs. external -// capacity on the comment above the buffer_ variable below. -constexpr size_t kCircularBufferInitialCapacity = 3; - -template -class circular_deque_const_iterator { - public: - using difference_type = std::ptrdiff_t; - using value_type = T; - using pointer = const T*; - using reference = const T&; - using iterator_category = std::random_access_iterator_tag; - - circular_deque_const_iterator() : parent_deque_(nullptr), index_(0) { -#if DCHECK_IS_ON() - created_generation_ = 0; -#endif // DCHECK_IS_ON() - } - - // Dereferencing. - const T& operator*() const { - CheckUnstableUsage(); - parent_deque_->CheckValidIndex(index_); - return parent_deque_->buffer_[index_]; - } - const T* operator->() const { - CheckUnstableUsage(); - parent_deque_->CheckValidIndex(index_); - return &parent_deque_->buffer_[index_]; - } - const value_type& operator[](difference_type i) const { return *(*this + i); } - - // Increment and decrement. - circular_deque_const_iterator& operator++() { - Increment(); - return *this; - } - circular_deque_const_iterator operator++(int) { - circular_deque_const_iterator ret = *this; - Increment(); - return ret; - } - circular_deque_const_iterator& operator--() { - Decrement(); - return *this; - } - circular_deque_const_iterator operator--(int) { - circular_deque_const_iterator ret = *this; - Decrement(); - return ret; - } - - // Random access mutation. - friend circular_deque_const_iterator operator+( - const circular_deque_const_iterator& iter, - difference_type offset) { - circular_deque_const_iterator ret = iter; - ret.Add(offset); - return ret; - } - circular_deque_const_iterator& operator+=(difference_type offset) { - Add(offset); - return *this; - } - friend circular_deque_const_iterator operator-( - const circular_deque_const_iterator& iter, - difference_type offset) { - circular_deque_const_iterator ret = iter; - ret.Add(-offset); - return ret; - } - circular_deque_const_iterator& operator-=(difference_type offset) { - Add(-offset); - return *this; - } - - friend std::ptrdiff_t operator-(const circular_deque_const_iterator& lhs, - const circular_deque_const_iterator& rhs) { - lhs.CheckComparable(rhs); - return lhs.OffsetFromBegin() - rhs.OffsetFromBegin(); - } - - // Comparisons. - friend bool operator==(const circular_deque_const_iterator& lhs, - const circular_deque_const_iterator& rhs) { - lhs.CheckComparable(rhs); - return lhs.index_ == rhs.index_; - } - friend bool operator!=(const circular_deque_const_iterator& lhs, - const circular_deque_const_iterator& rhs) { - return !(lhs == rhs); - } - friend bool operator<(const circular_deque_const_iterator& lhs, - const circular_deque_const_iterator& rhs) { - lhs.CheckComparable(rhs); - return lhs.OffsetFromBegin() < rhs.OffsetFromBegin(); - } - friend bool operator<=(const circular_deque_const_iterator& lhs, - const circular_deque_const_iterator& rhs) { - return !(lhs > rhs); - } - friend bool operator>(const circular_deque_const_iterator& lhs, - const circular_deque_const_iterator& rhs) { - lhs.CheckComparable(rhs); - return lhs.OffsetFromBegin() > rhs.OffsetFromBegin(); - } - friend bool operator>=(const circular_deque_const_iterator& lhs, - const circular_deque_const_iterator& rhs) { - return !(lhs < rhs); - } - - protected: - friend class circular_deque; - - circular_deque_const_iterator(const circular_deque* parent, size_t index) - : parent_deque_(parent), index_(index) { -#if DCHECK_IS_ON() - created_generation_ = parent->generation_; -#endif // DCHECK_IS_ON() - } - - // Returns the offset from the beginning index of the buffer to the current - // item. - size_t OffsetFromBegin() const { - if (index_ >= parent_deque_->begin_) - return index_ - parent_deque_->begin_; // On the same side as begin. - return parent_deque_->buffer_.capacity() - parent_deque_->begin_ + index_; - } - - // Most uses will be ++ and -- so use a simplified implementation. - void Increment() { - CheckUnstableUsage(); - parent_deque_->CheckValidIndex(index_); - index_++; - if (index_ == parent_deque_->buffer_.capacity()) - index_ = 0; - } - void Decrement() { - CheckUnstableUsage(); - parent_deque_->CheckValidIndexOrEnd(index_); - if (index_ == 0) - index_ = parent_deque_->buffer_.capacity() - 1; - else - index_--; - } - void Add(difference_type delta) { - CheckUnstableUsage(); -#if DCHECK_IS_ON() - if (delta <= 0) - parent_deque_->CheckValidIndexOrEnd(index_); - else - parent_deque_->CheckValidIndex(index_); -#endif - // It should be valid to add 0 to any iterator, even if the container is - // empty and the iterator points to end(). The modulo below will divide - // by 0 if the buffer capacity is empty, so it's important to check for - // this case explicitly. - if (delta == 0) - return; - - difference_type new_offset = OffsetFromBegin() + delta; - DCHECK(new_offset >= 0 && - new_offset <= static_cast(parent_deque_->size())); - index_ = (new_offset + parent_deque_->begin_) % - parent_deque_->buffer_.capacity(); - } - -#if DCHECK_IS_ON() - void CheckUnstableUsage() const { - DCHECK(parent_deque_); - // Since circular_deque doesn't guarantee stability, any attempt to - // dereference this iterator after a mutation (i.e. the generation doesn't - // match the original) in the container is illegal. - DCHECK_EQ(created_generation_, parent_deque_->generation_) - << "circular_deque iterator dereferenced after mutation."; - } - void CheckComparable(const circular_deque_const_iterator& other) const { - DCHECK_EQ(parent_deque_, other.parent_deque_); - // Since circular_deque doesn't guarantee stability, two iterators that - // are compared must have been generated without mutating the container. - // If this fires, the container was mutated between generating the two - // iterators being compared. - DCHECK_EQ(created_generation_, other.created_generation_); - } -#else - inline void CheckUnstableUsage() const {} - inline void CheckComparable(const circular_deque_const_iterator&) const {} -#endif // DCHECK_IS_ON() - - const circular_deque* parent_deque_; - size_t index_; - -#if DCHECK_IS_ON() - // The generation of the parent deque when this iterator was created. The - // container will update the generation for every modification so we can - // test if the container was modified by comparing them. - uint64_t created_generation_; -#endif // DCHECK_IS_ON() -}; - -template -class circular_deque_iterator : public circular_deque_const_iterator { - using base = circular_deque_const_iterator; - - public: - friend class circular_deque; - - using difference_type = std::ptrdiff_t; - using value_type = T; - using pointer = T*; - using reference = T&; - using iterator_category = std::random_access_iterator_tag; - - // Expose the base class' constructor. - circular_deque_iterator() : circular_deque_const_iterator() {} - - // Dereferencing. - T& operator*() const { return const_cast(base::operator*()); } - T* operator->() const { return const_cast(base::operator->()); } - T& operator[](difference_type i) { - return const_cast(base::operator[](i)); - } - - // Random access mutation. - friend circular_deque_iterator operator+(const circular_deque_iterator& iter, - difference_type offset) { - circular_deque_iterator ret = iter; - ret.Add(offset); - return ret; - } - circular_deque_iterator& operator+=(difference_type offset) { - base::Add(offset); - return *this; - } - friend circular_deque_iterator operator-(const circular_deque_iterator& iter, - difference_type offset) { - circular_deque_iterator ret = iter; - ret.Add(-offset); - return ret; - } - circular_deque_iterator& operator-=(difference_type offset) { - base::Add(-offset); - return *this; - } - - // Increment and decrement. - circular_deque_iterator& operator++() { - base::Increment(); - return *this; - } - circular_deque_iterator operator++(int) { - circular_deque_iterator ret = *this; - base::Increment(); - return ret; - } - circular_deque_iterator& operator--() { - base::Decrement(); - return *this; - } - circular_deque_iterator operator--(int) { - circular_deque_iterator ret = *this; - base::Decrement(); - return ret; - } - - private: - circular_deque_iterator(const circular_deque* parent, size_t index) - : circular_deque_const_iterator(parent, index) {} -}; - -} // namespace internal - -template -class circular_deque { - private: - using VectorBuffer = internal::VectorBuffer; - - public: - using value_type = T; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using reference = value_type&; - using const_reference = const value_type&; - using pointer = value_type*; - using const_pointer = const value_type*; - - using iterator = internal::circular_deque_iterator; - using const_iterator = internal::circular_deque_const_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - // --------------------------------------------------------------------------- - // Constructor - - constexpr circular_deque() = default; - - // Constructs with |count| copies of |value| or default constructed version. - circular_deque(size_type count) { resize(count); } - circular_deque(size_type count, const T& value) { resize(count, value); } - - // Range constructor. - template - circular_deque(InputIterator first, InputIterator last) { - assign(first, last); - } - - // Copy/move. - circular_deque(const circular_deque& other) : buffer_(other.size() + 1) { - assign(other.begin(), other.end()); - } - circular_deque(circular_deque&& other) noexcept - : buffer_(std::move(other.buffer_)), - begin_(other.begin_), - end_(other.end_) { - other.begin_ = 0; - other.end_ = 0; - } - - circular_deque(std::initializer_list init) { assign(init); } - - ~circular_deque() { DestructRange(begin_, end_); } - - // --------------------------------------------------------------------------- - // Assignments. - // - // All of these may invalidate iterators and references. - - circular_deque& operator=(const circular_deque& other) { - if (&other == this) - return *this; - - reserve(other.size()); - assign(other.begin(), other.end()); - return *this; - } - circular_deque& operator=(circular_deque&& other) noexcept { - if (&other == this) - return *this; - - // We're about to overwrite the buffer, so don't free it in clear to - // avoid doing it twice. - ClearRetainCapacity(); - buffer_ = std::move(other.buffer_); - begin_ = other.begin_; - end_ = other.end_; - - other.begin_ = 0; - other.end_ = 0; - - IncrementGeneration(); - return *this; - } - circular_deque& operator=(std::initializer_list ilist) { - reserve(ilist.size()); - assign(std::begin(ilist), std::end(ilist)); - return *this; - } - - void assign(size_type count, const value_type& value) { - ClearRetainCapacity(); - reserve(count); - for (size_t i = 0; i < count; i++) - emplace_back(value); - IncrementGeneration(); - } - - // This variant should be enabled only when InputIterator is an iterator. - template - typename std::enable_if<::base::internal::is_iterator::value, - void>::type - assign(InputIterator first, InputIterator last) { - // Possible future enhancement, dispatch on iterator tag type. For forward - // iterators we can use std::difference to preallocate the space required - // and only do one copy. - ClearRetainCapacity(); - for (; first != last; ++first) - emplace_back(*first); - IncrementGeneration(); - } - - void assign(std::initializer_list value) { - reserve(std::distance(value.begin(), value.end())); - assign(value.begin(), value.end()); - } - - // --------------------------------------------------------------------------- - // Accessors. - // - // Since this class assumes no exceptions, at() and operator[] are equivalent. - - const value_type& at(size_type i) const { - DCHECK(i < size()); - size_t right_size = buffer_.capacity() - begin_; - if (begin_ <= end_ || i < right_size) - return buffer_[begin_ + i]; - return buffer_[i - right_size]; - } - value_type& at(size_type i) { - return const_cast( - const_cast(this)->at(i)); - } - - value_type& operator[](size_type i) { return at(i); } - const value_type& operator[](size_type i) const { - return const_cast(this)->at(i); - } - - value_type& front() { - DCHECK(!empty()); - return buffer_[begin_]; - } - const value_type& front() const { - DCHECK(!empty()); - return buffer_[begin_]; - } - - value_type& back() { - DCHECK(!empty()); - return *(--end()); - } - const value_type& back() const { - DCHECK(!empty()); - return *(--end()); - } - - // --------------------------------------------------------------------------- - // Iterators. - - iterator begin() { return iterator(this, begin_); } - const_iterator begin() const { return const_iterator(this, begin_); } - const_iterator cbegin() const { return const_iterator(this, begin_); } - - iterator end() { return iterator(this, end_); } - const_iterator end() const { return const_iterator(this, end_); } - const_iterator cend() const { return const_iterator(this, end_); } - - reverse_iterator rbegin() { return reverse_iterator(end()); } - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end()); - } - const_reverse_iterator crbegin() const { return rbegin(); } - - reverse_iterator rend() { return reverse_iterator(begin()); } - const_reverse_iterator rend() const { - return const_reverse_iterator(begin()); - } - const_reverse_iterator crend() const { return rend(); } - - // --------------------------------------------------------------------------- - // Memory management. - - // IMPORTANT NOTE ON reserve(...): This class implements auto-shrinking of - // the buffer when elements are deleted and there is "too much" wasted space. - // So if you call reserve() with a large size in anticipation of pushing many - // elements, but pop an element before the queue is full, the capacity you - // reserved may be lost. - // - // As a result, it's only worthwhile to call reserve() when you're adding - // many things at once with no intermediate operations. - void reserve(size_type new_capacity) { - if (new_capacity > capacity()) - SetCapacityTo(new_capacity); - } - - size_type capacity() const { - // One item is wasted to indicate end(). - return buffer_.capacity() == 0 ? 0 : buffer_.capacity() - 1; - } - - void shrink_to_fit() { - if (empty()) { - // Optimize empty case to really delete everything if there was - // something. - if (buffer_.capacity()) - buffer_ = VectorBuffer(); - } else { - SetCapacityTo(size()); - } - } - - // --------------------------------------------------------------------------- - // Size management. - - // This will additionally reset the capacity() to 0. - void clear() { - // This can't resize(0) because that requires a default constructor to - // compile, which not all contained classes may implement. - ClearRetainCapacity(); - buffer_ = VectorBuffer(); - } - - bool empty() const { return begin_ == end_; } - - size_type size() const { - if (begin_ <= end_) - return end_ - begin_; - return buffer_.capacity() - begin_ + end_; - } - - // When reducing size, the elements are deleted from the end. When expanding - // size, elements are added to the end with |value| or the default - // constructed version. Even when using resize(count) to shrink, a default - // constructor is required for the code to compile, even though it will not - // be called. - // - // There are two versions rather than using a default value to avoid - // creating a temporary when shrinking (when it's not needed). Plus if - // the default constructor is desired when expanding usually just calling it - // for each element is faster than making a default-constructed temporary and - // copying it. - void resize(size_type count) { - // SEE BELOW VERSION if you change this. The code is mostly the same. - if (count > size()) { - // This could be slighly more efficient but expanding a queue with - // identical elements is unusual and the extra computations of emplacing - // one-by-one will typically be small relative to calling the constructor - // for every item. - ExpandCapacityIfNecessary(count - size()); - while (size() < count) - emplace_back(); - } else if (count < size()) { - size_t new_end = (begin_ + count) % buffer_.capacity(); - DestructRange(new_end, end_); - end_ = new_end; - - ShrinkCapacityIfNecessary(); - } - IncrementGeneration(); - } - void resize(size_type count, const value_type& value) { - // SEE ABOVE VERSION if you change this. The code is mostly the same. - if (count > size()) { - ExpandCapacityIfNecessary(count - size()); - while (size() < count) - emplace_back(value); - } else if (count < size()) { - size_t new_end = (begin_ + count) % buffer_.capacity(); - DestructRange(new_end, end_); - end_ = new_end; - - ShrinkCapacityIfNecessary(); - } - IncrementGeneration(); - } - - // --------------------------------------------------------------------------- - // Insert and erase. - // - // Insertion and deletion in the middle is O(n) and invalidates all existing - // iterators. - // - // The implementation of insert isn't optimized as much as it could be. If - // the insertion requires that the buffer be grown, it will first be grown - // and everything moved, and then the items will be inserted, potentially - // moving some items twice. This simplifies the implemntation substantially - // and means less generated templatized code. Since this is an uncommon - // operation for deques, and already relatively slow, it doesn't seem worth - // the benefit to optimize this. - - void insert(const_iterator pos, size_type count, const T& value) { - ValidateIterator(pos); - - // Optimize insert at the beginning. - if (pos == begin()) { - ExpandCapacityIfNecessary(count); - for (size_t i = 0; i < count; i++) - push_front(value); - return; - } - - iterator insert_cur(this, pos.index_); - iterator insert_end; - MakeRoomFor(count, &insert_cur, &insert_end); - while (insert_cur < insert_end) { - new (&buffer_[insert_cur.index_]) T(value); - ++insert_cur; - } - - IncrementGeneration(); - } - - // This enable_if keeps this call from getting confused with the (pos, count, - // value) version when value is an integer. - template - typename std::enable_if<::base::internal::is_iterator::value, - void>::type - insert(const_iterator pos, InputIterator first, InputIterator last) { - ValidateIterator(pos); - - size_t inserted_items = std::distance(first, last); - if (inserted_items == 0) - return; // Can divide by 0 when doing modulo below, so return early. - - // Make a hole to copy the items into. - iterator insert_cur; - iterator insert_end; - if (pos == begin()) { - // Optimize insert at the beginning, nothing needs to be shifted and the - // hole is the |inserted_items| block immediately before |begin_|. - ExpandCapacityIfNecessary(inserted_items); - insert_end = begin(); - begin_ = - (begin_ + buffer_.capacity() - inserted_items) % buffer_.capacity(); - insert_cur = begin(); - } else { - insert_cur = iterator(this, pos.index_); - MakeRoomFor(inserted_items, &insert_cur, &insert_end); - } - - // Copy the items. - while (insert_cur < insert_end) { - new (&buffer_[insert_cur.index_]) T(*first); - ++insert_cur; - ++first; - } - - IncrementGeneration(); - } - - // These all return an iterator to the inserted item. Existing iterators will - // be invalidated. - iterator insert(const_iterator pos, const T& value) { - return emplace(pos, value); - } - iterator insert(const_iterator pos, T&& value) { - return emplace(pos, std::move(value)); - } - template - iterator emplace(const_iterator pos, Args&&... args) { - ValidateIterator(pos); - - // Optimize insert at beginning which doesn't require shifting. - if (pos == cbegin()) { - emplace_front(std::forward(args)...); - return begin(); - } - - // Do this before we make the new iterators we return. - IncrementGeneration(); - - iterator insert_begin(this, pos.index_); - iterator insert_end; - MakeRoomFor(1, &insert_begin, &insert_end); - new (&buffer_[insert_begin.index_]) T(std::forward(args)...); - - return insert_begin; - } - - // Calling erase() won't automatically resize the buffer smaller like resize - // or the pop functions. Erase is slow and relatively uncommon, and for - // normal deque usage a pop will normally be done on a regular basis that - // will prevent excessive buffer usage over long periods of time. It's not - // worth having the extra code for every template instantiation of erase() - // to resize capacity downward to a new buffer. - iterator erase(const_iterator pos) { return erase(pos, pos + 1); } - iterator erase(const_iterator first, const_iterator last) { - ValidateIterator(first); - ValidateIterator(last); - - IncrementGeneration(); - - // First, call the destructor on the deleted items. - if (first.index_ == last.index_) { - // Nothing deleted. Need to return early to avoid falling through to - // moving items on top of themselves. - return iterator(this, first.index_); - } else if (first.index_ < last.index_) { - // Contiguous range. - buffer_.DestructRange(&buffer_[first.index_], &buffer_[last.index_]); - } else { - // Deleted range wraps around. - buffer_.DestructRange(&buffer_[first.index_], - &buffer_[buffer_.capacity()]); - buffer_.DestructRange(&buffer_[0], &buffer_[last.index_]); - } - - if (first.index_ == begin_) { - // This deletion is from the beginning. Nothing needs to be copied, only - // begin_ needs to be updated. - begin_ = last.index_; - return iterator(this, last.index_); - } - - // In an erase operation, the shifted items all move logically to the left, - // so move them from left-to-right. - iterator move_src(this, last.index_); - iterator move_src_end = end(); - iterator move_dest(this, first.index_); - for (; move_src < move_src_end; move_src++, move_dest++) { - buffer_.MoveRange(&buffer_[move_src.index_], - &buffer_[move_src.index_ + 1], - &buffer_[move_dest.index_]); - } - - end_ = move_dest.index_; - - // Since we did not reallocate and only changed things after the erase - // element(s), the input iterator's index points to the thing following the - // deletion. - return iterator(this, first.index_); - } - - // --------------------------------------------------------------------------- - // Begin/end operations. - - void push_front(const T& value) { emplace_front(value); } - void push_front(T&& value) { emplace_front(std::move(value)); } - - void push_back(const T& value) { emplace_back(value); } - void push_back(T&& value) { emplace_back(std::move(value)); } - - template - reference emplace_front(Args&&... args) { - ExpandCapacityIfNecessary(1); - if (begin_ == 0) - begin_ = buffer_.capacity() - 1; - else - begin_--; - IncrementGeneration(); - new (&buffer_[begin_]) T(std::forward(args)...); - return front(); - } - - template - reference emplace_back(Args&&... args) { - ExpandCapacityIfNecessary(1); - new (&buffer_[end_]) T(std::forward(args)...); - if (end_ == buffer_.capacity() - 1) - end_ = 0; - else - end_++; - IncrementGeneration(); - return back(); - } - - void pop_front() { - DCHECK(size()); - buffer_.DestructRange(&buffer_[begin_], &buffer_[begin_ + 1]); - begin_++; - if (begin_ == buffer_.capacity()) - begin_ = 0; - - ShrinkCapacityIfNecessary(); - - // Technically popping will not invalidate any iterators since the - // underlying buffer will be stable. But in the future we may want to add a - // feature that resizes the buffer smaller if there is too much wasted - // space. This ensures we can make such a change safely. - IncrementGeneration(); - } - void pop_back() { - DCHECK(size()); - if (end_ == 0) - end_ = buffer_.capacity() - 1; - else - end_--; - buffer_.DestructRange(&buffer_[end_], &buffer_[end_ + 1]); - - ShrinkCapacityIfNecessary(); - - // See pop_front comment about why this is here. - IncrementGeneration(); - } - - // --------------------------------------------------------------------------- - // General operations. - - void swap(circular_deque& other) { - std::swap(buffer_, other.buffer_); - std::swap(begin_, other.begin_); - std::swap(end_, other.end_); - IncrementGeneration(); - } - - friend void swap(circular_deque& lhs, circular_deque& rhs) { lhs.swap(rhs); } - - private: - friend internal::circular_deque_iterator; - friend internal::circular_deque_const_iterator; - - // Moves the items in the given circular buffer to the current one. The - // source is moved from so will become invalid. The destination buffer must - // have already been allocated with enough size. - static void MoveBuffer(VectorBuffer& from_buf, - size_t from_begin, - size_t from_end, - VectorBuffer* to_buf, - size_t* to_begin, - size_t* to_end) { - size_t from_capacity = from_buf.capacity(); - - *to_begin = 0; - if (from_begin < from_end) { - // Contiguous. - from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_end], - to_buf->begin()); - *to_end = from_end - from_begin; - } else if (from_begin > from_end) { - // Discontiguous, copy the right side to the beginning of the new buffer. - from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_capacity], - to_buf->begin()); - size_t right_size = from_capacity - from_begin; - // Append the left side. - from_buf.MoveRange(&from_buf[0], &from_buf[from_end], - &(*to_buf)[right_size]); - *to_end = right_size + from_end; - } else { - // No items. - *to_end = 0; - } - } - - // Expands the buffer size. This assumes the size is larger than the - // number of elements in the vector (it won't call delete on anything). - void SetCapacityTo(size_t new_capacity) { - // Use the capacity + 1 as the internal buffer size to differentiate - // empty and full (see definition of buffer_ below). - VectorBuffer new_buffer(new_capacity + 1); - MoveBuffer(buffer_, begin_, end_, &new_buffer, &begin_, &end_); - buffer_ = std::move(new_buffer); - } - void ExpandCapacityIfNecessary(size_t additional_elts) { - size_t min_new_capacity = size() + additional_elts; - if (capacity() >= min_new_capacity) - return; // Already enough room. - - min_new_capacity = - std::max(min_new_capacity, internal::kCircularBufferInitialCapacity); - - // std::vector always grows by at least 50%. WTF::Deque grows by at least - // 25%. We expect queue workloads to generally stay at a similar size and - // grow less than a vector might, so use 25%. - size_t new_capacity = - std::max(min_new_capacity, capacity() + capacity() / 4); - SetCapacityTo(new_capacity); - } - - void ShrinkCapacityIfNecessary() { - // Don't auto-shrink below this size. - if (capacity() <= internal::kCircularBufferInitialCapacity) - return; - - // Shrink when 100% of the size() is wasted. - size_t sz = size(); - size_t empty_spaces = capacity() - sz; - if (empty_spaces < sz) - return; - - // Leave 1/4 the size as free capacity, not going below the initial - // capacity. - size_t new_capacity = - std::max(internal::kCircularBufferInitialCapacity, sz + sz / 4); - if (new_capacity < capacity()) { - // Count extra item to convert to internal capacity. - SetCapacityTo(new_capacity); - } - } - - // Backend for clear() but does not resize the internal buffer. - void ClearRetainCapacity() { - // This can't resize(0) because that requires a default constructor to - // compile, which not all contained classes may implement. - DestructRange(begin_, end_); - begin_ = 0; - end_ = 0; - IncrementGeneration(); - } - - // Calls destructors for the given begin->end indices. The indices may wrap - // around. The buffer is not resized, and the begin_ and end_ members are - // not changed. - void DestructRange(size_t begin, size_t end) { - if (end == begin) { - return; - } else if (end > begin) { - buffer_.DestructRange(&buffer_[begin], &buffer_[end]); - } else { - buffer_.DestructRange(&buffer_[begin], &buffer_[buffer_.capacity()]); - buffer_.DestructRange(&buffer_[0], &buffer_[end]); - } - } - - // Makes room for |count| items starting at |*insert_begin|. Since iterators - // are not stable across buffer resizes, |*insert_begin| will be updated to - // point to the beginning of the newly opened position in the new array (it's - // in/out), and the end of the newly opened position (it's out-only). - void MakeRoomFor(size_t count, iterator* insert_begin, iterator* insert_end) { - if (count == 0) { - *insert_end = *insert_begin; - return; - } - - // The offset from the beginning will be stable across reallocations. - size_t begin_offset = insert_begin->OffsetFromBegin(); - ExpandCapacityIfNecessary(count); - - insert_begin->index_ = (begin_ + begin_offset) % buffer_.capacity(); - *insert_end = - iterator(this, (insert_begin->index_ + count) % buffer_.capacity()); - - // Update the new end and prepare the iterators for copying. - iterator src = end(); - end_ = (end_ + count) % buffer_.capacity(); - iterator dest = end(); - - // Move the elements. This will always involve shifting logically to the - // right, so move in a right-to-left order. - while (true) { - if (src == *insert_begin) - break; - --src; - --dest; - buffer_.MoveRange(&buffer_[src.index_], &buffer_[src.index_ + 1], - &buffer_[dest.index_]); - } - } - -#if DCHECK_IS_ON() - // Asserts the given index is dereferencable. The index is an index into the - // buffer, not an index used by operator[] or at() which will be offsets from - // begin. - void CheckValidIndex(size_t i) const { - if (begin_ <= end_) - DCHECK(i >= begin_ && i < end_); - else - DCHECK((i >= begin_ && i < buffer_.capacity()) || i < end_); - } - - // Asserts the given index is either dereferencable or points to end(). - void CheckValidIndexOrEnd(size_t i) const { - if (i != end_) - CheckValidIndex(i); - } - - void ValidateIterator(const const_iterator& i) const { - DCHECK(i.parent_deque_ == this); - i.CheckUnstableUsage(); - } - - // See generation_ below. - void IncrementGeneration() { generation_++; } -#else - // No-op versions of these functions for release builds. - void CheckValidIndex(size_t) const {} - void CheckValidIndexOrEnd(size_t) const {} - void ValidateIterator(const const_iterator& i) const {} - void IncrementGeneration() {} -#endif - - // Danger, the buffer_.capacity() is the "internal capacity" which is - // capacity() + 1 since there is an extra item to indicate the end. Otherwise - // being completely empty and completely full are indistinguishable (begin == - // end). We could add a separate flag to avoid it, but that adds significant - // extra complexity since every computation will have to check for it. Always - // keeping one extra unused element in the buffer makes iterator computations - // much simpler. - // - // Container internal code will want to use buffer_.capacity() for offset - // computations rather than capacity(). - VectorBuffer buffer_; - size_type begin_ = 0; - size_type end_ = 0; - -#if DCHECK_IS_ON() - // Incremented every time a modification is made that could affect iterator - // invalidations. - uint64_t generation_ = 0; -#endif -}; - -// Implementations of base::Erase[If] (see base/stl_util.h). -template -void Erase(circular_deque& container, const Value& value) { - container.erase(std::remove(container.begin(), container.end(), value), - container.end()); -} - -template -void EraseIf(circular_deque& container, Predicate pred) { - container.erase(std::remove_if(container.begin(), container.end(), pred), - container.end()); -} - -} // namespace base - -#endif // BASE_CONTAINERS_CIRCULAR_DEQUE_H_ diff --git a/containers/circular_deque_unittest.cc b/containers/circular_deque_unittest.cc deleted file mode 100644 index 0c168e0c8..000000000 --- a/containers/circular_deque_unittest.cc +++ /dev/null @@ -1,881 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/containers/circular_deque.h" - -#include "base/test/copy_only_int.h" -#include "base/test/move_only_int.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::internal::VectorBuffer; - -namespace base { - -namespace { - -circular_deque MakeSequence(size_t max) { - circular_deque ret; - for (size_t i = 0; i < max; i++) - ret.push_back(i); - return ret; -} - -// Cycles through the queue, popping items from the back and pushing items -// at the front to validate behavior across different configurations of the -// queue in relation to the underlying buffer. The tester closure is run for -// each cycle. -template -void CycleTest(circular_deque& queue, const Tester& tester) { - size_t steps = queue.size() * 2; - for (size_t i = 0; i < steps; i++) { - tester(queue, i); - queue.pop_back(); - queue.push_front(QueueT()); - } -} - -class DestructorCounter { - public: - DestructorCounter(int* counter) : counter_(counter) {} - ~DestructorCounter() { ++(*counter_); } - - private: - int* counter_; -}; - -} // namespace - -TEST(CircularDeque, FillConstructor) { - constexpr size_t num_elts = 9; - - std::vector foo(15); - EXPECT_EQ(15u, foo.size()); - - // Fill with default constructor. - { - circular_deque buf(num_elts); - - EXPECT_EQ(num_elts, buf.size()); - EXPECT_EQ(num_elts, static_cast(buf.end() - buf.begin())); - - for (size_t i = 0; i < num_elts; i++) - EXPECT_EQ(0, buf[i]); - } - - // Fill with explicit value. - { - int value = 199; - circular_deque buf(num_elts, value); - - EXPECT_EQ(num_elts, buf.size()); - EXPECT_EQ(num_elts, static_cast(buf.end() - buf.begin())); - - for (size_t i = 0; i < num_elts; i++) - EXPECT_EQ(value, buf[i]); - } -} - -TEST(CircularDeque, CopyAndRangeConstructor) { - int values[] = {1, 2, 3, 4, 5, 6}; - circular_deque first(std::begin(values), std::end(values)); - - circular_deque second(first); - EXPECT_EQ(6u, second.size()); - for (int i = 0; i < 6; i++) - EXPECT_EQ(i + 1, second[i].data()); -} - -TEST(CircularDeque, MoveConstructor) { - int values[] = {1, 2, 3, 4, 5, 6}; - circular_deque first(std::begin(values), std::end(values)); - - circular_deque second(std::move(first)); - EXPECT_TRUE(first.empty()); - EXPECT_EQ(6u, second.size()); - for (int i = 0; i < 6; i++) - EXPECT_EQ(i + 1, second[i].data()); -} - -TEST(CircularDeque, InitializerListConstructor) { - circular_deque empty({}); - ASSERT_TRUE(empty.empty()); - - circular_deque first({1, 2, 3, 4, 5, 6}); - EXPECT_EQ(6u, first.size()); - for (int i = 0; i < 6; i++) - EXPECT_EQ(i + 1, first[i]); -} - -TEST(CircularDeque, Destructor) { - int destruct_count = 0; - - // Contiguous buffer. - { - circular_deque q; - q.resize(5, DestructorCounter(&destruct_count)); - - EXPECT_EQ(1, destruct_count); // The temporary in the call to resize(). - destruct_count = 0; - } - EXPECT_EQ(5, destruct_count); // One call for each. - - // Force a wraparound buffer. - { - circular_deque q; - q.reserve(7); - q.resize(5, DestructorCounter(&destruct_count)); - - // Cycle throught some elements in our buffer to force a wraparound. - destruct_count = 0; - for (int i = 0; i < 4; i++) { - q.emplace_back(&destruct_count); - q.pop_front(); - } - EXPECT_EQ(4, destruct_count); // One for each cycle. - destruct_count = 0; - } - EXPECT_EQ(5, destruct_count); // One call for each. -} - -TEST(CircularDeque, EqualsCopy) { - circular_deque first = {1, 2, 3, 4, 5, 6}; - circular_deque copy; - EXPECT_TRUE(copy.empty()); - copy = first; - EXPECT_EQ(6u, copy.size()); - for (int i = 0; i < 6; i++) { - EXPECT_EQ(i + 1, first[i]); - EXPECT_EQ(i + 1, copy[i]); - EXPECT_NE(&first[i], ©[i]); - } -} - -TEST(CircularDeque, EqualsMove) { - circular_deque first = {1, 2, 3, 4, 5, 6}; - circular_deque move; - EXPECT_TRUE(move.empty()); - move = std::move(first); - EXPECT_TRUE(first.empty()); - EXPECT_EQ(6u, move.size()); - for (int i = 0; i < 6; i++) - EXPECT_EQ(i + 1, move[i]); -} - -// Tests that self-assignment is a no-op. -TEST(CircularDeque, EqualsSelf) { - circular_deque q = {1, 2, 3, 4, 5, 6}; - q = *&q; // The *& defeats Clang's -Wself-assign warning. - EXPECT_EQ(6u, q.size()); - for (int i = 0; i < 6; i++) - EXPECT_EQ(i + 1, q[i]); -} - -TEST(CircularDeque, EqualsInitializerList) { - circular_deque q; - EXPECT_TRUE(q.empty()); - q = {1, 2, 3, 4, 5, 6}; - EXPECT_EQ(6u, q.size()); - for (int i = 0; i < 6; i++) - EXPECT_EQ(i + 1, q[i]); -} - -TEST(CircularDeque, AssignCountValue) { - circular_deque empty; - empty.assign(0, 52); - EXPECT_EQ(0u, empty.size()); - - circular_deque full; - size_t count = 13; - int value = 12345; - full.assign(count, value); - EXPECT_EQ(count, full.size()); - - for (size_t i = 0; i < count; i++) - EXPECT_EQ(value, full[i]); -} - -TEST(CircularDeque, AssignIterator) { - int range[8] = {11, 12, 13, 14, 15, 16, 17, 18}; - - circular_deque empty; - empty.assign(std::begin(range), std::begin(range)); - EXPECT_TRUE(empty.empty()); - - circular_deque full; - full.assign(std::begin(range), std::end(range)); - EXPECT_EQ(8u, full.size()); - for (size_t i = 0; i < 8; i++) - EXPECT_EQ(range[i], full[i]); -} - -TEST(CircularDeque, AssignInitializerList) { - circular_deque empty; - empty.assign({}); - EXPECT_TRUE(empty.empty()); - - circular_deque full; - full.assign({11, 12, 13, 14, 15, 16, 17, 18}); - EXPECT_EQ(8u, full.size()); - for (int i = 0; i < 8; i++) - EXPECT_EQ(11 + i, full[i]); -} - -// Tests [] and .at(). -TEST(CircularDeque, At) { - circular_deque q = MakeSequence(10); - CycleTest(q, [](const circular_deque& q, size_t cycle) { - size_t expected_size = 10; - EXPECT_EQ(expected_size, q.size()); - - // A sequence of 0's. - size_t index = 0; - size_t num_zeros = std::min(expected_size, cycle); - for (size_t i = 0; i < num_zeros; i++, index++) { - EXPECT_EQ(0, q[index]); - EXPECT_EQ(0, q.at(index)); - } - - // Followed by a sequence of increasing ints. - size_t num_ints = expected_size - num_zeros; - for (int i = 0; i < static_cast(num_ints); i++, index++) { - EXPECT_EQ(i, q[index]); - EXPECT_EQ(i, q.at(index)); - } - }); -} - -// This also tests the copy constructor with lots of different types of -// input configurations. -TEST(CircularDeque, FrontBackPushPop) { - circular_deque q = MakeSequence(10); - - int expected_front = 0; - int expected_back = 9; - - // Go in one direction. - for (int i = 0; i < 100; i++) { - const circular_deque const_q(q); - - EXPECT_EQ(expected_front, q.front()); - EXPECT_EQ(expected_back, q.back()); - EXPECT_EQ(expected_front, const_q.front()); - EXPECT_EQ(expected_back, const_q.back()); - - expected_front++; - expected_back++; - - q.pop_front(); - q.push_back(expected_back); - } - - // Go back in reverse. - for (int i = 0; i < 100; i++) { - const circular_deque const_q(q); - - EXPECT_EQ(expected_front, q.front()); - EXPECT_EQ(expected_back, q.back()); - EXPECT_EQ(expected_front, const_q.front()); - EXPECT_EQ(expected_back, const_q.back()); - - expected_front--; - expected_back--; - - q.pop_back(); - q.push_front(expected_front); - } -} - -TEST(CircularDeque, ReallocateWithSplitBuffer) { - // Tests reallocating a deque with an internal buffer that looks like this: - // 4 5 x x 0 1 2 3 - // end-^ ^-begin - circular_deque q; - q.reserve(7); // Internal buffer is always 1 larger than requested. - q.push_back(-1); - q.push_back(-1); - q.push_back(-1); - q.push_back(-1); - q.push_back(0); - q.pop_front(); - q.pop_front(); - q.pop_front(); - q.pop_front(); - q.push_back(1); - q.push_back(2); - q.push_back(3); - q.push_back(4); - q.push_back(5); - - q.shrink_to_fit(); - EXPECT_EQ(6u, q.size()); - - EXPECT_EQ(0, q[0]); - EXPECT_EQ(1, q[1]); - EXPECT_EQ(2, q[2]); - EXPECT_EQ(3, q[3]); - EXPECT_EQ(4, q[4]); - EXPECT_EQ(5, q[5]); -} - -TEST(CircularDeque, Swap) { - circular_deque a = MakeSequence(10); - circular_deque b = MakeSequence(100); - - a.swap(b); - EXPECT_EQ(100u, a.size()); - for (int i = 0; i < 100; i++) - EXPECT_EQ(i, a[i]); - - EXPECT_EQ(10u, b.size()); - for (int i = 0; i < 10; i++) - EXPECT_EQ(i, b[i]); -} - -TEST(CircularDeque, Iteration) { - circular_deque q = MakeSequence(10); - - int expected_front = 0; - int expected_back = 9; - - // This loop causes various combinations of begin and end to be tested. - for (int i = 0; i < 30; i++) { - // Range-based for loop going forward. - int current_expected = expected_front; - for (int cur : q) { - EXPECT_EQ(current_expected, cur); - current_expected++; - } - - // Manually test reverse iterators. - current_expected = expected_back; - for (auto cur = q.crbegin(); cur < q.crend(); cur++) { - EXPECT_EQ(current_expected, *cur); - current_expected--; - } - - expected_front++; - expected_back++; - - q.pop_front(); - q.push_back(expected_back); - } - - // Go back in reverse. - for (int i = 0; i < 100; i++) { - const circular_deque const_q(q); - - EXPECT_EQ(expected_front, q.front()); - EXPECT_EQ(expected_back, q.back()); - EXPECT_EQ(expected_front, const_q.front()); - EXPECT_EQ(expected_back, const_q.back()); - - expected_front--; - expected_back--; - - q.pop_back(); - q.push_front(expected_front); - } -} - -TEST(CircularDeque, IteratorComparisons) { - circular_deque q = MakeSequence(10); - - // This loop causes various combinations of begin and end to be tested. - for (int i = 0; i < 30; i++) { - EXPECT_LT(q.begin(), q.end()); - EXPECT_LE(q.begin(), q.end()); - EXPECT_LE(q.begin(), q.begin()); - - EXPECT_GT(q.end(), q.begin()); - EXPECT_GE(q.end(), q.begin()); - EXPECT_GE(q.end(), q.end()); - - EXPECT_EQ(q.begin(), q.begin()); - EXPECT_NE(q.begin(), q.end()); - - q.push_front(10); - q.pop_back(); - } -} - -TEST(CircularDeque, IteratorIncDec) { - circular_deque q; - - // No-op offset computations with no capacity. - EXPECT_EQ(q.end(), q.end() + 0); - EXPECT_EQ(q.end(), q.end() - 0); - - q = MakeSequence(10); - - // Mutable preincrement, predecrement. - { - circular_deque::iterator it = q.begin(); - circular_deque::iterator op_result = ++it; - EXPECT_EQ(1, *op_result); - EXPECT_EQ(1, *it); - - op_result = --it; - EXPECT_EQ(0, *op_result); - EXPECT_EQ(0, *it); - } - - // Const preincrement, predecrement. - { - circular_deque::const_iterator it = q.begin(); - circular_deque::const_iterator op_result = ++it; - EXPECT_EQ(1, *op_result); - EXPECT_EQ(1, *it); - - op_result = --it; - EXPECT_EQ(0, *op_result); - EXPECT_EQ(0, *it); - } - - // Mutable postincrement, postdecrement. - { - circular_deque::iterator it = q.begin(); - circular_deque::iterator op_result = it++; - EXPECT_EQ(0, *op_result); - EXPECT_EQ(1, *it); - - op_result = it--; - EXPECT_EQ(1, *op_result); - EXPECT_EQ(0, *it); - } - - // Const postincrement, postdecrement. - { - circular_deque::const_iterator it = q.begin(); - circular_deque::const_iterator op_result = it++; - EXPECT_EQ(0, *op_result); - EXPECT_EQ(1, *it); - - op_result = it--; - EXPECT_EQ(1, *op_result); - EXPECT_EQ(0, *it); - } -} - -TEST(CircularDeque, IteratorIntegerOps) { - circular_deque q = MakeSequence(10); - - int expected_front = 0; - int expected_back = 9; - - for (int i = 0; i < 30; i++) { - EXPECT_EQ(0, q.begin() - q.begin()); - EXPECT_EQ(0, q.end() - q.end()); - EXPECT_EQ(q.size(), static_cast(q.end() - q.begin())); - - // += - circular_deque::iterator eight = q.begin(); - eight += 8; - EXPECT_EQ(8, eight - q.begin()); - EXPECT_EQ(expected_front + 8, *eight); - - // -= - eight -= 8; - EXPECT_EQ(q.begin(), eight); - - // + - eight = eight + 8; - EXPECT_EQ(8, eight - q.begin()); - - // - - eight = eight - 8; - EXPECT_EQ(q.begin(), eight); - - expected_front++; - expected_back++; - - q.pop_front(); - q.push_back(expected_back); - } -} - -TEST(CircularDeque, IteratorArrayAccess) { - circular_deque q = MakeSequence(10); - - circular_deque::iterator begin = q.begin(); - EXPECT_EQ(0, begin[0]); - EXPECT_EQ(9, begin[9]); - - circular_deque::iterator end = q.end(); - EXPECT_EQ(0, end[-10]); - EXPECT_EQ(9, end[-1]); - - begin[0] = 100; - EXPECT_EQ(100, end[-10]); -} - -TEST(CircularDeque, ReverseIterator) { - circular_deque q; - q.push_back(4); - q.push_back(3); - q.push_back(2); - q.push_back(1); - - circular_deque::reverse_iterator iter = q.rbegin(); - EXPECT_EQ(1, *iter); - iter++; - EXPECT_EQ(2, *iter); - ++iter; - EXPECT_EQ(3, *iter); - iter++; - EXPECT_EQ(4, *iter); - ++iter; - EXPECT_EQ(q.rend(), iter); -} - -TEST(CircularDeque, CapacityReserveShrink) { - circular_deque q; - - // A default constructed queue should have no capacity since it should waste - // no space. - EXPECT_TRUE(q.empty()); - EXPECT_EQ(0u, q.size()); - EXPECT_EQ(0u, q.capacity()); - - size_t new_capacity = 100; - q.reserve(new_capacity); - EXPECT_EQ(new_capacity, q.capacity()); - - // Adding that many items should not cause a resize. - for (size_t i = 0; i < new_capacity; i++) - q.push_back(i); - EXPECT_EQ(new_capacity, q.size()); - EXPECT_EQ(new_capacity, q.capacity()); - - // Shrink to fit to a smaller size. - size_t capacity_2 = new_capacity / 2; - q.resize(capacity_2); - q.shrink_to_fit(); - EXPECT_EQ(capacity_2, q.size()); - EXPECT_EQ(capacity_2, q.capacity()); -} - -TEST(CircularDeque, CapacityAutoShrink) { - size_t big_size = 1000u; - circular_deque q; - q.resize(big_size); - - size_t big_capacity = q.capacity(); - - // Delete 3/4 of the items. - for (size_t i = 0; i < big_size / 4 * 3; i++) - q.pop_back(); - - // The capacity should have shrunk by deleting that many items. - size_t medium_capacity = q.capacity(); - EXPECT_GT(big_capacity, medium_capacity); - - // Using resize to shrink should keep some extra capacity. - q.resize(1); - EXPECT_LT(1u, q.capacity()); - - q.resize(0); - EXPECT_LT(0u, q.capacity()); - - // Using clear() should delete everything. - q.clear(); - EXPECT_EQ(0u, q.capacity()); -} - -TEST(CircularDeque, ClearAndEmpty) { - circular_deque q; - EXPECT_TRUE(q.empty()); - - q.resize(10); - EXPECT_EQ(10u, q.size()); - EXPECT_FALSE(q.empty()); - - q.clear(); - EXPECT_EQ(0u, q.size()); - EXPECT_TRUE(q.empty()); - - // clear() also should reset the capacity. - EXPECT_EQ(0u, q.capacity()); -} - -TEST(CircularDeque, Resize) { - circular_deque q; - - // Resize with default constructor. - size_t first_size = 10; - q.resize(first_size); - EXPECT_EQ(first_size, q.size()); - for (size_t i = 0; i < first_size; i++) - EXPECT_EQ(0, q[i]); - - // Resize with different value. - size_t second_expand = 10; - q.resize(first_size + second_expand, 3); - EXPECT_EQ(first_size + second_expand, q.size()); - for (size_t i = 0; i < first_size; i++) - EXPECT_EQ(0, q[i]); - for (size_t i = 0; i < second_expand; i++) - EXPECT_EQ(3, q[i + first_size]); - - // Erase from the end and add to the beginning so resize is forced to cross - // a circular buffer wrap boundary. - q.shrink_to_fit(); - for (int i = 0; i < 5; i++) { - q.pop_back(); - q.push_front(6); - } - q.resize(10); - - EXPECT_EQ(6, q[0]); - EXPECT_EQ(6, q[1]); - EXPECT_EQ(6, q[2]); - EXPECT_EQ(6, q[3]); - EXPECT_EQ(6, q[4]); - EXPECT_EQ(0, q[5]); - EXPECT_EQ(0, q[6]); - EXPECT_EQ(0, q[7]); - EXPECT_EQ(0, q[8]); - EXPECT_EQ(0, q[9]); -} - -// Tests destructor behavior of resize. -TEST(CircularDeque, ResizeDelete) { - int counter = 0; - circular_deque q; - q.resize(10, DestructorCounter(&counter)); - // The one temporary when calling resize() should be deleted, that's it. - EXPECT_EQ(1, counter); - - // The loops below assume the capacity will be set by resize(). - EXPECT_EQ(10u, q.capacity()); - - // Delete some via resize(). This is done so that the wasted items are - // still greater than the size() so that auto-shrinking is not triggered - // (which will mess up our destructor counting). - counter = 0; - q.resize(8, DestructorCounter(&counter)); - // 2 deleted ones + the one temporary in the resize() call. - EXPECT_EQ(3, counter); - - // Cycle through some items so two items will cross the boundary in the - // 11-item buffer (one more than the capacity). - // Before: x x x x x x x x . . . - // After: x . . . x x x x x x x - counter = 0; - for (int i = 0; i < 4; i++) { - q.emplace_back(&counter); - q.pop_front(); - } - EXPECT_EQ(4, counter); // Loop should have deleted 7 items. - - // Delete two items with resize, these should be on either side of the - // buffer wrap point. - counter = 0; - q.resize(6, DestructorCounter(&counter)); - // 2 deleted ones + the one temporary in the resize() call. - EXPECT_EQ(3, counter); -} - -TEST(CircularDeque, InsertEraseSingle) { - circular_deque q; - q.push_back(1); - q.push_back(2); - - // Insert at the beginning. - auto result = q.insert(q.begin(), 0); - EXPECT_EQ(q.begin(), result); - EXPECT_EQ(3u, q.size()); - EXPECT_EQ(0, q[0]); - EXPECT_EQ(1, q[1]); - EXPECT_EQ(2, q[2]); - - // Erase at the beginning. - result = q.erase(q.begin()); - EXPECT_EQ(q.begin(), result); - EXPECT_EQ(2u, q.size()); - EXPECT_EQ(1, q[0]); - EXPECT_EQ(2, q[1]); - - // Insert at the end. - result = q.insert(q.end(), 3); - EXPECT_EQ(q.end() - 1, result); - EXPECT_EQ(1, q[0]); - EXPECT_EQ(2, q[1]); - EXPECT_EQ(3, q[2]); - - // Erase at the end. - result = q.erase(q.end() - 1); - EXPECT_EQ(q.end(), result); - EXPECT_EQ(1, q[0]); - EXPECT_EQ(2, q[1]); - - // Insert in the middle. - result = q.insert(q.begin() + 1, 10); - EXPECT_EQ(q.begin() + 1, result); - EXPECT_EQ(1, q[0]); - EXPECT_EQ(10, q[1]); - EXPECT_EQ(2, q[2]); - - // Erase in the middle. - result = q.erase(q.begin() + 1); - EXPECT_EQ(q.begin() + 1, result); - EXPECT_EQ(1, q[0]); - EXPECT_EQ(2, q[1]); -} - -TEST(CircularDeque, InsertFill) { - circular_deque q; - - // Fill when empty. - q.insert(q.begin(), 2, 1); - - // 0's at the beginning. - q.insert(q.begin(), 2, 0); - - // 50's in the middle (now at offset 3). - q.insert(q.begin() + 3, 2, 50); - - // 200's at the end. - q.insert(q.end(), 2, 200); - - ASSERT_EQ(8u, q.size()); - EXPECT_EQ(0, q[0]); - EXPECT_EQ(0, q[1]); - EXPECT_EQ(1, q[2]); - EXPECT_EQ(50, q[3]); - EXPECT_EQ(50, q[4]); - EXPECT_EQ(1, q[5]); - EXPECT_EQ(200, q[6]); - EXPECT_EQ(200, q[7]); -} - -TEST(CircularDeque, InsertEraseRange) { - circular_deque q; - - // Erase nothing from an empty deque should work. - q.erase(q.begin(), q.end()); - - // Loop index used below to shift the used items in the buffer. - for (int i = 0; i < 10; i++) { - circular_deque source; - - // Fill empty range. - q.insert(q.begin(), source.begin(), source.end()); - - // Have some stuff to insert. - source.push_back(1); - source.push_back(2); - - q.insert(q.begin(), source.begin(), source.end()); - - // Shift the used items in the buffer by i which will place the two used - // elements in different places in the buffer each time through this loop. - for (int shift_i = 0; shift_i < i; shift_i++) { - q.push_back(0); - q.pop_front(); - } - - // Set the two items to notable values so we can check for them later. - ASSERT_EQ(2u, q.size()); - q[0] = 100; - q[1] = 101; - - // Insert at the beginning, middle (now at offset 3), and end. - q.insert(q.begin(), source.begin(), source.end()); - q.insert(q.begin() + 3, source.begin(), source.end()); - q.insert(q.end(), source.begin(), source.end()); - - ASSERT_EQ(8u, q.size()); - EXPECT_EQ(1, q[0]); - EXPECT_EQ(2, q[1]); - EXPECT_EQ(100, q[2]); // First inserted one. - EXPECT_EQ(1, q[3]); - EXPECT_EQ(2, q[4]); - EXPECT_EQ(101, q[5]); // First inserted second one. - EXPECT_EQ(1, q[6]); - EXPECT_EQ(2, q[7]); - - // Now erase the inserted ranges. Try each subsection also with no items - // being erased, which should be a no-op. - auto result = q.erase(q.begin(), q.begin()); // No-op. - EXPECT_EQ(q.begin(), result); - result = q.erase(q.begin(), q.begin() + 2); - EXPECT_EQ(q.begin(), result); - - result = q.erase(q.begin() + 1, q.begin() + 1); // No-op. - EXPECT_EQ(q.begin() + 1, result); - result = q.erase(q.begin() + 1, q.begin() + 3); - EXPECT_EQ(q.begin() + 1, result); - - result = q.erase(q.end() - 2, q.end() - 2); // No-op. - EXPECT_EQ(q.end() - 2, result); - result = q.erase(q.end() - 2, q.end()); - EXPECT_EQ(q.end(), result); - - ASSERT_EQ(2u, q.size()); - EXPECT_EQ(100, q[0]); - EXPECT_EQ(101, q[1]); - - // Erase everything. - result = q.erase(q.begin(), q.end()); - EXPECT_EQ(q.end(), result); - EXPECT_TRUE(q.empty()); - } -} - -TEST(CircularDeque, EmplaceMoveOnly) { - int values[] = {1, 3}; - circular_deque q(std::begin(values), std::end(values)); - - q.emplace(q.begin(), MoveOnlyInt(0)); - q.emplace(q.begin() + 2, MoveOnlyInt(2)); - q.emplace(q.end(), MoveOnlyInt(4)); - - ASSERT_EQ(5u, q.size()); - EXPECT_EQ(0, q[0].data()); - EXPECT_EQ(1, q[1].data()); - EXPECT_EQ(2, q[2].data()); - EXPECT_EQ(3, q[3].data()); - EXPECT_EQ(4, q[4].data()); -} - -TEST(CircularDeque, EmplaceFrontBackReturnsReference) { - circular_deque q; - q.reserve(2); - - int& front = q.emplace_front(1); - int& back = q.emplace_back(2); - ASSERT_EQ(2u, q.size()); - EXPECT_EQ(1, q[0]); - EXPECT_EQ(2, q[1]); - - EXPECT_EQ(&front, &q.front()); - EXPECT_EQ(&back, &q.back()); - - front = 3; - back = 4; - - ASSERT_EQ(2u, q.size()); - EXPECT_EQ(3, q[0]); - EXPECT_EQ(4, q[1]); - - EXPECT_EQ(&front, &q.front()); - EXPECT_EQ(&back, &q.back()); -} - -/* -This test should assert in a debug build. It tries to dereference an iterator -after mutating the container. Uncomment to double-check that this works. -TEST(CircularDeque, UseIteratorAfterMutate) { - circular_deque q; - q.push_back(0); - - auto old_begin = q.begin(); - EXPECT_EQ(0, *old_begin); - - q.push_back(1); - EXPECT_EQ(0, *old_begin); // Should DCHECK. -} -*/ - -} // namespace base diff --git a/containers/flat_map.h b/containers/flat_map.h deleted file mode 100644 index b4fe5196e..000000000 --- a/containers/flat_map.h +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_FLAT_MAP_H_ -#define BASE_CONTAINERS_FLAT_MAP_H_ - -#include -#include -#include - -#include "base/containers/flat_tree.h" -#include "base/logging.h" -#include "base/template_util.h" - -namespace base { - -namespace internal { - -// An implementation of the flat_tree GetKeyFromValue template parameter that -// extracts the key as the first element of a pair. -template -struct GetKeyFromValuePairFirst { - const Key& operator()(const std::pair& p) const { - return p.first; - } -}; - -} // namespace internal - -// flat_map is a container with a std::map-like interface that stores its -// contents in a sorted vector. -// -// Please see //base/containers/README.md for an overview of which container -// to select. -// -// PROS -// -// - Good memory locality. -// - Low overhead, especially for smaller maps. -// - Performance is good for more workloads than you might expect (see -// overview link above). -// - Supports C++14 map interface. -// -// CONS -// -// - Inserts and removals are O(n). -// -// IMPORTANT NOTES -// -// - Iterators are invalidated across mutations. -// - If possible, construct a flat_map in one operation by inserting into -// a std::vector and moving that vector into the flat_map constructor. -// -// QUICK REFERENCE -// -// Most of the core functionality is inherited from flat_tree. Please see -// flat_tree.h for more details for most of these functions. As a quick -// reference, the functions available are: -// -// Constructors (inputs need not be sorted): -// flat_map(InputIterator first, InputIterator last, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, -// const Compare& compare = Compare()); -// flat_map(const flat_map&); -// flat_map(flat_map&&); -// flat_map(std::vector, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, -// const Compare& compare = Compare()); // Re-use storage. -// flat_map(std::initializer_list ilist, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, -// const Compare& comp = Compare()); -// -// Assignment functions: -// flat_map& operator=(const flat_map&); -// flat_map& operator=(flat_map&&); -// flat_map& operator=(initializer_list); -// -// Memory management functions: -// void reserve(size_t); -// size_t capacity() const; -// void shrink_to_fit(); -// -// Size management functions: -// void clear(); -// size_t size() const; -// size_t max_size() const; -// bool empty() const; -// -// Iterator functions: -// iterator begin(); -// const_iterator begin() const; -// const_iterator cbegin() const; -// iterator end(); -// const_iterator end() const; -// const_iterator cend() const; -// reverse_iterator rbegin(); -// const reverse_iterator rbegin() const; -// const_reverse_iterator crbegin() const; -// reverse_iterator rend(); -// const_reverse_iterator rend() const; -// const_reverse_iterator crend() const; -// -// Insert and accessor functions: -// mapped_type& operator[](const key_type&); -// mapped_type& operator[](key_type&&); -// pair insert(const value_type&); -// pair insert(value_type&&); -// iterator insert(const_iterator hint, const value_type&); -// iterator insert(const_iterator hint, value_type&&); -// void insert(InputIterator first, InputIterator last, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES); -// pair insert_or_assign(K&&, M&&); -// iterator insert_or_assign(const_iterator hint, K&&, M&&); -// pair emplace(Args&&...); -// iterator emplace_hint(const_iterator, Args&&...); -// pair try_emplace(K&&, Args&&...); -// iterator try_emplace(const_iterator hint, K&&, Args&&...); -// -// Erase functions: -// iterator erase(iterator); -// iterator erase(const_iterator); -// iterator erase(const_iterator first, const_iterator& last); -// template size_t erase(const K& key); -// -// Comparators (see std::map documentation). -// key_compare key_comp() const; -// value_compare value_comp() const; -// -// Search functions: -// template size_t count(const K&) const; -// template iterator find(const K&); -// template const_iterator find(const K&) const; -// template pair equal_range(const K&); -// template iterator lower_bound(const K&); -// template const_iterator lower_bound(const K&) const; -// template iterator upper_bound(const K&); -// template const_iterator upper_bound(const K&) const; -// -// General functions: -// void swap(flat_map&&); -// -// Non-member operators: -// bool operator==(const flat_map&, const flat_map); -// bool operator!=(const flat_map&, const flat_map); -// bool operator<(const flat_map&, const flat_map); -// bool operator>(const flat_map&, const flat_map); -// bool operator>=(const flat_map&, const flat_map); -// bool operator<=(const flat_map&, const flat_map); -// -template > -class flat_map : public ::base::internal::flat_tree< - Key, - std::pair, - ::base::internal::GetKeyFromValuePairFirst, - Compare> { - private: - using tree = typename ::base::internal::flat_tree< - Key, - std::pair, - ::base::internal::GetKeyFromValuePairFirst, - Compare>; - - public: - using key_type = typename tree::key_type; - using mapped_type = Mapped; - using value_type = typename tree::value_type; - using iterator = typename tree::iterator; - using const_iterator = typename tree::const_iterator; - - // -------------------------------------------------------------------------- - // Lifetime and assignments. - // - // Note: we could do away with these constructors, destructor and assignment - // operator overloads by inheriting |tree|'s, but this breaks the GCC build - // due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84782 (see - // https://crbug.com/837221). - - flat_map() = default; - explicit flat_map(const Compare& comp); - - template - flat_map(InputIterator first, - InputIterator last, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, - const Compare& comp = Compare()); - - flat_map(const flat_map&) = default; - flat_map(flat_map&&) noexcept = default; - - flat_map(std::vector items, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, - const Compare& comp = Compare()); - - flat_map(std::initializer_list ilist, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, - const Compare& comp = Compare()); - - ~flat_map() = default; - - flat_map& operator=(const flat_map&) = default; - flat_map& operator=(flat_map&&) = default; - // Takes the first if there are duplicates in the initializer list. - flat_map& operator=(std::initializer_list ilist); - - // -------------------------------------------------------------------------- - // Map-specific insert operations. - // - // Normal insert() functions are inherited from flat_tree. - // - // Assume that every operation invalidates iterators and references. - // Insertion of one element can take O(size). - - mapped_type& operator[](const key_type& key); - mapped_type& operator[](key_type&& key); - - template - std::pair insert_or_assign(K&& key, M&& obj); - template - iterator insert_or_assign(const_iterator hint, K&& key, M&& obj); - - template - std::enable_if_t::value, - std::pair> - try_emplace(K&& key, Args&&... args); - - template - std::enable_if_t::value, iterator> - try_emplace(const_iterator hint, K&& key, Args&&... args); - - // -------------------------------------------------------------------------- - // General operations. - // - // Assume that swap invalidates iterators and references. - - void swap(flat_map& other) noexcept; - - friend void swap(flat_map& lhs, flat_map& rhs) noexcept { lhs.swap(rhs); } -}; - -// ---------------------------------------------------------------------------- -// Lifetime. - -template -flat_map::flat_map(const Compare& comp) : tree(comp) {} - -template -template -flat_map::flat_map(InputIterator first, - InputIterator last, - FlatContainerDupes dupe_handling, - const Compare& comp) - : tree(first, last, dupe_handling, comp) {} - -template -flat_map::flat_map(std::vector items, - FlatContainerDupes dupe_handling, - const Compare& comp) - : tree(std::move(items), dupe_handling, comp) {} - -template -flat_map::flat_map( - std::initializer_list ilist, - FlatContainerDupes dupe_handling, - const Compare& comp) - : flat_map(std::begin(ilist), std::end(ilist), dupe_handling, comp) {} - -// ---------------------------------------------------------------------------- -// Assignments. - -template -auto flat_map::operator=( - std::initializer_list ilist) -> flat_map& { - // When https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84782 gets fixed, we - // need to remember to inherit tree::operator= to prevent - // flat_map<...> x; - // x = {...}; - // from first creating a flat_map and then move assigning it. This most - // likely would be optimized away but still affects our debug builds. - tree::operator=(ilist); - return *this; -} - -// ---------------------------------------------------------------------------- -// Insert operations. - -template -auto flat_map::operator[](const key_type& key) - -> mapped_type& { - iterator found = tree::lower_bound(key); - if (found == tree::end() || tree::key_comp()(key, found->first)) - found = tree::unsafe_emplace(found, key, mapped_type()); - return found->second; -} - -template -auto flat_map::operator[](key_type&& key) - -> mapped_type& { - iterator found = tree::lower_bound(key); - if (found == tree::end() || tree::key_comp()(key, found->first)) - found = tree::unsafe_emplace(found, std::move(key), mapped_type()); - return found->second; -} - -template -template -auto flat_map::insert_or_assign(K&& key, M&& obj) - -> std::pair { - auto result = - tree::emplace_key_args(key, std::forward(key), std::forward(obj)); - if (!result.second) - result.first->second = std::forward(obj); - return result; -} - -template -template -auto flat_map::insert_or_assign(const_iterator hint, - K&& key, - M&& obj) -> iterator { - auto result = tree::emplace_hint_key_args(hint, key, std::forward(key), - std::forward(obj)); - if (!result.second) - result.first->second = std::forward(obj); - return result.first; -} - -template -template -auto flat_map::try_emplace(K&& key, Args&&... args) - -> std::enable_if_t::value, - std::pair> { - return tree::emplace_key_args( - key, std::piecewise_construct, - std::forward_as_tuple(std::forward(key)), - std::forward_as_tuple(std::forward(args)...)); -} - -template -template -auto flat_map::try_emplace(const_iterator hint, - K&& key, - Args&&... args) - -> std::enable_if_t::value, iterator> { - return tree::emplace_hint_key_args( - hint, key, std::piecewise_construct, - std::forward_as_tuple(std::forward(key)), - std::forward_as_tuple(std::forward(args)...)) - .first; -} - -// ---------------------------------------------------------------------------- -// General operations. - -template -void flat_map::swap(flat_map& other) noexcept { - tree::swap(other); -} - -} // namespace base - -#endif // BASE_CONTAINERS_FLAT_MAP_H_ diff --git a/containers/flat_map_unittest.cc b/containers/flat_map_unittest.cc deleted file mode 100644 index 87958bde4..000000000 --- a/containers/flat_map_unittest.cc +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/containers/flat_map.h" - -#include -#include - -#include "base/macros.h" -#include "base/test/move_only_int.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -// A flat_map is basically a interface to flat_tree. So several basic -// operations are tested to make sure things are set up properly, but the bulk -// of the tests are in flat_tree_unittests.cc. - -using ::testing::ElementsAre; - -namespace base { - -TEST(FlatMap, IncompleteType) { - struct A { - using Map = flat_map; - int data; - Map set_with_incomplete_type; - Map::iterator it; - Map::const_iterator cit; - - // We do not declare operator< because clang complains that it's unused. - }; - - A a; -} - -TEST(FlatMap, RangeConstructor) { - flat_map::value_type input_vals[] = { - {1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}}; - - flat_map first(std::begin(input_vals), std::end(input_vals)); - EXPECT_THAT(first, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 1), - std::make_pair(3, 1))); - - flat_map last(std::begin(input_vals), std::end(input_vals), - KEEP_LAST_OF_DUPES); - EXPECT_THAT(last, ElementsAre(std::make_pair(1, 3), std::make_pair(2, 3), - std::make_pair(3, 3))); -} - -TEST(FlatMap, MoveConstructor) { - using pair = std::pair; - - flat_map original; - original.insert(pair(MoveOnlyInt(1), MoveOnlyInt(1))); - original.insert(pair(MoveOnlyInt(2), MoveOnlyInt(2))); - original.insert(pair(MoveOnlyInt(3), MoveOnlyInt(3))); - original.insert(pair(MoveOnlyInt(4), MoveOnlyInt(4))); - - flat_map moved(std::move(original)); - - EXPECT_EQ(1U, moved.count(MoveOnlyInt(1))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(2))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(3))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(4))); -} - -TEST(FlatMap, VectorConstructor) { - using IntPair = std::pair; - using IntMap = flat_map; - { - std::vector vect{{1, 1}, {1, 2}, {2, 1}}; - IntMap map(std::move(vect)); - EXPECT_THAT(map, ElementsAre(IntPair(1, 1), IntPair(2, 1))); - } - { - std::vector vect{{1, 1}, {1, 2}, {2, 1}}; - IntMap map(std::move(vect), KEEP_LAST_OF_DUPES); - EXPECT_THAT(map, ElementsAre(IntPair(1, 2), IntPair(2, 1))); - } -} - -TEST(FlatMap, InitializerListConstructor) { - { - flat_map cont( - {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}}); - EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2), - std::make_pair(3, 3), std::make_pair(4, 4), - std::make_pair(5, 5), std::make_pair(8, 8), - std::make_pair(10, 10))); - } - { - flat_map cont( - {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}}, - KEEP_LAST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 2), std::make_pair(2, 2), - std::make_pair(3, 3), std::make_pair(4, 4), - std::make_pair(5, 5), std::make_pair(8, 8), - std::make_pair(10, 10))); - } -} - -TEST(FlatMap, InitializerListAssignment) { - flat_map cont; - cont = {{1, 1}, {2, 2}}; - EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2))); -} - -TEST(FlatMap, InsertFindSize) { - base::flat_map s; - s.insert(std::make_pair(1, 1)); - s.insert(std::make_pair(1, 1)); - s.insert(std::make_pair(2, 2)); - - EXPECT_EQ(2u, s.size()); - EXPECT_EQ(std::make_pair(1, 1), *s.find(1)); - EXPECT_EQ(std::make_pair(2, 2), *s.find(2)); - EXPECT_EQ(s.end(), s.find(7)); -} - -TEST(FlatMap, CopySwap) { - base::flat_map original; - original.insert({1, 1}); - original.insert({2, 2}); - EXPECT_THAT(original, - ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2))); - - base::flat_map copy(original); - EXPECT_THAT(copy, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2))); - - copy.erase(copy.begin()); - copy.insert({10, 10}); - EXPECT_THAT(copy, ElementsAre(std::make_pair(2, 2), std::make_pair(10, 10))); - - original.swap(copy); - EXPECT_THAT(original, - ElementsAre(std::make_pair(2, 2), std::make_pair(10, 10))); - EXPECT_THAT(copy, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2))); -} - -// operator[](const Key&) -TEST(FlatMap, SubscriptConstKey) { - base::flat_map m; - - // Default construct elements that don't exist yet. - int& s = m["a"]; - EXPECT_EQ(0, s); - EXPECT_EQ(1u, m.size()); - - // The returned mapped reference should refer into the map. - s = 22; - EXPECT_EQ(22, m["a"]); - - // Overwrite existing elements. - m["a"] = 44; - EXPECT_EQ(44, m["a"]); -} - -// operator[](Key&&) -TEST(FlatMap, SubscriptMoveOnlyKey) { - base::flat_map m; - - // Default construct elements that don't exist yet. - int& s = m[MoveOnlyInt(1)]; - EXPECT_EQ(0, s); - EXPECT_EQ(1u, m.size()); - - // The returned mapped reference should refer into the map. - s = 22; - EXPECT_EQ(22, m[MoveOnlyInt(1)]); - - // Overwrite existing elements. - m[MoveOnlyInt(1)] = 44; - EXPECT_EQ(44, m[MoveOnlyInt(1)]); -} - -// insert_or_assign(K&&, M&&) -TEST(FlatMap, InsertOrAssignMoveOnlyKey) { - base::flat_map m; - - // Initial insertion should return an iterator to the element and set the - // second pair member to |true|. The inserted key and value should be moved - // from. - MoveOnlyInt key(1); - MoveOnlyInt val(22); - auto result = m.insert_or_assign(std::move(key), std::move(val)); - EXPECT_EQ(1, result.first->first.data()); - EXPECT_EQ(22, result.first->second.data()); - EXPECT_TRUE(result.second); - EXPECT_EQ(1u, m.size()); - EXPECT_EQ(0, key.data()); // moved from - EXPECT_EQ(0, val.data()); // moved from - - // Second call with same key should result in an assignment, overwriting the - // old value. Assignment should be indicated by setting the second pair member - // to |false|. Only the inserted value should be moved from, the key should be - // left intact. - key = MoveOnlyInt(1); - val = MoveOnlyInt(44); - result = m.insert_or_assign(std::move(key), std::move(val)); - EXPECT_EQ(1, result.first->first.data()); - EXPECT_EQ(44, result.first->second.data()); - EXPECT_FALSE(result.second); - EXPECT_EQ(1u, m.size()); - EXPECT_EQ(1, key.data()); // not moved from - EXPECT_EQ(0, val.data()); // moved from - - // Check that random insertion results in sorted range. - base::flat_map map; - for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) { - map.insert_or_assign(MoveOnlyInt(i), i); - EXPECT_TRUE(std::is_sorted(map.begin(), map.end())); - } -} - -// insert_or_assign(const_iterator hint, K&&, M&&) -TEST(FlatMap, InsertOrAssignMoveOnlyKeyWithHint) { - base::flat_map m; - - // Initial insertion should return an iterator to the element. The inserted - // key and value should be moved from. - MoveOnlyInt key(1); - MoveOnlyInt val(22); - auto result = m.insert_or_assign(m.end(), std::move(key), std::move(val)); - EXPECT_EQ(1, result->first.data()); - EXPECT_EQ(22, result->second.data()); - EXPECT_EQ(1u, m.size()); - EXPECT_EQ(0, key.data()); // moved from - EXPECT_EQ(0, val.data()); // moved from - - // Second call with same key should result in an assignment, overwriting the - // old value. Only the inserted value should be moved from, the key should be - // left intact. - key = MoveOnlyInt(1); - val = MoveOnlyInt(44); - result = m.insert_or_assign(m.end(), std::move(key), std::move(val)); - EXPECT_EQ(1, result->first.data()); - EXPECT_EQ(44, result->second.data()); - EXPECT_EQ(1u, m.size()); - EXPECT_EQ(1, key.data()); // not moved from - EXPECT_EQ(0, val.data()); // moved from - - // Check that random insertion results in sorted range. - base::flat_map map; - for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) { - map.insert_or_assign(map.end(), MoveOnlyInt(i), i); - EXPECT_TRUE(std::is_sorted(map.begin(), map.end())); - } -} - -// try_emplace(K&&, Args&&...) -TEST(FlatMap, TryEmplaceMoveOnlyKey) { - base::flat_map> m; - - // Trying to emplace into an empty map should succeed. Insertion should return - // an iterator to the element and set the second pair member to |true|. The - // inserted key and value should be moved from. - MoveOnlyInt key(1); - MoveOnlyInt val1(22); - MoveOnlyInt val2(44); - // Test piecewise construction of mapped_type. - auto result = m.try_emplace(std::move(key), std::move(val1), std::move(val2)); - EXPECT_EQ(1, result.first->first.data()); - EXPECT_EQ(22, result.first->second.first.data()); - EXPECT_EQ(44, result.first->second.second.data()); - EXPECT_TRUE(result.second); - EXPECT_EQ(1u, m.size()); - EXPECT_EQ(0, key.data()); // moved from - EXPECT_EQ(0, val1.data()); // moved from - EXPECT_EQ(0, val2.data()); // moved from - - // Second call with same key should result in a no-op, returning an iterator - // to the existing element and returning false as the second pair member. - // Key and values that were attempted to be inserted should be left intact. - key = MoveOnlyInt(1); - auto paired_val = std::make_pair(MoveOnlyInt(33), MoveOnlyInt(55)); - // Test construction of mapped_type from pair. - result = m.try_emplace(std::move(key), std::move(paired_val)); - EXPECT_EQ(1, result.first->first.data()); - EXPECT_EQ(22, result.first->second.first.data()); - EXPECT_EQ(44, result.first->second.second.data()); - EXPECT_FALSE(result.second); - EXPECT_EQ(1u, m.size()); - EXPECT_EQ(1, key.data()); // not moved from - EXPECT_EQ(33, paired_val.first.data()); // not moved from - EXPECT_EQ(55, paired_val.second.data()); // not moved from - - // Check that random insertion results in sorted range. - base::flat_map map; - for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) { - map.try_emplace(MoveOnlyInt(i), i); - EXPECT_TRUE(std::is_sorted(map.begin(), map.end())); - } -} - -// try_emplace(const_iterator hint, K&&, Args&&...) -TEST(FlatMap, TryEmplaceMoveOnlyKeyWithHint) { - base::flat_map> m; - - // Trying to emplace into an empty map should succeed. Insertion should return - // an iterator to the element. The inserted key and value should be moved - // from. - MoveOnlyInt key(1); - MoveOnlyInt val1(22); - MoveOnlyInt val2(44); - // Test piecewise construction of mapped_type. - auto result = - m.try_emplace(m.end(), std::move(key), std::move(val1), std::move(val2)); - EXPECT_EQ(1, result->first.data()); - EXPECT_EQ(22, result->second.first.data()); - EXPECT_EQ(44, result->second.second.data()); - EXPECT_EQ(1u, m.size()); - EXPECT_EQ(0, key.data()); // moved from - EXPECT_EQ(0, val1.data()); // moved from - EXPECT_EQ(0, val2.data()); // moved from - - // Second call with same key should result in a no-op, returning an iterator - // to the existing element. Key and values that were attempted to be inserted - // should be left intact. - key = MoveOnlyInt(1); - val1 = MoveOnlyInt(33); - val2 = MoveOnlyInt(55); - auto paired_val = std::make_pair(MoveOnlyInt(33), MoveOnlyInt(55)); - // Test construction of mapped_type from pair. - result = m.try_emplace(m.end(), std::move(key), std::move(paired_val)); - EXPECT_EQ(1, result->first.data()); - EXPECT_EQ(22, result->second.first.data()); - EXPECT_EQ(44, result->second.second.data()); - EXPECT_EQ(1u, m.size()); - EXPECT_EQ(1, key.data()); // not moved from - EXPECT_EQ(33, paired_val.first.data()); // not moved from - EXPECT_EQ(55, paired_val.second.data()); // not moved from - - // Check that random insertion results in sorted range. - base::flat_map map; - for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) { - map.try_emplace(map.end(), MoveOnlyInt(i), i); - EXPECT_TRUE(std::is_sorted(map.begin(), map.end())); - } -} - -TEST(FlatMap, UsingTransparentCompare) { - using ExplicitInt = base::MoveOnlyInt; - base::flat_map m; - const auto& m1 = m; - int x = 0; - - // Check if we can use lookup functions without converting to key_type. - // Correctness is checked in flat_tree tests. - m.count(x); - m1.count(x); - m.find(x); - m1.find(x); - m.equal_range(x); - m1.equal_range(x); - m.lower_bound(x); - m1.lower_bound(x); - m.upper_bound(x); - m1.upper_bound(x); - m.erase(x); - - // Check if we broke overload resolution. - m.emplace(ExplicitInt(0), 0); - m.emplace(ExplicitInt(1), 0); - m.erase(m.begin()); - m.erase(m.cbegin()); -} - -} // namespace base diff --git a/containers/flat_set.h b/containers/flat_set.h deleted file mode 100644 index bf14c36fd..000000000 --- a/containers/flat_set.h +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_FLAT_SET_H_ -#define BASE_CONTAINERS_FLAT_SET_H_ - -#include - -#include "base/containers/flat_tree.h" -#include "base/template_util.h" - -namespace base { - -// flat_set is a container with a std::set-like interface that stores its -// contents in a sorted vector. -// -// Please see //base/containers/README.md for an overview of which container -// to select. -// -// PROS -// -// - Good memory locality. -// - Low overhead, especially for smaller sets. -// - Performance is good for more workloads than you might expect (see -// overview link above). -// - Supports C++14 set interface. -// -// CONS -// -// - Inserts and removals are O(n). -// -// IMPORTANT NOTES -// -// - Iterators are invalidated across mutations. -// - If possible, construct a flat_set in one operation by inserting into -// a std::vector and moving that vector into the flat_set constructor. -// - For multiple removals use base::EraseIf() which is O(n) rather than -// O(n * removed_items). -// -// QUICK REFERENCE -// -// Most of the core functionality is inherited from flat_tree. Please see -// flat_tree.h for more details for most of these functions. As a quick -// reference, the functions available are: -// -// Constructors (inputs need not be sorted): -// flat_set(InputIterator first, InputIterator last, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, -// const Compare& compare = Compare()); -// flat_set(const flat_set&); -// flat_set(flat_set&&); -// flat_set(std::vector, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, -// const Compare& compare = Compare()); // Re-use storage. -// flat_set(std::initializer_list ilist, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES, -// const Compare& comp = Compare()); -// -// Assignment functions: -// flat_set& operator=(const flat_set&); -// flat_set& operator=(flat_set&&); -// flat_set& operator=(initializer_list); -// -// Memory management functions: -// void reserve(size_t); -// size_t capacity() const; -// void shrink_to_fit(); -// -// Size management functions: -// void clear(); -// size_t size() const; -// size_t max_size() const; -// bool empty() const; -// -// Iterator functions: -// iterator begin(); -// const_iterator begin() const; -// const_iterator cbegin() const; -// iterator end(); -// const_iterator end() const; -// const_iterator cend() const; -// reverse_iterator rbegin(); -// const reverse_iterator rbegin() const; -// const_reverse_iterator crbegin() const; -// reverse_iterator rend(); -// const_reverse_iterator rend() const; -// const_reverse_iterator crend() const; -// -// Insert and accessor functions: -// pair insert(const key_type&); -// pair insert(key_type&&); -// void insert(InputIterator first, InputIterator last, -// FlatContainerDupes = KEEP_FIRST_OF_DUPES); -// iterator insert(const_iterator hint, const key_type&); -// iterator insert(const_iterator hint, key_type&&); -// pair emplace(Args&&...); -// iterator emplace_hint(const_iterator, Args&&...); -// -// Erase functions: -// iterator erase(iterator); -// iterator erase(const_iterator); -// iterator erase(const_iterator first, const_iterator& last); -// template size_t erase(const K& key); -// -// Comparators (see std::set documentation). -// key_compare key_comp() const; -// value_compare value_comp() const; -// -// Search functions: -// template size_t count(const K&) const; -// template iterator find(const K&); -// template const_iterator find(const K&) const; -// template pair equal_range(K&); -// template iterator lower_bound(const K&); -// template const_iterator lower_bound(const K&) const; -// template iterator upper_bound(const K&); -// template const_iterator upper_bound(const K&) const; -// -// General functions: -// void swap(flat_set&&); -// -// Non-member operators: -// bool operator==(const flat_set&, const flat_set); -// bool operator!=(const flat_set&, const flat_set); -// bool operator<(const flat_set&, const flat_set); -// bool operator>(const flat_set&, const flat_set); -// bool operator>=(const flat_set&, const flat_set); -// bool operator<=(const flat_set&, const flat_set); -// -template > -using flat_set = typename ::base::internal::flat_tree< - Key, - Key, - ::base::internal::GetKeyFromValueIdentity, - Compare>; - -} // namespace base - -#endif // BASE_CONTAINERS_FLAT_SET_H_ diff --git a/containers/flat_set_unittest.cc b/containers/flat_set_unittest.cc deleted file mode 100644 index 459697549..000000000 --- a/containers/flat_set_unittest.cc +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/containers/flat_set.h" - -#include -#include - -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/test/move_only_int.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -// A flat_set is basically a interface to flat_tree. So several basic -// operations are tested to make sure things are set up properly, but the bulk -// of the tests are in flat_tree_unittests.cc. - -using ::testing::ElementsAre; - -namespace base { - -TEST(FlatSet, IncompleteType) { - struct A { - using Set = flat_set; - int data; - Set set_with_incomplete_type; - Set::iterator it; - Set::const_iterator cit; - - // We do not declare operator< because clang complains that it's unused. - }; - - A a; -} - -TEST(FlatSet, RangeConstructor) { - flat_set::value_type input_vals[] = {1, 1, 1, 2, 2, 2, 3, 3, 3}; - - flat_set cont(std::begin(input_vals), std::end(input_vals), - base::KEEP_FIRST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(1, 2, 3)); -} - -TEST(FlatSet, MoveConstructor) { - int input_range[] = {1, 2, 3, 4}; - - flat_set original(std::begin(input_range), std::end(input_range), - base::KEEP_FIRST_OF_DUPES); - flat_set moved(std::move(original)); - - EXPECT_EQ(1U, moved.count(MoveOnlyInt(1))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(2))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(3))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(4))); -} - -TEST(FlatSet, InitializerListConstructor) { - flat_set cont({1, 2, 3, 4, 5, 6, 10, 8}, KEEP_FIRST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10)); -} - -TEST(FlatSet, InsertFindSize) { - base::flat_set s; - s.insert(1); - s.insert(1); - s.insert(2); - - EXPECT_EQ(2u, s.size()); - EXPECT_EQ(1, *s.find(1)); - EXPECT_EQ(2, *s.find(2)); - EXPECT_EQ(s.end(), s.find(7)); -} - -TEST(FlatSet, CopySwap) { - base::flat_set original; - original.insert(1); - original.insert(2); - EXPECT_THAT(original, ElementsAre(1, 2)); - - base::flat_set copy(original); - EXPECT_THAT(copy, ElementsAre(1, 2)); - - copy.erase(copy.begin()); - copy.insert(10); - EXPECT_THAT(copy, ElementsAre(2, 10)); - - original.swap(copy); - EXPECT_THAT(original, ElementsAre(2, 10)); - EXPECT_THAT(copy, ElementsAre(1, 2)); -} - -TEST(FlatSet, UsingTransparentCompare) { - using ExplicitInt = base::MoveOnlyInt; - base::flat_set s; - const auto& s1 = s; - int x = 0; - - // Check if we can use lookup functions without converting to key_type. - // Correctness is checked in flat_tree tests. - s.count(x); - s1.count(x); - s.find(x); - s1.find(x); - s.equal_range(x); - s1.equal_range(x); - s.lower_bound(x); - s1.lower_bound(x); - s.upper_bound(x); - s1.upper_bound(x); - s.erase(x); - - // Check if we broke overload resolution. - s.emplace(0); - s.emplace(1); - s.erase(s.begin()); - s.erase(s.cbegin()); -} - -} // namespace base diff --git a/containers/flat_tree.h b/containers/flat_tree.h deleted file mode 100644 index 7856e2423..000000000 --- a/containers/flat_tree.h +++ /dev/null @@ -1,1004 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_FLAT_TREE_H_ -#define BASE_CONTAINERS_FLAT_TREE_H_ - -#include -#include -#include -#include - -#include "base/template_util.h" - -namespace base { - -enum FlatContainerDupes { - KEEP_FIRST_OF_DUPES, - KEEP_LAST_OF_DUPES, -}; - -namespace internal { - -// This is a convenience method returning true if Iterator is at least a -// ForwardIterator and thus supports multiple passes over a range. -template -constexpr bool is_multipass() { - return std::is_base_of< - std::forward_iterator_tag, - typename std::iterator_traits::iterator_category>::value; -} - -// This algorithm is like unique() from the standard library except it -// selects only the last of consecutive values instead of the first. -template -Iterator LastUnique(Iterator first, Iterator last, BinaryPredicate compare) { - Iterator replacable = std::adjacent_find(first, last, compare); - - // No duplicate elements found. - if (replacable == last) - return last; - - first = std::next(replacable); - - // Last element is a duplicate but all others are unique. - if (first == last) - return replacable; - - // This loop is based on std::adjacent_find but std::adjacent_find doesn't - // quite cut it. - for (Iterator next = std::next(first); next != last; ++next, ++first) { - if (!compare(*first, *next)) - *replacable++ = std::move(*first); - } - - // Last element should be copied unconditionally. - *replacable++ = std::move(*first); - return replacable; -} - -// Uses SFINAE to detect whether type has is_transparent member. -template -struct IsTransparentCompare : std::false_type {}; -template -struct IsTransparentCompare> - : std::true_type {}; - -// Implementation ------------------------------------------------------------- - -// Implementation of a sorted vector for backing flat_set and flat_map. Do not -// use directly. -// -// The use of "value" in this is like std::map uses, meaning it's the thing -// contained (in the case of map it's a pair). The Key is how -// things are looked up. In the case of a set, Key == Value. In the case of -// a map, the Key is a component of a Value. -// -// The helper class GetKeyFromValue provides the means to extract a key from a -// value for comparison purposes. It should implement: -// const Key& operator()(const Value&). -template -class flat_tree { - private: - using underlying_type = std::vector; - - public: - // -------------------------------------------------------------------------- - // Types. - // - using key_type = Key; - using key_compare = KeyCompare; - using value_type = Value; - - // Wraps the templated key comparison to compare values. - class value_compare : public key_compare { - public: - value_compare() = default; - - template - explicit value_compare(Cmp&& compare_arg) - : KeyCompare(std::forward(compare_arg)) {} - - bool operator()(const value_type& left, const value_type& right) const { - GetKeyFromValue extractor; - return key_compare::operator()(extractor(left), extractor(right)); - } - }; - - using pointer = typename underlying_type::pointer; - using const_pointer = typename underlying_type::const_pointer; - using reference = typename underlying_type::reference; - using const_reference = typename underlying_type::const_reference; - using size_type = typename underlying_type::size_type; - using difference_type = typename underlying_type::difference_type; - using iterator = typename underlying_type::iterator; - using const_iterator = typename underlying_type::const_iterator; - using reverse_iterator = typename underlying_type::reverse_iterator; - using const_reverse_iterator = - typename underlying_type::const_reverse_iterator; - - // -------------------------------------------------------------------------- - // Lifetime. - // - // Constructors that take range guarantee O(N * log^2(N)) + O(N) complexity - // and take O(N * log(N)) + O(N) if extra memory is available (N is a range - // length). - // - // Assume that move constructors invalidate iterators and references. - // - // The constructors that take ranges, lists, and vectors do not require that - // the input be sorted. - - flat_tree(); - explicit flat_tree(const key_compare& comp); - - template - flat_tree(InputIterator first, - InputIterator last, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, - const key_compare& comp = key_compare()); - - flat_tree(const flat_tree&); - flat_tree(flat_tree&&) noexcept = default; - - flat_tree(std::vector items, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, - const key_compare& comp = key_compare()); - - flat_tree(std::initializer_list ilist, - FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES, - const key_compare& comp = key_compare()); - - ~flat_tree(); - - // -------------------------------------------------------------------------- - // Assignments. - // - // Assume that move assignment invalidates iterators and references. - - flat_tree& operator=(const flat_tree&); - flat_tree& operator=(flat_tree&&); - // Takes the first if there are duplicates in the initializer list. - flat_tree& operator=(std::initializer_list ilist); - - // -------------------------------------------------------------------------- - // Memory management. - // - // Beware that shrink_to_fit() simply forwards the request to the - // underlying_type and its implementation is free to optimize otherwise and - // leave capacity() to be greater that its size. - // - // reserve() and shrink_to_fit() invalidate iterators and references. - - void reserve(size_type new_capacity); - size_type capacity() const; - void shrink_to_fit(); - - // -------------------------------------------------------------------------- - // Size management. - // - // clear() leaves the capacity() of the flat_tree unchanged. - - void clear(); - - size_type size() const; - size_type max_size() const; - bool empty() const; - - // -------------------------------------------------------------------------- - // Iterators. - - iterator begin(); - const_iterator begin() const; - const_iterator cbegin() const; - - iterator end(); - const_iterator end() const; - const_iterator cend() const; - - reverse_iterator rbegin(); - const_reverse_iterator rbegin() const; - const_reverse_iterator crbegin() const; - - reverse_iterator rend(); - const_reverse_iterator rend() const; - const_reverse_iterator crend() const; - - // -------------------------------------------------------------------------- - // Insert operations. - // - // Assume that every operation invalidates iterators and references. - // Insertion of one element can take O(size). Capacity of flat_tree grows in - // an implementation-defined manner. - // - // NOTE: Prefer to build a new flat_tree from a std::vector (or similar) - // instead of calling insert() repeatedly. - - std::pair insert(const value_type& val); - std::pair insert(value_type&& val); - - iterator insert(const_iterator position_hint, const value_type& x); - iterator insert(const_iterator position_hint, value_type&& x); - - // This method inserts the values from the range [first, last) into the - // current tree. In case of KEEP_LAST_OF_DUPES newly added elements can - // overwrite existing values. - template - void insert(InputIterator first, - InputIterator last, - FlatContainerDupes dupes = KEEP_FIRST_OF_DUPES); - - template - std::pair emplace(Args&&... args); - - template - iterator emplace_hint(const_iterator position_hint, Args&&... args); - - // -------------------------------------------------------------------------- - // Erase operations. - // - // Assume that every operation invalidates iterators and references. - // - // erase(position), erase(first, last) can take O(size). - // erase(key) may take O(size) + O(log(size)). - // - // Prefer base::EraseIf() or some other variation on erase(remove(), end()) - // idiom when deleting multiple non-consecutive elements. - - iterator erase(iterator position); - iterator erase(const_iterator position); - iterator erase(const_iterator first, const_iterator last); - template - size_type erase(const K& key); - - // -------------------------------------------------------------------------- - // Comparators. - - key_compare key_comp() const; - value_compare value_comp() const; - - // -------------------------------------------------------------------------- - // Search operations. - // - // Search operations have O(log(size)) complexity. - - template - size_type count(const K& key) const; - - template - iterator find(const K& key); - - template - const_iterator find(const K& key) const; - - template - std::pair equal_range(const K& key); - - template - std::pair equal_range(const K& key) const; - - template - iterator lower_bound(const K& key); - - template - const_iterator lower_bound(const K& key) const; - - template - iterator upper_bound(const K& key); - - template - const_iterator upper_bound(const K& key) const; - - // -------------------------------------------------------------------------- - // General operations. - // - // Assume that swap invalidates iterators and references. - // - // Implementation note: currently we use operator==() and operator<() on - // std::vector, because they have the same contract we need, so we use them - // directly for brevity and in case it is more optimal than calling equal() - // and lexicograhpical_compare(). If the underlying container type is changed, - // this code may need to be modified. - - void swap(flat_tree& other) noexcept; - - friend bool operator==(const flat_tree& lhs, const flat_tree& rhs) { - return lhs.impl_.body_ == rhs.impl_.body_; - } - - friend bool operator!=(const flat_tree& lhs, const flat_tree& rhs) { - return !(lhs == rhs); - } - - friend bool operator<(const flat_tree& lhs, const flat_tree& rhs) { - return lhs.impl_.body_ < rhs.impl_.body_; - } - - friend bool operator>(const flat_tree& lhs, const flat_tree& rhs) { - return rhs < lhs; - } - - friend bool operator>=(const flat_tree& lhs, const flat_tree& rhs) { - return !(lhs < rhs); - } - - friend bool operator<=(const flat_tree& lhs, const flat_tree& rhs) { - return !(lhs > rhs); - } - - friend void swap(flat_tree& lhs, flat_tree& rhs) noexcept { lhs.swap(rhs); } - - protected: - // Emplaces a new item into the tree that is known not to be in it. This - // is for implementing map operator[]. - template - iterator unsafe_emplace(const_iterator position, Args&&... args); - - // Attempts to emplace a new element with key |key|. Only if |key| is not yet - // present, construct value_type from |args| and insert it. Returns an - // iterator to the element with key |key| and a bool indicating whether an - // insertion happened. - template - std::pair emplace_key_args(const K& key, Args&&... args); - - // Similar to |emplace_key_args|, but checks |hint| first as a possible - // insertion position. - template - std::pair emplace_hint_key_args(const_iterator hint, - const K& key, - Args&&... args); - - private: - // Helper class for e.g. lower_bound that can compare a value on the left - // to a key on the right. - struct KeyValueCompare { - // The key comparison object must outlive this class. - explicit KeyValueCompare(const key_compare& key_comp) - : key_comp_(key_comp) {} - - template - bool operator()(const T& lhs, const U& rhs) const { - return key_comp_(extract_if_value_type(lhs), extract_if_value_type(rhs)); - } - - private: - const key_type& extract_if_value_type(const value_type& v) const { - GetKeyFromValue extractor; - return extractor(v); - } - - template - const K& extract_if_value_type(const K& k) const { - return k; - } - - const key_compare& key_comp_; - }; - - const flat_tree& as_const() { return *this; } - - iterator const_cast_it(const_iterator c_it) { - auto distance = std::distance(cbegin(), c_it); - return std::next(begin(), distance); - } - - // This method is inspired by both std::map::insert(P&&) and - // std::map::insert_or_assign(const K&, V&&). It inserts val if an equivalent - // element is not present yet, otherwise it overwrites. It returns an iterator - // to the modified element and a flag indicating whether insertion or - // assignment happened. - template - std::pair insert_or_assign(V&& val) { - auto position = lower_bound(GetKeyFromValue()(val)); - - if (position == end() || value_comp()(val, *position)) - return {impl_.body_.emplace(position, std::forward(val)), true}; - - *position = std::forward(val); - return {position, false}; - } - - // This method is similar to insert_or_assign, with the following differences: - // - Instead of searching [begin(), end()) it only searches [first, last). - // - In case no equivalent element is found, val is appended to the end of the - // underlying body and an iterator to the next bigger element in [first, - // last) is returned. - template - std::pair append_or_assign(iterator first, - iterator last, - V&& val) { - auto position = std::lower_bound(first, last, val, value_comp()); - - if (position == last || value_comp()(val, *position)) { - // emplace_back might invalidate position, which is why distance needs to - // be cached. - const difference_type distance = std::distance(begin(), position); - impl_.body_.emplace_back(std::forward(val)); - return {std::next(begin(), distance), true}; - } - - *position = std::forward(val); - return {position, false}; - } - - // This method is similar to insert, with the following differences: - // - Instead of searching [begin(), end()) it only searches [first, last). - // - In case no equivalent element is found, val is appended to the end of the - // underlying body and an iterator to the next bigger element in [first, - // last) is returned. - template - std::pair append_unique(iterator first, - iterator last, - V&& val) { - auto position = std::lower_bound(first, last, val, value_comp()); - - if (position == last || value_comp()(val, *position)) { - // emplace_back might invalidate position, which is why distance needs to - // be cached. - const difference_type distance = std::distance(begin(), position); - impl_.body_.emplace_back(std::forward(val)); - return {std::next(begin(), distance), true}; - } - - return {position, false}; - } - - void sort_and_unique(iterator first, - iterator last, - FlatContainerDupes dupes) { - // Preserve stability for the unique code below. - std::stable_sort(first, last, impl_.get_value_comp()); - - auto comparator = [this](const value_type& lhs, const value_type& rhs) { - // lhs is already <= rhs due to sort, therefore - // !(lhs < rhs) <=> lhs == rhs. - return !impl_.get_value_comp()(lhs, rhs); - }; - - iterator erase_after; - switch (dupes) { - case KEEP_FIRST_OF_DUPES: - erase_after = std::unique(first, last, comparator); - break; - case KEEP_LAST_OF_DUPES: - erase_after = LastUnique(first, last, comparator); - break; - } - erase(erase_after, last); - } - - // To support comparators that may not be possible to default-construct, we - // have to store an instance of Compare. Using this to store all internal - // state of flat_tree and using private inheritance to store compare lets us - // take advantage of an empty base class optimization to avoid extra space in - // the common case when Compare has no state. - struct Impl : private value_compare { - Impl() = default; - - template - explicit Impl(Cmp&& compare_arg, Body&&... underlying_type_args) - : value_compare(std::forward(compare_arg)), - body_(std::forward(underlying_type_args)...) {} - - const value_compare& get_value_comp() const { return *this; } - const key_compare& get_key_comp() const { return *this; } - - underlying_type body_; - } impl_; - - // If the compare is not transparent we want to construct key_type once. - template - using KeyTypeOrK = typename std:: - conditional::value, K, key_type>::type; -}; - -// ---------------------------------------------------------------------------- -// Lifetime. - -template -flat_tree::flat_tree() = default; - -template -flat_tree::flat_tree( - const KeyCompare& comp) - : impl_(comp) {} - -template -template -flat_tree::flat_tree( - InputIterator first, - InputIterator last, - FlatContainerDupes dupe_handling, - const KeyCompare& comp) - : impl_(comp, first, last) { - sort_and_unique(begin(), end(), dupe_handling); -} - -template -flat_tree::flat_tree( - const flat_tree&) = default; - -template -flat_tree::flat_tree( - std::vector items, - FlatContainerDupes dupe_handling, - const KeyCompare& comp) - : impl_(comp, std::move(items)) { - sort_and_unique(begin(), end(), dupe_handling); -} - -template -flat_tree::flat_tree( - std::initializer_list ilist, - FlatContainerDupes dupe_handling, - const KeyCompare& comp) - : flat_tree(std::begin(ilist), std::end(ilist), dupe_handling, comp) {} - -template -flat_tree::~flat_tree() = default; - -// ---------------------------------------------------------------------------- -// Assignments. - -template -auto flat_tree::operator=( - const flat_tree&) -> flat_tree& = default; - -template -auto flat_tree::operator=(flat_tree &&) - -> flat_tree& = default; - -template -auto flat_tree::operator=( - std::initializer_list ilist) -> flat_tree& { - impl_.body_ = ilist; - sort_and_unique(begin(), end(), KEEP_FIRST_OF_DUPES); - return *this; -} - -// ---------------------------------------------------------------------------- -// Memory management. - -template -void flat_tree::reserve( - size_type new_capacity) { - impl_.body_.reserve(new_capacity); -} - -template -auto flat_tree::capacity() const - -> size_type { - return impl_.body_.capacity(); -} - -template -void flat_tree::shrink_to_fit() { - impl_.body_.shrink_to_fit(); -} - -// ---------------------------------------------------------------------------- -// Size management. - -template -void flat_tree::clear() { - impl_.body_.clear(); -} - -template -auto flat_tree::size() const - -> size_type { - return impl_.body_.size(); -} - -template -auto flat_tree::max_size() const - -> size_type { - return impl_.body_.max_size(); -} - -template -bool flat_tree::empty() const { - return impl_.body_.empty(); -} - -// ---------------------------------------------------------------------------- -// Iterators. - -template -auto flat_tree::begin() -> iterator { - return impl_.body_.begin(); -} - -template -auto flat_tree::begin() const - -> const_iterator { - return impl_.body_.begin(); -} - -template -auto flat_tree::cbegin() const - -> const_iterator { - return impl_.body_.cbegin(); -} - -template -auto flat_tree::end() -> iterator { - return impl_.body_.end(); -} - -template -auto flat_tree::end() const - -> const_iterator { - return impl_.body_.end(); -} - -template -auto flat_tree::cend() const - -> const_iterator { - return impl_.body_.cend(); -} - -template -auto flat_tree::rbegin() - -> reverse_iterator { - return impl_.body_.rbegin(); -} - -template -auto flat_tree::rbegin() const - -> const_reverse_iterator { - return impl_.body_.rbegin(); -} - -template -auto flat_tree::crbegin() const - -> const_reverse_iterator { - return impl_.body_.crbegin(); -} - -template -auto flat_tree::rend() - -> reverse_iterator { - return impl_.body_.rend(); -} - -template -auto flat_tree::rend() const - -> const_reverse_iterator { - return impl_.body_.rend(); -} - -template -auto flat_tree::crend() const - -> const_reverse_iterator { - return impl_.body_.crend(); -} - -// ---------------------------------------------------------------------------- -// Insert operations. -// -// Currently we use position_hint the same way as eastl or boost: -// https://github.com/electronicarts/EASTL/blob/master/include/EASTL/vector_set.h#L493 - -template -auto flat_tree::insert( - const value_type& val) -> std::pair { - return emplace_key_args(GetKeyFromValue()(val), val); -} - -template -auto flat_tree::insert( - value_type&& val) -> std::pair { - return emplace_key_args(GetKeyFromValue()(val), std::move(val)); -} - -template -auto flat_tree::insert( - const_iterator position_hint, - const value_type& val) -> iterator { - return emplace_hint_key_args(position_hint, GetKeyFromValue()(val), val) - .first; -} - -template -auto flat_tree::insert( - const_iterator position_hint, - value_type&& val) -> iterator { - return emplace_hint_key_args(position_hint, GetKeyFromValue()(val), - std::move(val)) - .first; -} - -template -template -void flat_tree::insert( - InputIterator first, - InputIterator last, - FlatContainerDupes dupes) { - if (first == last) - return; - - // Cache results whether existing elements should be overwritten and whether - // inserting new elements happens immediately or will be done in a batch. - const bool overwrite_existing = dupes == KEEP_LAST_OF_DUPES; - const bool insert_inplace = - is_multipass() && std::next(first) == last; - - if (insert_inplace) { - if (overwrite_existing) { - for (; first != last; ++first) - insert_or_assign(*first); - } else - std::copy(first, last, std::inserter(*this, end())); - return; - } - - // Provide a convenience lambda to obtain an iterator pointing past the last - // old element. This needs to be dymanic due to possible re-allocations. - const size_type original_size = size(); - auto middle = [this, original_size]() { - return std::next(begin(), original_size); - }; - - // For batch updates initialize the first insertion point. - difference_type pos_first_new = original_size; - - // Loop over the input range while appending new values and overwriting - // existing ones, if applicable. Keep track of the first insertion point. - if (overwrite_existing) { - for (; first != last; ++first) { - std::pair result = - append_or_assign(begin(), middle(), *first); - if (result.second) { - pos_first_new = - std::min(pos_first_new, std::distance(begin(), result.first)); - } - } - } else { - for (; first != last; ++first) { - std::pair result = - append_unique(begin(), middle(), *first); - if (result.second) { - pos_first_new = - std::min(pos_first_new, std::distance(begin(), result.first)); - } - } - } - - // The new elements might be unordered and contain duplicates, so post-process - // the just inserted elements and merge them with the rest, inserting them at - // the previously found spot. - sort_and_unique(middle(), end(), dupes); - std::inplace_merge(std::next(begin(), pos_first_new), middle(), end(), - value_comp()); -} - -template -template -auto flat_tree::emplace(Args&&... args) - -> std::pair { - return insert(value_type(std::forward(args)...)); -} - -template -template -auto flat_tree::emplace_hint( - const_iterator position_hint, - Args&&... args) -> iterator { - return insert(position_hint, value_type(std::forward(args)...)); -} - -// ---------------------------------------------------------------------------- -// Erase operations. - -template -auto flat_tree::erase( - iterator position) -> iterator { - return impl_.body_.erase(position); -} - -template -auto flat_tree::erase( - const_iterator position) -> iterator { - return impl_.body_.erase(position); -} - -template -template -auto flat_tree::erase(const K& val) - -> size_type { - auto eq_range = equal_range(val); - auto res = std::distance(eq_range.first, eq_range.second); - erase(eq_range.first, eq_range.second); - return res; -} - -template -auto flat_tree::erase( - const_iterator first, - const_iterator last) -> iterator { - return impl_.body_.erase(first, last); -} - -// ---------------------------------------------------------------------------- -// Comparators. - -template -auto flat_tree::key_comp() const - -> key_compare { - return impl_.get_key_comp(); -} - -template -auto flat_tree::value_comp() const - -> value_compare { - return impl_.get_value_comp(); -} - -// ---------------------------------------------------------------------------- -// Search operations. - -template -template -auto flat_tree::count( - const K& key) const -> size_type { - auto eq_range = equal_range(key); - return std::distance(eq_range.first, eq_range.second); -} - -template -template -auto flat_tree::find(const K& key) - -> iterator { - return const_cast_it(as_const().find(key)); -} - -template -template -auto flat_tree::find( - const K& key) const -> const_iterator { - auto eq_range = equal_range(key); - return (eq_range.first == eq_range.second) ? end() : eq_range.first; -} - -template -template -auto flat_tree::equal_range( - const K& key) -> std::pair { - auto res = as_const().equal_range(key); - return {const_cast_it(res.first), const_cast_it(res.second)}; -} - -template -template -auto flat_tree::equal_range( - const K& key) const -> std::pair { - auto lower = lower_bound(key); - - GetKeyFromValue extractor; - if (lower == end() || impl_.get_key_comp()(key, extractor(*lower))) - return {lower, lower}; - - return {lower, std::next(lower)}; -} - -template -template -auto flat_tree::lower_bound( - const K& key) -> iterator { - return const_cast_it(as_const().lower_bound(key)); -} - -template -template -auto flat_tree::lower_bound( - const K& key) const -> const_iterator { - static_assert(std::is_convertible&, const K&>::value, - "Requested type cannot be bound to the container's key_type " - "which is required for a non-transparent compare."); - - const KeyTypeOrK& key_ref = key; - - KeyValueCompare key_value(impl_.get_key_comp()); - return std::lower_bound(begin(), end(), key_ref, key_value); -} - -template -template -auto flat_tree::upper_bound( - const K& key) -> iterator { - return const_cast_it(as_const().upper_bound(key)); -} - -template -template -auto flat_tree::upper_bound( - const K& key) const -> const_iterator { - static_assert(std::is_convertible&, const K&>::value, - "Requested type cannot be bound to the container's key_type " - "which is required for a non-transparent compare."); - - const KeyTypeOrK& key_ref = key; - - KeyValueCompare key_value(impl_.get_key_comp()); - return std::upper_bound(begin(), end(), key_ref, key_value); -} - -// ---------------------------------------------------------------------------- -// General operations. - -template -void flat_tree::swap( - flat_tree& other) noexcept { - std::swap(impl_, other.impl_); -} - -template -template -auto flat_tree::unsafe_emplace( - const_iterator position, - Args&&... args) -> iterator { - return impl_.body_.emplace(position, std::forward(args)...); -} - -template -template -auto flat_tree::emplace_key_args( - const K& key, - Args&&... args) -> std::pair { - auto lower = lower_bound(key); - if (lower == end() || key_comp()(key, GetKeyFromValue()(*lower))) - return {unsafe_emplace(lower, std::forward(args)...), true}; - return {lower, false}; -} - -template -template -auto flat_tree::emplace_hint_key_args( - const_iterator hint, - const K& key, - Args&&... args) -> std::pair { - GetKeyFromValue extractor; - if ((hint == begin() || key_comp()(extractor(*std::prev(hint)), key))) { - if (hint == end() || key_comp()(key, extractor(*hint))) { - // *(hint - 1) < key < *hint => key did not exist and hint is correct. - return {unsafe_emplace(hint, std::forward(args)...), true}; - } - if (!key_comp()(extractor(*hint), key)) { - // key == *hint => no-op, return correct hint. - return {const_cast_it(hint), false}; - } - } - // hint was not helpful, dispatch to hintless version. - return emplace_key_args(key, std::forward(args)...); -} - -// For containers like sets, the key is the same as the value. This implements -// the GetKeyFromValue template parameter to flat_tree for this case. -template -struct GetKeyFromValueIdentity { - const Key& operator()(const Key& k) const { return k; } -}; - -} // namespace internal - -// ---------------------------------------------------------------------------- -// Free functions. - -// Erases all elements that match predicate. It has O(size) complexity. -template -void EraseIf(base::internal::flat_tree& - container, - Predicate pred) { - container.erase(std::remove_if(container.begin(), container.end(), pred), - container.end()); -} - -} // namespace base - -#endif // BASE_CONTAINERS_FLAT_TREE_H_ diff --git a/containers/flat_tree_unittest.cc b/containers/flat_tree_unittest.cc deleted file mode 100644 index 5b788d506..000000000 --- a/containers/flat_tree_unittest.cc +++ /dev/null @@ -1,1385 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/containers/flat_tree.h" - -// Following tests are ported and extended tests from libcpp for std::set. -// They can be found here: -// https://github.com/llvm-mirror/libcxx/tree/master/test/std/containers/associative/set -// -// Not ported tests: -// * No tests with PrivateConstructor and std::less<> changed to std::less -// These tests have to do with C++14 std::less<> -// http://en.cppreference.com/w/cpp/utility/functional/less_void -// and add support for templated versions of lookup functions. -// Because we use same implementation, we figured that it's OK just to check -// compilation and this is what we do in flat_set_unittest/flat_map_unittest. -// * No tests for max_size() -// Has to do with allocator support. -// * No tests with DefaultOnly. -// Standard containers allocate each element in the separate node on the heap -// and then manipulate these nodes. Flat containers store their elements in -// contiguous memory and move them around, type is required to be movable. -// * No tests for N3644. -// This proposal suggests that all default constructed iterators compare -// equal. Currently we use std::vector iterators and they don't implement -// this. -// * No tests with min_allocator and no tests counting allocations. -// Flat sets currently don't support allocators. - -#include -#include -#include -#include -#include -#include - -#include "base/macros.h" -#include "base/template_util.h" -#include "base/test/move_only_int.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace internal { - -namespace { - -template -class InputIterator { - public: - using iterator_category = std::input_iterator_tag; - using value_type = typename std::iterator_traits::value_type; - using difference_type = typename std::iterator_traits::difference_type; - using pointer = It; - using reference = typename std::iterator_traits::reference; - - InputIterator() : it_() {} - explicit InputIterator(It it) : it_(it) {} - - reference operator*() const { return *it_; } - pointer operator->() const { return it_; } - - InputIterator& operator++() { - ++it_; - return *this; - } - InputIterator operator++(int) { - InputIterator tmp(*this); - ++(*this); - return tmp; - } - - friend bool operator==(const InputIterator& lhs, const InputIterator& rhs) { - return lhs.it_ == rhs.it_; - } - friend bool operator!=(const InputIterator& lhs, const InputIterator& rhs) { - return !(lhs == rhs); - } - - private: - It it_; -}; - -template -InputIterator MakeInputIterator(It it) { - return InputIterator(it); -} - -class Emplaceable { - public: - Emplaceable() : Emplaceable(0, 0.0) {} - Emplaceable(int i, double d) : int_(i), double_(d) {} - Emplaceable(Emplaceable&& other) : int_(other.int_), double_(other.double_) { - other.int_ = 0; - other.double_ = 0.0; - } - - Emplaceable& operator=(Emplaceable&& other) { - int_ = other.int_; - other.int_ = 0; - double_ = other.double_; - other.double_ = 0.0; - return *this; - } - - friend bool operator==(const Emplaceable& lhs, const Emplaceable& rhs) { - return std::tie(lhs.int_, lhs.double_) == std::tie(rhs.int_, rhs.double_); - } - - friend bool operator<(const Emplaceable& lhs, const Emplaceable& rhs) { - return std::tie(lhs.int_, lhs.double_) < std::tie(rhs.int_, rhs.double_); - } - - private: - int int_; - double double_; - - DISALLOW_COPY_AND_ASSIGN(Emplaceable); -}; - -struct TemplateConstructor { - template - TemplateConstructor(const T&) {} - - friend bool operator<(const TemplateConstructor&, - const TemplateConstructor&) { - return false; - } -}; - -class NonDefaultConstructibleCompare { - public: - explicit NonDefaultConstructibleCompare(int) {} - - template - bool operator()(const T& lhs, const T& rhs) const { - return std::less()(lhs, rhs); - } -}; - -template -struct LessByFirst { - bool operator()(const PairType& lhs, const PairType& rhs) const { - return lhs.first < rhs.first; - } -}; - -// Common test trees. -using IntTree = - flat_tree, std::less>; -using IntPair = std::pair; -using IntPairTree = flat_tree, - LessByFirst>; -using MoveOnlyTree = flat_tree, - std::less>; -using EmplaceableTree = flat_tree, - std::less>; -using ReversedTree = - flat_tree, std::greater>; - -using TreeWithStrangeCompare = flat_tree, - NonDefaultConstructibleCompare>; - -using ::testing::ElementsAre; - -} // namespace - -TEST(FlatTree, IsMultipass) { - static_assert(!is_multipass>(), - "InputIterator is not multipass"); - static_assert(!is_multipass>(), - "OutputIterator is not multipass"); - - static_assert(is_multipass::iterator>(), - "ForwardIterator is multipass"); - static_assert(is_multipass::iterator>(), - "BidirectionalIterator is multipass"); - static_assert(is_multipass::iterator>(), - "RandomAccessIterator is multipass"); -} - -TEST(FlatTree, LastUnique) { - using Pair = std::pair; - using Vect = std::vector; - - auto cmp = [](const Pair& lhs, const Pair& rhs) { - return lhs.first == rhs.first; - }; - - // Empty case. - Vect empty; - EXPECT_EQ(empty.end(), LastUnique(empty.begin(), empty.end(), cmp)); - - // Single element. - Vect one; - one.push_back(Pair(1, 1)); - EXPECT_EQ(one.end(), LastUnique(one.begin(), one.end(), cmp)); - ASSERT_EQ(1u, one.size()); - EXPECT_THAT(one, ElementsAre(Pair(1, 1))); - - // Two elements, already unique. - Vect two_u; - two_u.push_back(Pair(1, 1)); - two_u.push_back(Pair(2, 2)); - EXPECT_EQ(two_u.end(), LastUnique(two_u.begin(), two_u.end(), cmp)); - EXPECT_THAT(two_u, ElementsAre(Pair(1, 1), Pair(2, 2))); - - // Two elements, dupes. - Vect two_d; - two_d.push_back(Pair(1, 1)); - two_d.push_back(Pair(1, 2)); - auto last = LastUnique(two_d.begin(), two_d.end(), cmp); - EXPECT_EQ(two_d.begin() + 1, last); - two_d.erase(last, two_d.end()); - EXPECT_THAT(two_d, ElementsAre(Pair(1, 2))); - - // Non-dupes, dupes, non-dupes. - Vect ndn; - ndn.push_back(Pair(1, 1)); - ndn.push_back(Pair(2, 1)); - ndn.push_back(Pair(2, 2)); - ndn.push_back(Pair(2, 3)); - ndn.push_back(Pair(3, 1)); - last = LastUnique(ndn.begin(), ndn.end(), cmp); - EXPECT_EQ(ndn.begin() + 3, last); - ndn.erase(last, ndn.end()); - EXPECT_THAT(ndn, ElementsAre(Pair(1, 1), Pair(2, 3), Pair(3, 1))); - - // Dupes, non-dupes, dupes. - Vect dnd; - dnd.push_back(Pair(1, 1)); - dnd.push_back(Pair(1, 2)); - dnd.push_back(Pair(1, 3)); - dnd.push_back(Pair(2, 1)); - dnd.push_back(Pair(3, 1)); - dnd.push_back(Pair(3, 2)); - dnd.push_back(Pair(3, 3)); - last = LastUnique(dnd.begin(), dnd.end(), cmp); - EXPECT_EQ(dnd.begin() + 3, last); - dnd.erase(last, dnd.end()); - EXPECT_THAT(dnd, ElementsAre(Pair(1, 3), Pair(2, 1), Pair(3, 3))); -} - -// ---------------------------------------------------------------------------- -// Class. - -// Check that flat_tree and its iterators can be instantiated with an -// incomplete type. - -TEST(FlatTree, IncompleteType) { - struct A { - using Tree = flat_tree, std::less>; - int data; - Tree set_with_incomplete_type; - Tree::iterator it; - Tree::const_iterator cit; - - // We do not declare operator< because clang complains that it's unused. - }; - - A a; -} - -TEST(FlatTree, Stability) { - using Pair = std::pair; - - using Tree = - flat_tree, LessByFirst>; - - // Constructors are stable. - Tree cont({{0, 0}, {1, 0}, {0, 1}, {2, 0}, {0, 2}, {1, 1}}); - - auto AllOfSecondsAreZero = [&cont] { - return std::all_of(cont.begin(), cont.end(), - [](const Pair& elem) { return elem.second == 0; }); - }; - - EXPECT_TRUE(AllOfSecondsAreZero()) << "constructor should be stable"; - - // Should not replace existing. - cont.insert(Pair(0, 2)); - cont.insert(Pair(1, 2)); - cont.insert(Pair(2, 2)); - - EXPECT_TRUE(AllOfSecondsAreZero()) << "insert should be stable"; - - cont.insert(Pair(3, 0)); - cont.insert(Pair(3, 2)); - - EXPECT_TRUE(AllOfSecondsAreZero()) << "insert should be stable"; -} - -// ---------------------------------------------------------------------------- -// Types. - -// key_type -// key_compare -// value_type -// value_compare -// pointer -// const_pointer -// reference -// const_reference -// size_type -// difference_type -// iterator -// const_iterator -// reverse_iterator -// const_reverse_iterator - -TEST(FlatTree, Types) { - // These are guaranteed to be portable. - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same, IntTree::key_compare>::value), - ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), - ""); - static_assert((std::is_same::value), ""); - static_assert((std::is_same::value), ""); -} - -// ---------------------------------------------------------------------------- -// Lifetime. - -// flat_tree() -// flat_tree(const Compare& comp) - -TEST(FlatTree, DefaultConstructor) { - { - IntTree cont; - EXPECT_THAT(cont, ElementsAre()); - } - - { - TreeWithStrangeCompare cont(NonDefaultConstructibleCompare(0)); - EXPECT_THAT(cont, ElementsAre()); - } -} - -// flat_tree(InputIterator first, -// InputIterator last, -// FlatContainerDupes dupe_handling, -// const Compare& comp = Compare()) - -TEST(FlatTree, RangeConstructor) { - { - IntPair input_vals[] = {{1, 1}, {1, 2}, {2, 1}, {2, 2}, {1, 3}, - {2, 3}, {3, 1}, {3, 2}, {3, 3}}; - - IntPairTree first_of(MakeInputIterator(std::begin(input_vals)), - MakeInputIterator(std::end(input_vals))); - EXPECT_THAT(first_of, - ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1))); - - IntPairTree last_of(MakeInputIterator(std::begin(input_vals)), - MakeInputIterator(std::end(input_vals)), - KEEP_LAST_OF_DUPES); - EXPECT_THAT(last_of, - ElementsAre(IntPair(1, 3), IntPair(2, 3), IntPair(3, 3))); - } - { - TreeWithStrangeCompare::value_type input_vals[] = {1, 1, 1, 2, 2, - 2, 3, 3, 3}; - - TreeWithStrangeCompare cont(MakeInputIterator(std::begin(input_vals)), - MakeInputIterator(std::end(input_vals)), - KEEP_FIRST_OF_DUPES, - NonDefaultConstructibleCompare(0)); - EXPECT_THAT(cont, ElementsAre(1, 2, 3)); - } -} - -// flat_tree(const flat_tree& x) - -TEST(FlatTree, CopyConstructor) { - IntTree original({1, 2, 3, 4}); - IntTree copied(original); - - EXPECT_THAT(copied, ElementsAre(1, 2, 3, 4)); - - EXPECT_THAT(copied, ElementsAre(1, 2, 3, 4)); - EXPECT_THAT(original, ElementsAre(1, 2, 3, 4)); - EXPECT_EQ(original, copied); -} - -// flat_tree(flat_tree&& x) - -TEST(FlatTree, MoveConstructor) { - int input_range[] = {1, 2, 3, 4}; - - MoveOnlyTree original(std::begin(input_range), std::end(input_range)); - MoveOnlyTree moved(std::move(original)); - - EXPECT_EQ(1U, moved.count(MoveOnlyInt(1))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(2))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(3))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(4))); -} - -// flat_tree(std::vector, FlatContainerDupes dupe_handling) - -TEST(FlatTree, VectorConstructor) { - using Pair = std::pair; - - // Construct an unsorted vector with a duplicate item in it. Sorted by the - // first item, the second allows us to test for stability. Using a move - // only type to ensure the vector is not copied. - std::vector storage; - storage.push_back(Pair(2, MoveOnlyInt(0))); - storage.push_back(Pair(1, MoveOnlyInt(0))); - storage.push_back(Pair(2, MoveOnlyInt(1))); - - using Tree = - flat_tree, LessByFirst>; - Tree tree(std::move(storage)); - - // The list should be two items long, with only the first "2" saved. - ASSERT_EQ(2u, tree.size()); - const Pair& zeroth = *tree.begin(); - ASSERT_EQ(1, zeroth.first); - ASSERT_EQ(0, zeroth.second.data()); - - const Pair& first = *(tree.begin() + 1); - ASSERT_EQ(2, first.first); - ASSERT_EQ(0, first.second.data()); - - // Test KEEP_LAST_OF_DUPES with a simple vector constructor. - std::vector int_storage{{1, 1}, {1, 2}, {2, 1}}; - IntPairTree int_tree(std::move(int_storage), KEEP_LAST_OF_DUPES); - EXPECT_THAT(int_tree, ElementsAre(IntPair(1, 2), IntPair(2, 1))); -} - -// flat_tree(std::initializer_list ilist, -// FlatContainerDupes dupe_handling, -// const Compare& comp = Compare()) - -TEST(FlatTree, InitializerListConstructor) { - { - IntTree cont({1, 2, 3, 4, 5, 6, 10, 8}); - EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10)); - } - { - IntTree cont({1, 2, 3, 4, 5, 6, 10, 8}); - EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10)); - } - { - TreeWithStrangeCompare cont({1, 2, 3, 4, 5, 6, 10, 8}, KEEP_FIRST_OF_DUPES, - NonDefaultConstructibleCompare(0)); - EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10)); - } - { - IntPairTree first_of({{1, 1}, {2, 1}, {1, 2}}); - EXPECT_THAT(first_of, ElementsAre(IntPair(1, 1), IntPair(2, 1))); - } - { - IntPairTree last_of({{1, 1}, {2, 1}, {1, 2}}, KEEP_LAST_OF_DUPES); - EXPECT_THAT(last_of, ElementsAre(IntPair(1, 2), IntPair(2, 1))); - } -} - -// ---------------------------------------------------------------------------- -// Assignments. - -// flat_tree& operator=(const flat_tree&) - -TEST(FlatTree, CopyAssignable) { - IntTree original({1, 2, 3, 4}); - IntTree copied; - copied = original; - - EXPECT_THAT(copied, ElementsAre(1, 2, 3, 4)); - EXPECT_THAT(original, ElementsAre(1, 2, 3, 4)); - EXPECT_EQ(original, copied); -} - -// flat_tree& operator=(flat_tree&&) - -TEST(FlatTree, MoveAssignable) { - int input_range[] = {1, 2, 3, 4}; - - MoveOnlyTree original(std::begin(input_range), std::end(input_range)); - MoveOnlyTree moved; - moved = std::move(original); - - EXPECT_EQ(1U, moved.count(MoveOnlyInt(1))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(2))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(3))); - EXPECT_EQ(1U, moved.count(MoveOnlyInt(4))); -} - -// flat_tree& operator=(std::initializer_list ilist) - -TEST(FlatTree, InitializerListAssignable) { - IntTree cont({0}); - cont = {1, 2, 3, 4, 5, 6, 10, 8}; - - EXPECT_EQ(0U, cont.count(0)); - EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10)); -} - -// -------------------------------------------------------------------------- -// Memory management. - -// void reserve(size_type new_capacity) - -TEST(FlatTree, Reserve) { - IntTree cont({1, 2, 3}); - - cont.reserve(5); - EXPECT_LE(5U, cont.capacity()); -} - -// size_type capacity() const - -TEST(FlatTree, Capacity) { - IntTree cont({1, 2, 3}); - - EXPECT_LE(cont.size(), cont.capacity()); - cont.reserve(5); - EXPECT_LE(cont.size(), cont.capacity()); -} - -// void shrink_to_fit() - -TEST(FlatTree, ShrinkToFit) { - IntTree cont({1, 2, 3}); - - IntTree::size_type capacity_before = cont.capacity(); - cont.shrink_to_fit(); - EXPECT_GE(capacity_before, cont.capacity()); -} - -// ---------------------------------------------------------------------------- -// Size management. - -// void clear() - -TEST(FlatTree, Clear) { - IntTree cont({1, 2, 3, 4, 5, 6, 7, 8}); - cont.clear(); - EXPECT_THAT(cont, ElementsAre()); -} - -// size_type size() const - -TEST(FlatTree, Size) { - IntTree cont; - - EXPECT_EQ(0U, cont.size()); - cont.insert(2); - EXPECT_EQ(1U, cont.size()); - cont.insert(1); - EXPECT_EQ(2U, cont.size()); - cont.insert(3); - EXPECT_EQ(3U, cont.size()); - cont.erase(cont.begin()); - EXPECT_EQ(2U, cont.size()); - cont.erase(cont.begin()); - EXPECT_EQ(1U, cont.size()); - cont.erase(cont.begin()); - EXPECT_EQ(0U, cont.size()); -} - -// bool empty() const - -TEST(FlatTree, Empty) { - IntTree cont; - - EXPECT_TRUE(cont.empty()); - cont.insert(1); - EXPECT_FALSE(cont.empty()); - cont.clear(); - EXPECT_TRUE(cont.empty()); -} - -// ---------------------------------------------------------------------------- -// Iterators. - -// iterator begin() -// const_iterator begin() const -// iterator end() -// const_iterator end() const -// -// reverse_iterator rbegin() -// const_reverse_iterator rbegin() const -// reverse_iterator rend() -// const_reverse_iterator rend() const -// -// const_iterator cbegin() const -// const_iterator cend() const -// const_reverse_iterator crbegin() const -// const_reverse_iterator crend() const - -TEST(FlatTree, Iterators) { - IntTree cont({1, 2, 3, 4, 5, 6, 7, 8}); - - auto size = static_cast(cont.size()); - - EXPECT_EQ(size, std::distance(cont.begin(), cont.end())); - EXPECT_EQ(size, std::distance(cont.cbegin(), cont.cend())); - EXPECT_EQ(size, std::distance(cont.rbegin(), cont.rend())); - EXPECT_EQ(size, std::distance(cont.crbegin(), cont.crend())); - - { - IntTree::iterator it = cont.begin(); - IntTree::const_iterator c_it = cont.cbegin(); - EXPECT_EQ(it, c_it); - for (int j = 1; it != cont.end(); ++it, ++c_it, ++j) { - EXPECT_EQ(j, *it); - EXPECT_EQ(j, *c_it); - } - } - { - IntTree::reverse_iterator rit = cont.rbegin(); - IntTree::const_reverse_iterator c_rit = cont.crbegin(); - EXPECT_EQ(rit, c_rit); - for (int j = static_cast(size); rit != cont.rend(); - ++rit, ++c_rit, --j) { - EXPECT_EQ(j, *rit); - EXPECT_EQ(j, *c_rit); - } - } -} - -// ---------------------------------------------------------------------------- -// Insert operations. - -// pair insert(const value_type& val) - -TEST(FlatTree, InsertLValue) { - IntTree cont; - - int value = 2; - std::pair result = cont.insert(value); - EXPECT_TRUE(result.second); - EXPECT_EQ(cont.begin(), result.first); - EXPECT_EQ(1U, cont.size()); - EXPECT_EQ(2, *result.first); - - value = 1; - result = cont.insert(value); - EXPECT_TRUE(result.second); - EXPECT_EQ(cont.begin(), result.first); - EXPECT_EQ(2U, cont.size()); - EXPECT_EQ(1, *result.first); - - value = 3; - result = cont.insert(value); - EXPECT_TRUE(result.second); - EXPECT_EQ(std::prev(cont.end()), result.first); - EXPECT_EQ(3U, cont.size()); - EXPECT_EQ(3, *result.first); - - value = 3; - result = cont.insert(value); - EXPECT_FALSE(result.second); - EXPECT_EQ(std::prev(cont.end()), result.first); - EXPECT_EQ(3U, cont.size()); - EXPECT_EQ(3, *result.first); -} - -// pair insert(value_type&& val) - -TEST(FlatTree, InsertRValue) { - MoveOnlyTree cont; - - std::pair result = cont.insert(MoveOnlyInt(2)); - EXPECT_TRUE(result.second); - EXPECT_EQ(cont.begin(), result.first); - EXPECT_EQ(1U, cont.size()); - EXPECT_EQ(2, result.first->data()); - - result = cont.insert(MoveOnlyInt(1)); - EXPECT_TRUE(result.second); - EXPECT_EQ(cont.begin(), result.first); - EXPECT_EQ(2U, cont.size()); - EXPECT_EQ(1, result.first->data()); - - result = cont.insert(MoveOnlyInt(3)); - EXPECT_TRUE(result.second); - EXPECT_EQ(std::prev(cont.end()), result.first); - EXPECT_EQ(3U, cont.size()); - EXPECT_EQ(3, result.first->data()); - - result = cont.insert(MoveOnlyInt(3)); - EXPECT_FALSE(result.second); - EXPECT_EQ(std::prev(cont.end()), result.first); - EXPECT_EQ(3U, cont.size()); - EXPECT_EQ(3, result.first->data()); -} - -// iterator insert(const_iterator position_hint, const value_type& val) - -TEST(FlatTree, InsertPositionLValue) { - IntTree cont; - - IntTree::iterator result = cont.insert(cont.cend(), 2); - EXPECT_EQ(cont.begin(), result); - EXPECT_EQ(1U, cont.size()); - EXPECT_EQ(2, *result); - - result = cont.insert(cont.cend(), 1); - EXPECT_EQ(cont.begin(), result); - EXPECT_EQ(2U, cont.size()); - EXPECT_EQ(1, *result); - - result = cont.insert(cont.cend(), 3); - EXPECT_EQ(std::prev(cont.end()), result); - EXPECT_EQ(3U, cont.size()); - EXPECT_EQ(3, *result); - - result = cont.insert(cont.cend(), 3); - EXPECT_EQ(std::prev(cont.end()), result); - EXPECT_EQ(3U, cont.size()); - EXPECT_EQ(3, *result); -} - -// iterator insert(const_iterator position_hint, value_type&& val) - -TEST(FlatTree, InsertPositionRValue) { - MoveOnlyTree cont; - - MoveOnlyTree::iterator result = cont.insert(cont.cend(), MoveOnlyInt(2)); - EXPECT_EQ(cont.begin(), result); - EXPECT_EQ(1U, cont.size()); - EXPECT_EQ(2, result->data()); - - result = cont.insert(cont.cend(), MoveOnlyInt(1)); - EXPECT_EQ(cont.begin(), result); - EXPECT_EQ(2U, cont.size()); - EXPECT_EQ(1, result->data()); - - result = cont.insert(cont.cend(), MoveOnlyInt(3)); - EXPECT_EQ(std::prev(cont.end()), result); - EXPECT_EQ(3U, cont.size()); - EXPECT_EQ(3, result->data()); - - result = cont.insert(cont.cend(), MoveOnlyInt(3)); - EXPECT_EQ(std::prev(cont.end()), result); - EXPECT_EQ(3U, cont.size()); - EXPECT_EQ(3, result->data()); -} - -// template -// void insert(InputIterator first, InputIterator last); - -TEST(FlatTree, InsertIterIter) { - struct GetKeyFromIntIntPair { - const int& operator()(const std::pair& p) const { - return p.first; - } - }; - - using IntIntMap = - flat_tree>; - - { - IntIntMap cont; - IntPair int_pairs[] = {{3, 1}, {1, 1}, {4, 1}, {2, 1}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs)); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1), - IntPair(4, 1))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - std::vector int_pairs; - cont.insert(std::begin(int_pairs), std::end(int_pairs)); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1), - IntPair(4, 1))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{1, 1}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs)); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1), - IntPair(4, 1))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{1, 2}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 1), IntPair(3, 1), - IntPair(4, 1))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{5, 1}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs)); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1), - IntPair(4, 1), IntPair(5, 1))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{5, 1}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1), - IntPair(4, 1), IntPair(5, 1))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs)); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1), - IntPair(4, 1))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 2), IntPair(3, 2), - IntPair(4, 2))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}, {7, 2}, {6, 2}, - {8, 2}, {5, 2}, {5, 3}, {6, 3}, {7, 3}, {8, 3}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs)); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1), - IntPair(4, 1), IntPair(5, 2), IntPair(6, 2), - IntPair(7, 2), IntPair(8, 2))); - } - - { - IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}}); - IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}, {7, 2}, {6, 2}, - {8, 2}, {5, 2}, {5, 3}, {6, 3}, {7, 3}, {8, 3}}; - cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES); - EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 2), IntPair(3, 2), - IntPair(4, 2), IntPair(5, 3), IntPair(6, 3), - IntPair(7, 3), IntPair(8, 3))); - } -} - -// template -// pair emplace(Args&&... args) - -TEST(FlatTree, Emplace) { - { - EmplaceableTree cont; - - std::pair result = cont.emplace(); - EXPECT_TRUE(result.second); - EXPECT_EQ(cont.begin(), result.first); - EXPECT_EQ(1U, cont.size()); - EXPECT_EQ(Emplaceable(), *cont.begin()); - - result = cont.emplace(2, 3.5); - EXPECT_TRUE(result.second); - EXPECT_EQ(std::next(cont.begin()), result.first); - EXPECT_EQ(2U, cont.size()); - EXPECT_EQ(Emplaceable(2, 3.5), *result.first); - - result = cont.emplace(2, 3.5); - EXPECT_FALSE(result.second); - EXPECT_EQ(std::next(cont.begin()), result.first); - EXPECT_EQ(2U, cont.size()); - EXPECT_EQ(Emplaceable(2, 3.5), *result.first); - } - { - IntTree cont; - - std::pair result = cont.emplace(2); - EXPECT_TRUE(result.second); - EXPECT_EQ(cont.begin(), result.first); - EXPECT_EQ(1U, cont.size()); - EXPECT_EQ(2, *result.first); - } -} - -// template -// iterator emplace_hint(const_iterator position_hint, Args&&... args) - -TEST(FlatTree, EmplacePosition) { - { - EmplaceableTree cont; - - EmplaceableTree::iterator result = cont.emplace_hint(cont.cend()); - EXPECT_EQ(cont.begin(), result); - EXPECT_EQ(1U, cont.size()); - EXPECT_EQ(Emplaceable(), *cont.begin()); - - result = cont.emplace_hint(cont.cend(), 2, 3.5); - EXPECT_EQ(std::next(cont.begin()), result); - EXPECT_EQ(2U, cont.size()); - EXPECT_EQ(Emplaceable(2, 3.5), *result); - - result = cont.emplace_hint(cont.cbegin(), 2, 3.5); - EXPECT_EQ(std::next(cont.begin()), result); - EXPECT_EQ(2U, cont.size()); - EXPECT_EQ(Emplaceable(2, 3.5), *result); - } - { - IntTree cont; - - IntTree::iterator result = cont.emplace_hint(cont.cend(), 2); - EXPECT_EQ(cont.begin(), result); - EXPECT_EQ(1U, cont.size()); - EXPECT_EQ(2, *result); - } -} - -// ---------------------------------------------------------------------------- -// Erase operations. - -// iterator erase(const_iterator position_hint) - -TEST(FlatTree, ErasePosition) { - { - IntTree cont({1, 2, 3, 4, 5, 6, 7, 8}); - - IntTree::iterator it = cont.erase(std::next(cont.cbegin(), 3)); - EXPECT_EQ(std::next(cont.begin(), 3), it); - EXPECT_THAT(cont, ElementsAre(1, 2, 3, 5, 6, 7, 8)); - - it = cont.erase(std::next(cont.cbegin(), 0)); - EXPECT_EQ(cont.begin(), it); - EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7, 8)); - - it = cont.erase(std::next(cont.cbegin(), 5)); - EXPECT_EQ(cont.end(), it); - EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7)); - - it = cont.erase(std::next(cont.cbegin(), 1)); - EXPECT_EQ(std::next(cont.begin()), it); - EXPECT_THAT(cont, ElementsAre(2, 5, 6, 7)); - - it = cont.erase(std::next(cont.cbegin(), 2)); - EXPECT_EQ(std::next(cont.begin(), 2), it); - EXPECT_THAT(cont, ElementsAre(2, 5, 7)); - - it = cont.erase(std::next(cont.cbegin(), 2)); - EXPECT_EQ(std::next(cont.begin(), 2), it); - EXPECT_THAT(cont, ElementsAre(2, 5)); - - it = cont.erase(std::next(cont.cbegin(), 0)); - EXPECT_EQ(std::next(cont.begin(), 0), it); - EXPECT_THAT(cont, ElementsAre(5)); - - it = cont.erase(cont.cbegin()); - EXPECT_EQ(cont.begin(), it); - EXPECT_EQ(cont.end(), it); - } - // This is LWG #2059. - // There is a potential ambiguity between erase with an iterator and erase - // with a key, if key has a templated constructor. - { - using T = TemplateConstructor; - - flat_tree, std::less<>> cont; - T v(0); - - auto it = cont.find(v); - if (it != cont.end()) - cont.erase(it); - } -} - -// iterator erase(const_iterator first, const_iterator last) - -TEST(FlatTree, EraseRange) { - IntTree cont({1, 2, 3, 4, 5, 6, 7, 8}); - - IntTree::iterator it = - cont.erase(std::next(cont.cbegin(), 5), std::next(cont.cbegin(), 5)); - EXPECT_EQ(std::next(cont.begin(), 5), it); - EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8)); - - it = cont.erase(std::next(cont.cbegin(), 3), std::next(cont.cbegin(), 4)); - EXPECT_EQ(std::next(cont.begin(), 3), it); - EXPECT_THAT(cont, ElementsAre(1, 2, 3, 5, 6, 7, 8)); - - it = cont.erase(std::next(cont.cbegin(), 2), std::next(cont.cbegin(), 5)); - EXPECT_EQ(std::next(cont.begin(), 2), it); - EXPECT_THAT(cont, ElementsAre(1, 2, 7, 8)); - - it = cont.erase(std::next(cont.cbegin(), 0), std::next(cont.cbegin(), 2)); - EXPECT_EQ(std::next(cont.begin(), 0), it); - EXPECT_THAT(cont, ElementsAre(7, 8)); - - it = cont.erase(cont.cbegin(), cont.cend()); - EXPECT_EQ(cont.begin(), it); - EXPECT_EQ(cont.end(), it); -} - -// size_type erase(const key_type& key) - -TEST(FlatTree, EraseKey) { - IntTree cont({1, 2, 3, 4, 5, 6, 7, 8}); - - EXPECT_EQ(0U, cont.erase(9)); - EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8)); - - EXPECT_EQ(1U, cont.erase(4)); - EXPECT_THAT(cont, ElementsAre(1, 2, 3, 5, 6, 7, 8)); - - EXPECT_EQ(1U, cont.erase(1)); - EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7, 8)); - - EXPECT_EQ(1U, cont.erase(8)); - EXPECT_THAT(cont, ElementsAre(2, 3, 5, 6, 7)); - - EXPECT_EQ(1U, cont.erase(3)); - EXPECT_THAT(cont, ElementsAre(2, 5, 6, 7)); - - EXPECT_EQ(1U, cont.erase(6)); - EXPECT_THAT(cont, ElementsAre(2, 5, 7)); - - EXPECT_EQ(1U, cont.erase(7)); - EXPECT_THAT(cont, ElementsAre(2, 5)); - - EXPECT_EQ(1U, cont.erase(2)); - EXPECT_THAT(cont, ElementsAre(5)); - - EXPECT_EQ(1U, cont.erase(5)); - EXPECT_THAT(cont, ElementsAre()); -} - -// ---------------------------------------------------------------------------- -// Comparators. - -// key_compare key_comp() const - -TEST(FlatTree, KeyComp) { - ReversedTree cont({1, 2, 3, 4, 5}); - - EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.key_comp())); - int new_elements[] = {6, 7, 8, 9, 10}; - std::copy(std::begin(new_elements), std::end(new_elements), - std::inserter(cont, cont.end())); - EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.key_comp())); -} - -// value_compare value_comp() const - -TEST(FlatTree, ValueComp) { - ReversedTree cont({1, 2, 3, 4, 5}); - - EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.value_comp())); - int new_elements[] = {6, 7, 8, 9, 10}; - std::copy(std::begin(new_elements), std::end(new_elements), - std::inserter(cont, cont.end())); - EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.value_comp())); -} - -// ---------------------------------------------------------------------------- -// Search operations. - -// size_type count(const key_type& key) const - -TEST(FlatTree, Count) { - const IntTree cont({5, 6, 7, 8, 9, 10, 11, 12}); - - EXPECT_EQ(1U, cont.count(5)); - EXPECT_EQ(1U, cont.count(6)); - EXPECT_EQ(1U, cont.count(7)); - EXPECT_EQ(1U, cont.count(8)); - EXPECT_EQ(1U, cont.count(9)); - EXPECT_EQ(1U, cont.count(10)); - EXPECT_EQ(1U, cont.count(11)); - EXPECT_EQ(1U, cont.count(12)); - EXPECT_EQ(0U, cont.count(4)); -} - -// iterator find(const key_type& key) -// const_iterator find(const key_type& key) const - -TEST(FlatTree, Find) { - { - IntTree cont({5, 6, 7, 8, 9, 10, 11, 12}); - - EXPECT_EQ(cont.begin(), cont.find(5)); - EXPECT_EQ(std::next(cont.begin()), cont.find(6)); - EXPECT_EQ(std::next(cont.begin(), 2), cont.find(7)); - EXPECT_EQ(std::next(cont.begin(), 3), cont.find(8)); - EXPECT_EQ(std::next(cont.begin(), 4), cont.find(9)); - EXPECT_EQ(std::next(cont.begin(), 5), cont.find(10)); - EXPECT_EQ(std::next(cont.begin(), 6), cont.find(11)); - EXPECT_EQ(std::next(cont.begin(), 7), cont.find(12)); - EXPECT_EQ(std::next(cont.begin(), 8), cont.find(4)); - } - { - const IntTree cont({5, 6, 7, 8, 9, 10, 11, 12}); - - EXPECT_EQ(cont.begin(), cont.find(5)); - EXPECT_EQ(std::next(cont.begin()), cont.find(6)); - EXPECT_EQ(std::next(cont.begin(), 2), cont.find(7)); - EXPECT_EQ(std::next(cont.begin(), 3), cont.find(8)); - EXPECT_EQ(std::next(cont.begin(), 4), cont.find(9)); - EXPECT_EQ(std::next(cont.begin(), 5), cont.find(10)); - EXPECT_EQ(std::next(cont.begin(), 6), cont.find(11)); - EXPECT_EQ(std::next(cont.begin(), 7), cont.find(12)); - EXPECT_EQ(std::next(cont.begin(), 8), cont.find(4)); - } -} - -// pair equal_range(const key_type& key) -// pair equal_range(const key_type& key) const - -TEST(FlatTree, EqualRange) { - { - IntTree cont({5, 7, 9, 11, 13, 15, 17, 19}); - - std::pair result = - cont.equal_range(5); - EXPECT_EQ(std::next(cont.begin(), 0), result.first); - EXPECT_EQ(std::next(cont.begin(), 1), result.second); - result = cont.equal_range(7); - EXPECT_EQ(std::next(cont.begin(), 1), result.first); - EXPECT_EQ(std::next(cont.begin(), 2), result.second); - result = cont.equal_range(9); - EXPECT_EQ(std::next(cont.begin(), 2), result.first); - EXPECT_EQ(std::next(cont.begin(), 3), result.second); - result = cont.equal_range(11); - EXPECT_EQ(std::next(cont.begin(), 3), result.first); - EXPECT_EQ(std::next(cont.begin(), 4), result.second); - result = cont.equal_range(13); - EXPECT_EQ(std::next(cont.begin(), 4), result.first); - EXPECT_EQ(std::next(cont.begin(), 5), result.second); - result = cont.equal_range(15); - EXPECT_EQ(std::next(cont.begin(), 5), result.first); - EXPECT_EQ(std::next(cont.begin(), 6), result.second); - result = cont.equal_range(17); - EXPECT_EQ(std::next(cont.begin(), 6), result.first); - EXPECT_EQ(std::next(cont.begin(), 7), result.second); - result = cont.equal_range(19); - EXPECT_EQ(std::next(cont.begin(), 7), result.first); - EXPECT_EQ(std::next(cont.begin(), 8), result.second); - result = cont.equal_range(4); - EXPECT_EQ(std::next(cont.begin(), 0), result.first); - EXPECT_EQ(std::next(cont.begin(), 0), result.second); - result = cont.equal_range(6); - EXPECT_EQ(std::next(cont.begin(), 1), result.first); - EXPECT_EQ(std::next(cont.begin(), 1), result.second); - result = cont.equal_range(8); - EXPECT_EQ(std::next(cont.begin(), 2), result.first); - EXPECT_EQ(std::next(cont.begin(), 2), result.second); - result = cont.equal_range(10); - EXPECT_EQ(std::next(cont.begin(), 3), result.first); - EXPECT_EQ(std::next(cont.begin(), 3), result.second); - result = cont.equal_range(12); - EXPECT_EQ(std::next(cont.begin(), 4), result.first); - EXPECT_EQ(std::next(cont.begin(), 4), result.second); - result = cont.equal_range(14); - EXPECT_EQ(std::next(cont.begin(), 5), result.first); - EXPECT_EQ(std::next(cont.begin(), 5), result.second); - result = cont.equal_range(16); - EXPECT_EQ(std::next(cont.begin(), 6), result.first); - EXPECT_EQ(std::next(cont.begin(), 6), result.second); - result = cont.equal_range(18); - EXPECT_EQ(std::next(cont.begin(), 7), result.first); - EXPECT_EQ(std::next(cont.begin(), 7), result.second); - result = cont.equal_range(20); - EXPECT_EQ(std::next(cont.begin(), 8), result.first); - EXPECT_EQ(std::next(cont.begin(), 8), result.second); - } - { - const IntTree cont({5, 7, 9, 11, 13, 15, 17, 19}); - - std::pair result = - cont.equal_range(5); - EXPECT_EQ(std::next(cont.begin(), 0), result.first); - EXPECT_EQ(std::next(cont.begin(), 1), result.second); - result = cont.equal_range(7); - EXPECT_EQ(std::next(cont.begin(), 1), result.first); - EXPECT_EQ(std::next(cont.begin(), 2), result.second); - result = cont.equal_range(9); - EXPECT_EQ(std::next(cont.begin(), 2), result.first); - EXPECT_EQ(std::next(cont.begin(), 3), result.second); - result = cont.equal_range(11); - EXPECT_EQ(std::next(cont.begin(), 3), result.first); - EXPECT_EQ(std::next(cont.begin(), 4), result.second); - result = cont.equal_range(13); - EXPECT_EQ(std::next(cont.begin(), 4), result.first); - EXPECT_EQ(std::next(cont.begin(), 5), result.second); - result = cont.equal_range(15); - EXPECT_EQ(std::next(cont.begin(), 5), result.first); - EXPECT_EQ(std::next(cont.begin(), 6), result.second); - result = cont.equal_range(17); - EXPECT_EQ(std::next(cont.begin(), 6), result.first); - EXPECT_EQ(std::next(cont.begin(), 7), result.second); - result = cont.equal_range(19); - EXPECT_EQ(std::next(cont.begin(), 7), result.first); - EXPECT_EQ(std::next(cont.begin(), 8), result.second); - result = cont.equal_range(4); - EXPECT_EQ(std::next(cont.begin(), 0), result.first); - EXPECT_EQ(std::next(cont.begin(), 0), result.second); - result = cont.equal_range(6); - EXPECT_EQ(std::next(cont.begin(), 1), result.first); - EXPECT_EQ(std::next(cont.begin(), 1), result.second); - result = cont.equal_range(8); - EXPECT_EQ(std::next(cont.begin(), 2), result.first); - EXPECT_EQ(std::next(cont.begin(), 2), result.second); - result = cont.equal_range(10); - EXPECT_EQ(std::next(cont.begin(), 3), result.first); - EXPECT_EQ(std::next(cont.begin(), 3), result.second); - result = cont.equal_range(12); - EXPECT_EQ(std::next(cont.begin(), 4), result.first); - EXPECT_EQ(std::next(cont.begin(), 4), result.second); - result = cont.equal_range(14); - EXPECT_EQ(std::next(cont.begin(), 5), result.first); - EXPECT_EQ(std::next(cont.begin(), 5), result.second); - result = cont.equal_range(16); - EXPECT_EQ(std::next(cont.begin(), 6), result.first); - EXPECT_EQ(std::next(cont.begin(), 6), result.second); - result = cont.equal_range(18); - EXPECT_EQ(std::next(cont.begin(), 7), result.first); - EXPECT_EQ(std::next(cont.begin(), 7), result.second); - result = cont.equal_range(20); - EXPECT_EQ(std::next(cont.begin(), 8), result.first); - EXPECT_EQ(std::next(cont.begin(), 8), result.second); - } -} - -// iterator lower_bound(const key_type& key); -// const_iterator lower_bound(const key_type& key) const; - -TEST(FlatTree, LowerBound) { - { - IntTree cont({5, 7, 9, 11, 13, 15, 17, 19}); - - EXPECT_EQ(cont.begin(), cont.lower_bound(5)); - EXPECT_EQ(std::next(cont.begin()), cont.lower_bound(7)); - EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(9)); - EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(11)); - EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(13)); - EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(15)); - EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(17)); - EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(19)); - EXPECT_EQ(std::next(cont.begin(), 0), cont.lower_bound(4)); - EXPECT_EQ(std::next(cont.begin(), 1), cont.lower_bound(6)); - EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(8)); - EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(10)); - EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(12)); - EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(14)); - EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(16)); - EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(18)); - EXPECT_EQ(std::next(cont.begin(), 8), cont.lower_bound(20)); - } - { - const IntTree cont({5, 7, 9, 11, 13, 15, 17, 19}); - - EXPECT_EQ(cont.begin(), cont.lower_bound(5)); - EXPECT_EQ(std::next(cont.begin()), cont.lower_bound(7)); - EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(9)); - EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(11)); - EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(13)); - EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(15)); - EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(17)); - EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(19)); - EXPECT_EQ(std::next(cont.begin(), 0), cont.lower_bound(4)); - EXPECT_EQ(std::next(cont.begin(), 1), cont.lower_bound(6)); - EXPECT_EQ(std::next(cont.begin(), 2), cont.lower_bound(8)); - EXPECT_EQ(std::next(cont.begin(), 3), cont.lower_bound(10)); - EXPECT_EQ(std::next(cont.begin(), 4), cont.lower_bound(12)); - EXPECT_EQ(std::next(cont.begin(), 5), cont.lower_bound(14)); - EXPECT_EQ(std::next(cont.begin(), 6), cont.lower_bound(16)); - EXPECT_EQ(std::next(cont.begin(), 7), cont.lower_bound(18)); - EXPECT_EQ(std::next(cont.begin(), 8), cont.lower_bound(20)); - } -} - -// iterator upper_bound(const key_type& key) -// const_iterator upper_bound(const key_type& key) const - -TEST(FlatTree, UpperBound) { - { - IntTree cont({5, 7, 9, 11, 13, 15, 17, 19}); - - EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(5)); - EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(7)); - EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(9)); - EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(11)); - EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(13)); - EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(15)); - EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(17)); - EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(19)); - EXPECT_EQ(std::next(cont.begin(), 0), cont.upper_bound(4)); - EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(6)); - EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(8)); - EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(10)); - EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(12)); - EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(14)); - EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(16)); - EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(18)); - EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(20)); - } - { - const IntTree cont({5, 7, 9, 11, 13, 15, 17, 19}); - - EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(5)); - EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(7)); - EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(9)); - EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(11)); - EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(13)); - EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(15)); - EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(17)); - EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(19)); - EXPECT_EQ(std::next(cont.begin(), 0), cont.upper_bound(4)); - EXPECT_EQ(std::next(cont.begin(), 1), cont.upper_bound(6)); - EXPECT_EQ(std::next(cont.begin(), 2), cont.upper_bound(8)); - EXPECT_EQ(std::next(cont.begin(), 3), cont.upper_bound(10)); - EXPECT_EQ(std::next(cont.begin(), 4), cont.upper_bound(12)); - EXPECT_EQ(std::next(cont.begin(), 5), cont.upper_bound(14)); - EXPECT_EQ(std::next(cont.begin(), 6), cont.upper_bound(16)); - EXPECT_EQ(std::next(cont.begin(), 7), cont.upper_bound(18)); - EXPECT_EQ(std::next(cont.begin(), 8), cont.upper_bound(20)); - } -} - -// ---------------------------------------------------------------------------- -// General operations. - -// void swap(flat_tree& other) -// void swap(flat_tree& lhs, flat_tree& rhs) - -TEST(FlatTreeOurs, Swap) { - IntTree x({1, 2, 3}); - IntTree y({4}); - swap(x, y); - EXPECT_THAT(x, ElementsAre(4)); - EXPECT_THAT(y, ElementsAre(1, 2, 3)); - - y.swap(x); - EXPECT_THAT(x, ElementsAre(1, 2, 3)); - EXPECT_THAT(y, ElementsAre(4)); -} - -// bool operator==(const flat_tree& lhs, const flat_tree& rhs) -// bool operator!=(const flat_tree& lhs, const flat_tree& rhs) -// bool operator<(const flat_tree& lhs, const flat_tree& rhs) -// bool operator>(const flat_tree& lhs, const flat_tree& rhs) -// bool operator<=(const flat_tree& lhs, const flat_tree& rhs) -// bool operator>=(const flat_tree& lhs, const flat_tree& rhs) - -TEST(FlatTree, Comparison) { - // Provided comparator does not participate in comparison. - ReversedTree biggest({3}); - ReversedTree smallest({1}); - ReversedTree middle({1, 2}); - - EXPECT_EQ(biggest, biggest); - EXPECT_NE(biggest, smallest); - EXPECT_LT(smallest, middle); - EXPECT_LE(smallest, middle); - EXPECT_LE(middle, middle); - EXPECT_GT(biggest, middle); - EXPECT_GE(biggest, middle); - EXPECT_GE(biggest, biggest); -} - -TEST(FlatSet, EraseIf) { - IntTree x; - EraseIf(x, [](int) { return false; }); - EXPECT_THAT(x, ElementsAre()); - - x = {1, 2, 3}; - EraseIf(x, [](int elem) { return !(elem & 1); }); - EXPECT_THAT(x, ElementsAre(1, 3)); - - x = {1, 2, 3, 4}; - EraseIf(x, [](int elem) { return elem & 1; }); - EXPECT_THAT(x, ElementsAre(2, 4)); -} - -} // namespace internal -} // namespace base diff --git a/containers/hash_tables.h b/containers/hash_tables.h deleted file mode 100644 index 8da7b6726..000000000 --- a/containers/hash_tables.h +++ /dev/null @@ -1,75 +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_CONTAINERS_HASH_TABLES_H_ -#define BASE_CONTAINERS_HASH_TABLES_H_ - -#include -#include -#include -#include - -#include "base/hash.h" - -// This header file is deprecated. Use the corresponding C++11 type -// instead. https://crbug.com/576864 - -// Use a custom hasher instead. -#define BASE_HASH_NAMESPACE base_hash - -namespace BASE_HASH_NAMESPACE { - -// A separate hasher which, by default, forwards to std::hash. This is so legacy -// uses of BASE_HASH_NAMESPACE with base::hash_map do not interfere with -// std::hash mid-transition. -template -struct hash { - std::size_t operator()(const T& value) const { return std::hash()(value); } -}; - -// Use base::IntPairHash from base/hash.h as a custom hasher instead. -template -struct hash> { - std::size_t operator()(std::pair value) const { - return base::HashInts(value.first, value.second); - } -}; - -} // namespace BASE_HASH_NAMESPACE - -namespace base { - -// Use std::unordered_map instead. -template , - class Pred = std::equal_to, - class Alloc = std::allocator>> -using hash_map = std::unordered_map; - -// Use std::unordered_multimap instead. -template , - class Pred = std::equal_to, - class Alloc = std::allocator>> -using hash_multimap = std::unordered_multimap; - -// Use std::unordered_multiset instead. -template , - class Pred = std::equal_to, - class Alloc = std::allocator> -using hash_multiset = std::unordered_multiset; - -// Use std::unordered_set instead. -template , - class Pred = std::equal_to, - class Alloc = std::allocator> -using hash_set = std::unordered_set; - -} // namespace base - -#endif // BASE_CONTAINERS_HASH_TABLES_H_ diff --git a/containers/hash_tables_unittest.cc b/containers/hash_tables_unittest.cc deleted file mode 100644 index 6072e5dc9..000000000 --- a/containers/hash_tables_unittest.cc +++ /dev/null @@ -1,67 +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/containers/hash_tables.h" - -#include -#include - -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -class HashPairTest : public testing::Test { -}; - -#define INSERT_PAIR_TEST(Type, value1, value2) \ - { \ - Type pair(value1, value2); \ - base::hash_map map; \ - map[pair] = 1; \ - } - -// Verify that a hash_map can be constructed for pairs of integers of various -// sizes. -TEST_F(HashPairTest, IntegerPairs) { - typedef std::pair Int16Int16Pair; - typedef std::pair Int16Int32Pair; - typedef std::pair Int16Int64Pair; - - INSERT_PAIR_TEST(Int16Int16Pair, 4, 6); - INSERT_PAIR_TEST(Int16Int32Pair, 9, (1 << 29) + 378128932); - INSERT_PAIR_TEST(Int16Int64Pair, 10, - (INT64_C(1) << 60) + INT64_C(78931732321)); - - typedef std::pair Int32Int16Pair; - typedef std::pair Int32Int32Pair; - typedef std::pair Int32Int64Pair; - - INSERT_PAIR_TEST(Int32Int16Pair, 4, 6); - INSERT_PAIR_TEST(Int32Int32Pair, 9, (1 << 29) + 378128932); - INSERT_PAIR_TEST(Int32Int64Pair, 10, - (INT64_C(1) << 60) + INT64_C(78931732321)); - - typedef std::pair Int64Int16Pair; - typedef std::pair Int64Int32Pair; - typedef std::pair Int64Int64Pair; - - INSERT_PAIR_TEST(Int64Int16Pair, 4, 6); - INSERT_PAIR_TEST(Int64Int32Pair, 9, (1 << 29) + 378128932); - INSERT_PAIR_TEST(Int64Int64Pair, 10, - (INT64_C(1) << 60) + INT64_C(78931732321)); -} - -// Verify that base::hash_set compares by pointer value, not as C -// strings. -TEST(HashTableTest, CharPointers) { - std::string str1("hello"); - std::string str2("hello"); - base::hash_set set; - - set.insert(str1.c_str()); - EXPECT_EQ(1u, set.count(str1.c_str())); - EXPECT_EQ(0u, set.count(str2.c_str())); -} - -} // namespace diff --git a/containers/id_map.h b/containers/id_map.h deleted file mode 100644 index 4c816da37..000000000 --- a/containers/id_map.h +++ /dev/null @@ -1,290 +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_CONTAINERS_ID_MAP_H_ -#define BASE_CONTAINERS_ID_MAP_H_ - -#include -#include - -#include -#include -#include -#include -#include - -#include "base/containers/flat_set.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/sequence_checker.h" - -namespace base { - -// This object maintains a list of IDs that can be quickly converted to -// pointers to objects. It is implemented as a hash table, optimized for -// relatively small data sets (in the common case, there will be exactly one -// item in the list). -// -// Items can be inserted into the container with arbitrary ID, but the caller -// must ensure they are unique. Inserting IDs and relying on automatically -// generated ones is not allowed because they can collide. - -// The map's value type (the V param) can be any dereferenceable type, such as a -// raw pointer or smart pointer -template -class IDMap final { - public: - using KeyType = K; - - private: - using T = typename std::remove_reference::type; - - using HashTable = std::unordered_map; - - public: - IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) { - // A number of consumers of IDMap create it on one thread but always - // access it from a different, but consistent, thread (or sequence) - // post-construction. The first call to CalledOnValidSequence() will re-bind - // it. - DETACH_FROM_SEQUENCE(sequence_checker_); - } - - ~IDMap() { - // Many IDMap's are static, and hence will be destroyed on the main - // thread. However, all the accesses may take place on another thread (or - // sequence), such as the IO thread. Detaching again to clean this up. - DETACH_FROM_SEQUENCE(sequence_checker_); - } - - // Sets whether Add and Replace should DCHECK if passed in NULL data. - // Default is false. - void set_check_on_null_data(bool value) { check_on_null_data_ = value; } - - // Adds a view with an automatically generated unique ID. See AddWithID. - KeyType Add(V data) { return AddInternal(std::move(data)); } - - // Adds a new data member with the specified ID. The ID must not be in - // the list. The caller either must generate all unique IDs itself and use - // this function, or allow this object to generate IDs and call Add. These - // two methods may not be mixed, or duplicate IDs may be generated. - void AddWithID(V data, KeyType id) { AddWithIDInternal(std::move(data), id); } - - void Remove(KeyType id) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - typename HashTable::iterator i = data_.find(id); - if (i == data_.end() || IsRemoved(id)) { - NOTREACHED() << "Attempting to remove an item not in the list"; - return; - } - - if (iteration_depth_ == 0) { - data_.erase(i); - } else { - removed_ids_.insert(id); - } - } - - // Replaces the value for |id| with |new_data| and returns the existing value. - // Should only be called with an already added id. - V Replace(KeyType id, V new_data) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!check_on_null_data_ || new_data); - typename HashTable::iterator i = data_.find(id); - DCHECK(i != data_.end()); - DCHECK(!IsRemoved(id)); - - using std::swap; - swap(i->second, new_data); - return new_data; - } - - void Clear() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (iteration_depth_ == 0) { - data_.clear(); - } else { - removed_ids_.reserve(data_.size()); - removed_ids_.insert(KeyIterator(data_.begin()), KeyIterator(data_.end())); - } - } - - bool IsEmpty() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return size() == 0u; - } - - T* Lookup(KeyType id) const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - typename HashTable::const_iterator i = data_.find(id); - if (i == data_.end() || !i->second || IsRemoved(id)) - return nullptr; - return &*i->second; - } - - size_t size() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return data_.size() - removed_ids_.size(); - } - -#if defined(UNIT_TEST) - int iteration_depth() const { - return iteration_depth_; - } -#endif // defined(UNIT_TEST) - - // It is safe to remove elements from the map during iteration. All iterators - // will remain valid. - template - class Iterator { - public: - Iterator(IDMap* map) : map_(map), iter_(map_->data_.begin()) { - Init(); - } - - Iterator(const Iterator& iter) - : map_(iter.map_), - iter_(iter.iter_) { - Init(); - } - - const Iterator& operator=(const Iterator& iter) { - map_ = iter.map; - iter_ = iter.iter; - Init(); - return *this; - } - - ~Iterator() { - DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); - - // We're going to decrement iteration depth. Make sure it's greater than - // zero so that it doesn't become negative. - DCHECK_LT(0, map_->iteration_depth_); - - if (--map_->iteration_depth_ == 0) - map_->Compact(); - } - - bool IsAtEnd() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); - return iter_ == map_->data_.end(); - } - - KeyType GetCurrentKey() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); - return iter_->first; - } - - ReturnType* GetCurrentValue() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); - if (!iter_->second || map_->IsRemoved(iter_->first)) - return nullptr; - return &*iter_->second; - } - - void Advance() { - DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); - ++iter_; - SkipRemovedEntries(); - } - - private: - void Init() { - DCHECK_CALLED_ON_VALID_SEQUENCE(map_->sequence_checker_); - ++map_->iteration_depth_; - SkipRemovedEntries(); - } - - void SkipRemovedEntries() { - while (iter_ != map_->data_.end() && map_->IsRemoved(iter_->first)) - ++iter_; - } - - IDMap* map_; - typename HashTable::const_iterator iter_; - }; - - typedef Iterator iterator; - typedef Iterator const_iterator; - - private: - // Transforms a map iterator to an iterator on the keys of the map. - // Used by Clear() to populate |removed_ids_| in bulk. - struct KeyIterator : std::iterator { - using inner_iterator = typename HashTable::iterator; - inner_iterator iter_; - - KeyIterator(inner_iterator iter) : iter_(iter) {} - KeyType operator*() const { return iter_->first; } - KeyIterator& operator++() { - ++iter_; - return *this; - } - KeyIterator operator++(int) { return KeyIterator(iter_++); } - bool operator==(const KeyIterator& other) const { - return iter_ == other.iter_; - } - bool operator!=(const KeyIterator& other) const { - return iter_ != other.iter_; - } - }; - - KeyType AddInternal(V data) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!check_on_null_data_ || data); - KeyType this_id = next_id_; - DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item"; - data_[this_id] = std::move(data); - next_id_++; - return this_id; - } - - void AddWithIDInternal(V data, KeyType id) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!check_on_null_data_ || data); - if (IsRemoved(id)) { - removed_ids_.erase(id); - } else { - DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item"; - } - data_[id] = std::move(data); - } - - bool IsRemoved(KeyType key) const { - return removed_ids_.find(key) != removed_ids_.end(); - } - - void Compact() { - DCHECK_EQ(0, iteration_depth_); - for (const auto& i : removed_ids_) - data_.erase(i); - removed_ids_.clear(); - } - - // Keep track of how many iterators are currently iterating on us to safely - // handle removing items during iteration. - int iteration_depth_; - - // Keep set of IDs that should be removed after the outermost iteration has - // finished. This way we manage to not invalidate the iterator when an element - // is removed. - base::flat_set removed_ids_; - - // The next ID that we will return from Add() - KeyType next_id_; - - HashTable data_; - - // See description above setter. - bool check_on_null_data_; - - SEQUENCE_CHECKER(sequence_checker_); - - DISALLOW_COPY_AND_ASSIGN(IDMap); -}; - -} // namespace base - -#endif // BASE_CONTAINERS_ID_MAP_H_ diff --git a/containers/id_map_unittest.cc b/containers/id_map_unittest.cc deleted file mode 100644 index 346b69f2b..000000000 --- a/containers/id_map_unittest.cc +++ /dev/null @@ -1,399 +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/containers/id_map.h" - -#include - -#include - -#include "base/test/gtest_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -class TestObject { -}; - -class DestructorCounter { - public: - explicit DestructorCounter(int* counter) : counter_(counter) {} - ~DestructorCounter() { ++(*counter_); } - - private: - int* counter_; -}; - -} // namespace - -TEST(IDMapTest, Basic) { - IDMap map; - EXPECT_TRUE(map.IsEmpty()); - EXPECT_EQ(0U, map.size()); - - TestObject obj1; - TestObject obj2; - - int32_t id1 = map.Add(&obj1); - EXPECT_FALSE(map.IsEmpty()); - EXPECT_EQ(1U, map.size()); - EXPECT_EQ(&obj1, map.Lookup(id1)); - - int32_t id2 = map.Add(&obj2); - EXPECT_FALSE(map.IsEmpty()); - EXPECT_EQ(2U, map.size()); - - EXPECT_EQ(&obj1, map.Lookup(id1)); - EXPECT_EQ(&obj2, map.Lookup(id2)); - - map.Remove(id1); - EXPECT_FALSE(map.IsEmpty()); - EXPECT_EQ(1U, map.size()); - - map.Remove(id2); - EXPECT_TRUE(map.IsEmpty()); - EXPECT_EQ(0U, map.size()); - - map.AddWithID(&obj1, 1); - map.AddWithID(&obj2, 2); - EXPECT_EQ(&obj1, map.Lookup(1)); - EXPECT_EQ(&obj2, map.Lookup(2)); - - EXPECT_EQ(&obj2, map.Replace(2, &obj1)); - EXPECT_EQ(&obj1, map.Lookup(2)); - - EXPECT_EQ(0, map.iteration_depth()); -} - -TEST(IDMapTest, IteratorRemainsValidWhenRemovingCurrentElement) { - IDMap map; - - TestObject obj1; - TestObject obj2; - TestObject obj3; - - map.Add(&obj1); - map.Add(&obj2); - map.Add(&obj3); - - { - IDMap::const_iterator iter(&map); - - EXPECT_EQ(1, map.iteration_depth()); - - while (!iter.IsAtEnd()) { - map.Remove(iter.GetCurrentKey()); - iter.Advance(); - } - - // Test that while an iterator is still in scope, we get the map emptiness - // right (http://crbug.com/35571). - EXPECT_TRUE(map.IsEmpty()); - EXPECT_EQ(0U, map.size()); - } - - EXPECT_TRUE(map.IsEmpty()); - EXPECT_EQ(0U, map.size()); - - EXPECT_EQ(0, map.iteration_depth()); -} - -TEST(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) { - IDMap map; - - const int kCount = 5; - TestObject obj[kCount]; - - for (int i = 0; i < kCount; i++) - map.Add(&obj[i]); - - // IDMap has no predictable iteration order. - int32_t ids_in_iteration_order[kCount]; - const TestObject* objs_in_iteration_order[kCount]; - int counter = 0; - for (IDMap::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::const_iterator iter(&map); !iter.IsAtEnd(); - iter.Advance()) { - EXPECT_EQ(1, map.iteration_depth()); - - switch (counter) { - case 0: - 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_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_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"; - break; - } - - counter++; - } - - EXPECT_EQ(0, map.iteration_depth()); -} - -TEST(IDMapTest, CopyIterator) { - IDMap map; - - TestObject obj1; - TestObject obj2; - TestObject obj3; - - map.Add(&obj1); - map.Add(&obj2); - map.Add(&obj3); - - EXPECT_EQ(0, map.iteration_depth()); - - { - IDMap::const_iterator iter1(&map); - EXPECT_EQ(1, map.iteration_depth()); - - // Make sure that copying the iterator correctly increments - // map's iteration depth. - IDMap::const_iterator iter2(iter1); - EXPECT_EQ(2, map.iteration_depth()); - } - - // Make sure after destroying all iterators the map's iteration depth - // returns to initial state. - EXPECT_EQ(0, map.iteration_depth()); -} - -TEST(IDMapTest, AssignIterator) { - IDMap map; - - TestObject obj1; - TestObject obj2; - TestObject obj3; - - map.Add(&obj1); - map.Add(&obj2); - map.Add(&obj3); - - EXPECT_EQ(0, map.iteration_depth()); - - { - IDMap::const_iterator iter1(&map); - EXPECT_EQ(1, map.iteration_depth()); - - IDMap::const_iterator iter2(&map); - EXPECT_EQ(2, map.iteration_depth()); - - // Make sure that assigning the iterator correctly updates - // map's iteration depth (-1 for destruction, +1 for assignment). - EXPECT_EQ(2, map.iteration_depth()); - } - - // Make sure after destroying all iterators the map's iteration depth - // returns to initial state. - EXPECT_EQ(0, map.iteration_depth()); -} - -TEST(IDMapTest, IteratorRemainsValidWhenClearing) { - IDMap map; - - const int kCount = 5; - TestObject obj[kCount]; - - for (int i = 0; i < kCount; i++) - map.Add(&obj[i]); - - // IDMap has no predictable iteration order. - int32_t ids_in_iteration_order[kCount]; - const TestObject* objs_in_iteration_order[kCount]; - int counter = 0; - for (IDMap::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::const_iterator iter(&map); !iter.IsAtEnd(); - iter.Advance()) { - switch (counter) { - case 0: - EXPECT_EQ(ids_in_iteration_order[0], iter.GetCurrentKey()); - EXPECT_EQ(objs_in_iteration_order[0], iter.GetCurrentValue()); - break; - case 1: - 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()); - break; - default: - FAIL() << "should not have that many elements"; - break; - } - counter++; - } - - EXPECT_TRUE(map.IsEmpty()); - EXPECT_EQ(0U, map.size()); -} - -TEST(IDMapTest, OwningPointersDeletesThemOnRemove) { - const int kCount = 3; - - int external_del_count = 0; - DestructorCounter* external_obj[kCount]; - int map_external_ids[kCount]; - - int owned_del_count = 0; - int map_owned_ids[kCount]; - - IDMap map_external; - IDMap> map_owned; - - for (int i = 0; i < kCount; ++i) { - external_obj[i] = new DestructorCounter(&external_del_count); - map_external_ids[i] = map_external.Add(external_obj[i]); - - map_owned_ids[i] = - map_owned.Add(std::make_unique(&owned_del_count)); - } - - for (int i = 0; i < kCount; ++i) { - EXPECT_EQ(external_del_count, 0); - EXPECT_EQ(owned_del_count, i); - - map_external.Remove(map_external_ids[i]); - map_owned.Remove(map_owned_ids[i]); - } - - for (int i = 0; i < kCount; ++i) { - delete external_obj[i]; - } - - EXPECT_EQ(external_del_count, kCount); - EXPECT_EQ(owned_del_count, kCount); -} - -TEST(IDMapTest, OwningPointersDeletesThemOnClear) { - const int kCount = 3; - - int external_del_count = 0; - DestructorCounter* external_obj[kCount]; - - int owned_del_count = 0; - - IDMap map_external; - IDMap> map_owned; - - for (int i = 0; i < kCount; ++i) { - external_obj[i] = new DestructorCounter(&external_del_count); - map_external.Add(external_obj[i]); - - map_owned.Add(std::make_unique(&owned_del_count)); - } - - EXPECT_EQ(external_del_count, 0); - EXPECT_EQ(owned_del_count, 0); - - map_external.Clear(); - map_owned.Clear(); - - EXPECT_EQ(external_del_count, 0); - EXPECT_EQ(owned_del_count, kCount); - - for (int i = 0; i < kCount; ++i) { - delete external_obj[i]; - } - - EXPECT_EQ(external_del_count, kCount); - EXPECT_EQ(owned_del_count, kCount); -} - -TEST(IDMapTest, OwningPointersDeletesThemOnDestruct) { - const int kCount = 3; - - int external_del_count = 0; - DestructorCounter* external_obj[kCount]; - - int owned_del_count = 0; - - { - IDMap map_external; - IDMap> map_owned; - - for (int i = 0; i < kCount; ++i) { - external_obj[i] = new DestructorCounter(&external_del_count); - map_external.Add(external_obj[i]); - - map_owned.Add(std::make_unique(&owned_del_count)); - } - } - - EXPECT_EQ(external_del_count, 0); - - for (int i = 0; i < kCount; ++i) { - delete external_obj[i]; - } - - EXPECT_EQ(external_del_count, kCount); - EXPECT_EQ(owned_del_count, kCount); -} - -TEST(IDMapTest, Int64KeyType) { - IDMap map; - TestObject obj1; - const int64_t kId1 = 999999999999999999; - - map.AddWithID(&obj1, kId1); - EXPECT_EQ(&obj1, map.Lookup(kId1)); - - IDMap::const_iterator iter(&map); - ASSERT_FALSE(iter.IsAtEnd()); - EXPECT_EQ(kId1, iter.GetCurrentKey()); - EXPECT_EQ(&obj1, iter.GetCurrentValue()); - iter.Advance(); - ASSERT_TRUE(iter.IsAtEnd()); - - map.Remove(kId1); - EXPECT_TRUE(map.IsEmpty()); -} - -TEST(IDMapTest, RemovedValueHandling) { - TestObject obj; - IDMap map; - int key = map.Add(&obj); - - IDMap::iterator itr(&map); - map.Clear(); - EXPECT_DCHECK_DEATH(map.Remove(key)); - EXPECT_DCHECK_DEATH(map.Replace(key, &obj)); - EXPECT_FALSE(map.Lookup(key)); - EXPECT_FALSE(itr.IsAtEnd()); - EXPECT_FALSE(itr.GetCurrentValue()); - - EXPECT_TRUE(map.IsEmpty()); - map.AddWithID(&obj, key); - EXPECT_EQ(1u, map.size()); -} - -} // namespace base diff --git a/containers/linked_list.h b/containers/linked_list.h deleted file mode 100644 index a913badb8..000000000 --- a/containers/linked_list.h +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_LINKED_LIST_H_ -#define BASE_CONTAINERS_LINKED_LIST_H_ - -#include "base/macros.h" - -// Simple LinkedList type. (See the Q&A section to understand how this -// differs from std::list). -// -// To use, start by declaring the class which will be contained in the linked -// list, as extending LinkNode (this gives it next/previous pointers). -// -// class MyNodeType : public LinkNode { -// ... -// }; -// -// Next, to keep track of the list's head/tail, use a LinkedList instance: -// -// LinkedList list; -// -// To add elements to the list, use any of LinkedList::Append, -// LinkNode::InsertBefore, or LinkNode::InsertAfter: -// -// LinkNode* n1 = ...; -// LinkNode* n2 = ...; -// LinkNode* n3 = ...; -// -// list.Append(n1); -// list.Append(n3); -// n3->InsertBefore(n3); -// -// Lastly, to iterate through the linked list forwards: -// -// for (LinkNode* node = list.head(); -// node != list.end(); -// node = node->next()) { -// MyNodeType* value = node->value(); -// ... -// } -// -// Or to iterate the linked list backwards: -// -// for (LinkNode* node = list.tail(); -// node != list.end(); -// node = node->previous()) { -// MyNodeType* value = node->value(); -// ... -// } -// -// Questions and Answers: -// -// Q. Should I use std::list or base::LinkedList? -// -// A. The main reason to use base::LinkedList over std::list is -// performance. If you don't care about the performance differences -// then use an STL container, as it makes for better code readability. -// -// Comparing the performance of base::LinkedList to std::list: -// -// * Erasing an element of type T* from base::LinkedList is -// an O(1) operation. Whereas for std::list it is O(n). -// That is because with std::list you must obtain an -// iterator to the T* element before you can call erase(iterator). -// -// * Insertion operations with base::LinkedList never require -// heap allocations. -// -// Q. How does base::LinkedList implementation differ from std::list? -// -// A. Doubly-linked lists are made up of nodes that contain "next" and -// "previous" pointers that reference other nodes in the list. -// -// With base::LinkedList, the type being inserted already reserves -// space for the "next" and "previous" pointers (base::LinkNode*). -// Whereas with std::list the type can be anything, so the implementation -// needs to glue on the "next" and "previous" pointers using -// some internal node type. - -namespace base { - -template -class LinkNode { - public: - LinkNode() : previous_(nullptr), next_(nullptr) {} - LinkNode(LinkNode* previous, LinkNode* next) - : previous_(previous), next_(next) {} - - LinkNode(LinkNode&& rhs) { - next_ = rhs.next_; - rhs.next_ = nullptr; - previous_ = rhs.previous_; - rhs.previous_ = nullptr; - - // If the node belongs to a list, next_ and previous_ are both non-null. - // Otherwise, they are both null. - if (next_) { - next_->previous_ = this; - previous_->next_ = this; - } - } - - // Insert |this| into the linked list, before |e|. - void InsertBefore(LinkNode* e) { - this->next_ = e; - this->previous_ = e->previous_; - e->previous_->next_ = this; - e->previous_ = this; - } - - // Insert |this| into the linked list, after |e|. - void InsertAfter(LinkNode* e) { - this->next_ = e->next_; - this->previous_ = e; - e->next_->previous_ = this; - e->next_ = this; - } - - // Remove |this| from the linked list. - void RemoveFromList() { - this->previous_->next_ = this->next_; - this->next_->previous_ = this->previous_; - // next() and previous() return non-null if and only this node is not in any - // list. - this->next_ = nullptr; - this->previous_ = nullptr; - } - - LinkNode* previous() const { - return previous_; - } - - LinkNode* next() const { - return next_; - } - - // Cast from the node-type to the value type. - const T* value() const { - return static_cast(this); - } - - T* value() { - return static_cast(this); - } - - private: - LinkNode* previous_; - LinkNode* next_; - - DISALLOW_COPY_AND_ASSIGN(LinkNode); -}; - -template -class LinkedList { - public: - // The "root" node is self-referential, and forms the basis of a circular - // list (root_.next() will point back to the start of the list, - // and root_->previous() wraps around to the end of the list). - LinkedList() : root_(&root_, &root_) {} - - // Appends |e| to the end of the linked list. - void Append(LinkNode* e) { - e->InsertBefore(&root_); - } - - LinkNode* head() const { - return root_.next(); - } - - LinkNode* tail() const { - return root_.previous(); - } - - const LinkNode* end() const { - return &root_; - } - - bool empty() const { return head() == end(); } - - private: - LinkNode root_; - - DISALLOW_COPY_AND_ASSIGN(LinkedList); -}; - -} // namespace base - -#endif // BASE_CONTAINERS_LINKED_LIST_H_ diff --git a/containers/linked_list_unittest.cc b/containers/linked_list_unittest.cc deleted file mode 100644 index 8e547ba3f..000000000 --- a/containers/linked_list_unittest.cc +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/containers/linked_list.h" -#include "base/macros.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -class Node : public LinkNode { - public: - explicit Node(int id) : id_(id) {} - - int id() const { return id_; } - - private: - int id_; -}; - -class MultipleInheritanceNodeBase { - public: - MultipleInheritanceNodeBase() : field_taking_up_space_(0) {} - int field_taking_up_space_; -}; - -class MultipleInheritanceNode : public MultipleInheritanceNodeBase, - public LinkNode { - public: - MultipleInheritanceNode() = default; -}; - -class MovableNode : public LinkNode { - public: - explicit MovableNode(int id) : id_(id) {} - - MovableNode(MovableNode&&) = default; - - int id() const { return id_; } - - private: - int id_; -}; - -// Checks that when iterating |list| (either from head to tail, or from -// tail to head, as determined by |forward|), we get back |node_ids|, -// which is an array of size |num_nodes|. -void ExpectListContentsForDirection(const LinkedList& list, - int num_nodes, const int* node_ids, bool forward) { - int i = 0; - for (const LinkNode* node = (forward ? list.head() : list.tail()); - node != list.end(); - node = (forward ? node->next() : node->previous())) { - ASSERT_LT(i, num_nodes); - int index_of_id = forward ? i : num_nodes - i - 1; - EXPECT_EQ(node_ids[index_of_id], node->value()->id()); - ++i; - } - EXPECT_EQ(num_nodes, i); -} - -void ExpectListContents(const LinkedList& list, - int num_nodes, - const int* node_ids) { - { - SCOPED_TRACE("Iterating forward (from head to tail)"); - ExpectListContentsForDirection(list, num_nodes, node_ids, true); - } - { - SCOPED_TRACE("Iterating backward (from tail to head)"); - ExpectListContentsForDirection(list, num_nodes, node_ids, false); - } -} - -TEST(LinkedList, Empty) { - LinkedList list; - EXPECT_EQ(list.end(), list.head()); - EXPECT_EQ(list.end(), list.tail()); - ExpectListContents(list, 0, nullptr); -} - -TEST(LinkedList, Append) { - LinkedList list; - ExpectListContents(list, 0, nullptr); - - Node n1(1); - list.Append(&n1); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n1, list.tail()); - { - const int expected[] = {1}; - ExpectListContents(list, arraysize(expected), expected); - } - - Node n2(2); - list.Append(&n2); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n2, list.tail()); - { - const int expected[] = {1, 2}; - ExpectListContents(list, arraysize(expected), expected); - } - - Node n3(3); - list.Append(&n3); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n3, list.tail()); - { - const int expected[] = {1, 2, 3}; - ExpectListContents(list, arraysize(expected), expected); - } -} - -TEST(LinkedList, RemoveFromList) { - LinkedList list; - - Node n1(1); - Node n2(2); - Node n3(3); - Node n4(4); - Node n5(5); - - list.Append(&n1); - list.Append(&n2); - list.Append(&n3); - list.Append(&n4); - list.Append(&n5); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n5, list.tail()); - { - const int expected[] = {1, 2, 3, 4, 5}; - ExpectListContents(list, arraysize(expected), expected); - } - - // Remove from the middle. - n3.RemoveFromList(); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n5, list.tail()); - { - const int expected[] = {1, 2, 4, 5}; - ExpectListContents(list, arraysize(expected), expected); - } - - // Remove from the tail. - n5.RemoveFromList(); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n4, list.tail()); - { - const int expected[] = {1, 2, 4}; - ExpectListContents(list, arraysize(expected), expected); - } - - // Remove from the head. - n1.RemoveFromList(); - - EXPECT_EQ(&n2, list.head()); - EXPECT_EQ(&n4, list.tail()); - { - const int expected[] = {2, 4}; - ExpectListContents(list, arraysize(expected), expected); - } - - // Empty the list. - n2.RemoveFromList(); - n4.RemoveFromList(); - - ExpectListContents(list, 0, nullptr); - EXPECT_EQ(list.end(), list.head()); - EXPECT_EQ(list.end(), list.tail()); - - // Fill the list once again. - list.Append(&n1); - list.Append(&n2); - list.Append(&n3); - list.Append(&n4); - list.Append(&n5); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n5, list.tail()); - { - const int expected[] = {1, 2, 3, 4, 5}; - ExpectListContents(list, arraysize(expected), expected); - } -} - -TEST(LinkedList, InsertBefore) { - LinkedList list; - - Node n1(1); - Node n2(2); - Node n3(3); - Node n4(4); - - list.Append(&n1); - list.Append(&n2); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n2, list.tail()); - { - const int expected[] = {1, 2}; - ExpectListContents(list, arraysize(expected), expected); - } - - n3.InsertBefore(&n2); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n2, list.tail()); - { - const int expected[] = {1, 3, 2}; - ExpectListContents(list, arraysize(expected), expected); - } - - n4.InsertBefore(&n1); - - EXPECT_EQ(&n4, list.head()); - EXPECT_EQ(&n2, list.tail()); - { - const int expected[] = {4, 1, 3, 2}; - ExpectListContents(list, arraysize(expected), expected); - } -} - -TEST(LinkedList, InsertAfter) { - LinkedList list; - - Node n1(1); - Node n2(2); - Node n3(3); - Node n4(4); - - list.Append(&n1); - list.Append(&n2); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n2, list.tail()); - { - const int expected[] = {1, 2}; - ExpectListContents(list, arraysize(expected), expected); - } - - n3.InsertAfter(&n2); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n3, list.tail()); - { - const int expected[] = {1, 2, 3}; - ExpectListContents(list, arraysize(expected), expected); - } - - n4.InsertAfter(&n1); - - EXPECT_EQ(&n1, list.head()); - EXPECT_EQ(&n3, list.tail()); - { - const int expected[] = {1, 4, 2, 3}; - ExpectListContents(list, arraysize(expected), expected); - } -} - -TEST(LinkedList, MultipleInheritanceNode) { - MultipleInheritanceNode node; - EXPECT_EQ(&node, node.value()); -} - -TEST(LinkedList, EmptyListIsEmpty) { - LinkedList list; - EXPECT_TRUE(list.empty()); -} - -TEST(LinkedList, NonEmptyListIsNotEmpty) { - LinkedList list; - - Node n(1); - list.Append(&n); - - EXPECT_FALSE(list.empty()); -} - -TEST(LinkedList, EmptiedListIsEmptyAgain) { - LinkedList list; - - Node n(1); - list.Append(&n); - n.RemoveFromList(); - - EXPECT_TRUE(list.empty()); -} - -TEST(LinkedList, NodesCanBeReused) { - LinkedList list1; - LinkedList list2; - - Node n(1); - list1.Append(&n); - n.RemoveFromList(); - list2.Append(&n); - - EXPECT_EQ(list2.head()->value(), &n); -} - -TEST(LinkedList, RemovedNodeHasNullNextPrevious) { - LinkedList list; - - Node n(1); - list.Append(&n); - n.RemoveFromList(); - - EXPECT_EQ(nullptr, n.next()); - EXPECT_EQ(nullptr, n.previous()); -} - -TEST(LinkedList, NodeMoveConstructor) { - LinkedList list; - - MovableNode n1(1); - MovableNode n2(2); - MovableNode n3(3); - - list.Append(&n1); - list.Append(&n2); - list.Append(&n3); - - EXPECT_EQ(&n1, n2.previous()); - EXPECT_EQ(&n2, n1.next()); - EXPECT_EQ(&n3, n2.next()); - EXPECT_EQ(&n2, n3.previous()); - EXPECT_EQ(2, n2.id()); - - MovableNode n2_new(std::move(n2)); - - EXPECT_EQ(nullptr, n2.next()); - EXPECT_EQ(nullptr, n2.previous()); - - EXPECT_EQ(&n1, n2_new.previous()); - EXPECT_EQ(&n2_new, n1.next()); - EXPECT_EQ(&n3, n2_new.next()); - EXPECT_EQ(&n2_new, n3.previous()); - EXPECT_EQ(2, n2_new.id()); -} - -} // namespace -} // namespace base diff --git a/containers/mru_cache.h b/containers/mru_cache.h deleted file mode 100644 index 4a9f44e86..000000000 --- a/containers/mru_cache.h +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains a template for a Most Recently Used cache that allows -// constant-time access to items using a key, but easy identification of the -// least-recently-used items for removal. Each key can only be associated with -// one payload item at a time. -// -// The key object will be stored twice, so it should support efficient copying. -// -// NOTE: While all operations are O(1), this code is written for -// legibility rather than optimality. If future profiling identifies this as -// a bottleneck, there is room for smaller values of 1 in the O(1). :] - -#ifndef BASE_CONTAINERS_MRU_CACHE_H_ -#define BASE_CONTAINERS_MRU_CACHE_H_ - -#include - -#include -#include -#include -#include -#include -#include - -#include "base/logging.h" -#include "base/macros.h" - -namespace base { -namespace trace_event { -namespace internal { - -template -size_t DoEstimateMemoryUsageForMruCache(const MruCacheType&); - -} // namespace internal -} // namespace trace_event - -// MRUCacheBase ---------------------------------------------------------------- - -// This template is used to standardize map type containers that can be used -// by MRUCacheBase. This level of indirection is necessary because of the way -// that template template params and default template params interact. -template -struct MRUCacheStandardMap { - typedef std::map Type; -}; - -// Base class for the MRU cache specializations defined below. -template class MapType = - MRUCacheStandardMap> -class MRUCacheBase { - public: - // The payload of the list. This maintains a copy of the key so we can - // efficiently delete things given an element of the list. - typedef std::pair value_type; - - private: - typedef std::list PayloadList; - typedef typename MapType::Type KeyIndex; - - public: - typedef typename PayloadList::size_type size_type; - - typedef typename PayloadList::iterator iterator; - typedef typename PayloadList::const_iterator const_iterator; - typedef typename PayloadList::reverse_iterator reverse_iterator; - typedef typename PayloadList::const_reverse_iterator const_reverse_iterator; - - enum { NO_AUTO_EVICT = 0 }; - - // The max_size is the size at which the cache will prune its members to when - // a new item is inserted. If the caller wants to manager this itself (for - // example, maybe it has special work to do when something is evicted), it - // can pass NO_AUTO_EVICT to not restrict the cache size. - explicit MRUCacheBase(size_type max_size) : max_size_(max_size) {} - - virtual ~MRUCacheBase() = default; - - size_type max_size() const { return max_size_; } - - // Inserts a payload item with the given key. If an existing item has - // the same key, it is removed prior to insertion. An iterator indicating the - // inserted item will be returned (this will always be the front of the list). - // - // The payload will be forwarded. - template - iterator Put(const KeyType& key, Payload&& payload) { - // Remove any existing payload with that key. - typename KeyIndex::iterator index_iter = index_.find(key); - if (index_iter != index_.end()) { - // Erase the reference to it. The index reference will be replaced in the - // code below. - Erase(index_iter->second); - } else if (max_size_ != NO_AUTO_EVICT) { - // New item is being inserted which might make it larger than the maximum - // size: kick the oldest thing out if necessary. - ShrinkToSize(max_size_ - 1); - } - - ordering_.emplace_front(key, std::forward(payload)); - index_.emplace(key, ordering_.begin()); - return ordering_.begin(); - } - - // Retrieves the contents of the given key, or end() if not found. This method - // has the side effect of moving the requested item to the front of the - // recency list. - iterator Get(const KeyType& key) { - typename KeyIndex::iterator index_iter = index_.find(key); - if (index_iter == index_.end()) - return end(); - typename PayloadList::iterator iter = index_iter->second; - - // Move the touched item to the front of the recency ordering. - ordering_.splice(ordering_.begin(), ordering_, iter); - return ordering_.begin(); - } - - // Retrieves the payload associated with a given key and returns it via - // result without affecting the ordering (unlike Get). - iterator Peek(const KeyType& key) { - typename KeyIndex::const_iterator index_iter = index_.find(key); - if (index_iter == index_.end()) - return end(); - return index_iter->second; - } - - const_iterator Peek(const KeyType& key) const { - typename KeyIndex::const_iterator index_iter = index_.find(key); - if (index_iter == index_.end()) - return end(); - return index_iter->second; - } - - // Exchanges the contents of |this| by the contents of the |other|. - void Swap(MRUCacheBase& other) { - ordering_.swap(other.ordering_); - index_.swap(other.index_); - std::swap(max_size_, other.max_size_); - } - - // Erases the item referenced by the given iterator. An iterator to the item - // following it will be returned. The iterator must be valid. - iterator Erase(iterator pos) { - index_.erase(pos->first); - return ordering_.erase(pos); - } - - // MRUCache entries are often processed in reverse order, so we add this - // convenience function (not typically defined by STL containers). - reverse_iterator Erase(reverse_iterator pos) { - // We have to actually give it the incremented iterator to delete, since - // the forward iterator that base() returns is actually one past the item - // being iterated over. - return reverse_iterator(Erase((++pos).base())); - } - - // Shrinks the cache so it only holds |new_size| items. If |new_size| is - // bigger or equal to the current number of items, this will do nothing. - void ShrinkToSize(size_type new_size) { - for (size_type i = size(); i > new_size; i--) - Erase(rbegin()); - } - - // Deletes everything from the cache. - void Clear() { - index_.clear(); - ordering_.clear(); - } - - // Returns the number of elements in the cache. - size_type size() const { - // We don't use ordering_.size() for the return value because - // (as a linked list) it can be O(n). - DCHECK(index_.size() == ordering_.size()); - return index_.size(); - } - - // Allows iteration over the list. Forward iteration starts with the most - // recent item and works backwards. - // - // Note that since these iterators are actually iterators over a list, you - // can keep them as you insert or delete things (as long as you don't delete - // the one you are pointing to) and they will still be valid. - iterator begin() { return ordering_.begin(); } - const_iterator begin() const { return ordering_.begin(); } - iterator end() { return ordering_.end(); } - const_iterator end() const { return ordering_.end(); } - - reverse_iterator rbegin() { return ordering_.rbegin(); } - const_reverse_iterator rbegin() const { return ordering_.rbegin(); } - reverse_iterator rend() { return ordering_.rend(); } - const_reverse_iterator rend() const { return ordering_.rend(); } - - bool empty() const { return ordering_.empty(); } - - private: - template - friend size_t trace_event::internal::DoEstimateMemoryUsageForMruCache( - const MruCacheType&); - - PayloadList ordering_; - KeyIndex index_; - - size_type max_size_; - - DISALLOW_COPY_AND_ASSIGN(MRUCacheBase); -}; - -// MRUCache -------------------------------------------------------------------- - -// A container that does not do anything to free its data. Use this when storing -// value types (as opposed to pointers) in the list. -template > -class MRUCache : public MRUCacheBase { - private: - using ParentType = MRUCacheBase; - - public: - // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. - explicit MRUCache(typename ParentType::size_type max_size) - : ParentType(max_size) {} - virtual ~MRUCache() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(MRUCache); -}; - -// HashingMRUCache ------------------------------------------------------------ - -template -struct MRUCacheHashMap { - typedef std::unordered_map Type; -}; - -// This class is similar to MRUCache, except that it uses std::unordered_map as -// the map type instead of std::map. Note that your KeyType must be hashable to -// use this cache or you need to provide a hashing class. -template > -class HashingMRUCache - : public MRUCacheBase { - private: - using ParentType = - MRUCacheBase; - - public: - // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. - explicit HashingMRUCache(typename ParentType::size_type max_size) - : ParentType(max_size) {} - virtual ~HashingMRUCache() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(HashingMRUCache); -}; - -} // namespace base - -#endif // BASE_CONTAINERS_MRU_CACHE_H_ diff --git a/containers/mru_cache_unittest.cc b/containers/mru_cache_unittest.cc deleted file mode 100644 index 28e6f0d66..000000000 --- a/containers/mru_cache_unittest.cc +++ /dev/null @@ -1,394 +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/containers/mru_cache.h" - -#include -#include - -#include "base/memory/ptr_util.h" -#include "base/trace_event/memory_usage_estimator.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -int cached_item_live_count = 0; - -struct CachedItem { - CachedItem() : value(0) { - cached_item_live_count++; - } - - explicit CachedItem(int new_value) : value(new_value) { - cached_item_live_count++; - } - - explicit CachedItem(const CachedItem& other) : value(other.value) { - cached_item_live_count++; - } - - ~CachedItem() { - cached_item_live_count--; - } - - int value; -}; - -} // namespace - -TEST(MRUCacheTest, Basic) { - typedef base::MRUCache Cache; - Cache cache(Cache::NO_AUTO_EVICT); - - // Check failure conditions - { - CachedItem test_item; - EXPECT_TRUE(cache.Get(0) == cache.end()); - EXPECT_TRUE(cache.Peek(0) == cache.end()); - } - - static const int kItem1Key = 5; - CachedItem item1(10); - Cache::iterator inserted_item = cache.Put(kItem1Key, item1); - EXPECT_EQ(1U, cache.size()); - - // Check that item1 was properly inserted. - { - Cache::iterator found = cache.Get(kItem1Key); - EXPECT_TRUE(inserted_item == cache.begin()); - EXPECT_TRUE(found != cache.end()); - - found = cache.Peek(kItem1Key); - EXPECT_TRUE(found != cache.end()); - - EXPECT_EQ(kItem1Key, found->first); - EXPECT_EQ(item1.value, found->second.value); - } - - static const int kItem2Key = 7; - CachedItem item2(12); - cache.Put(kItem2Key, item2); - EXPECT_EQ(2U, cache.size()); - - // Check that item1 is the oldest since item2 was added afterwards. - { - Cache::reverse_iterator oldest = cache.rbegin(); - ASSERT_TRUE(oldest != cache.rend()); - EXPECT_EQ(kItem1Key, oldest->first); - EXPECT_EQ(item1.value, oldest->second.value); - } - - // Check that item1 is still accessible by key. - { - Cache::iterator test_item = cache.Get(kItem1Key); - ASSERT_TRUE(test_item != cache.end()); - EXPECT_EQ(kItem1Key, test_item->first); - EXPECT_EQ(item1.value, test_item->second.value); - } - - // Check that retrieving item1 pushed item2 to oldest. - { - Cache::reverse_iterator oldest = cache.rbegin(); - ASSERT_TRUE(oldest != cache.rend()); - EXPECT_EQ(kItem2Key, oldest->first); - EXPECT_EQ(item2.value, oldest->second.value); - } - - // Remove the oldest item and check that item1 is now the only member. - { - Cache::reverse_iterator next = cache.Erase(cache.rbegin()); - - EXPECT_EQ(1U, cache.size()); - - EXPECT_TRUE(next == cache.rbegin()); - EXPECT_EQ(kItem1Key, next->first); - EXPECT_EQ(item1.value, next->second.value); - - cache.Erase(cache.begin()); - EXPECT_EQ(0U, cache.size()); - } - - // Check that Clear() works properly. - cache.Put(kItem1Key, item1); - cache.Put(kItem2Key, item2); - EXPECT_EQ(2U, cache.size()); - cache.Clear(); - EXPECT_EQ(0U, cache.size()); -} - -TEST(MRUCacheTest, GetVsPeek) { - typedef base::MRUCache Cache; - Cache cache(Cache::NO_AUTO_EVICT); - - static const int kItem1Key = 1; - CachedItem item1(10); - cache.Put(kItem1Key, item1); - - static const int kItem2Key = 2; - CachedItem item2(20); - cache.Put(kItem2Key, item2); - - // This should do nothing since the size is bigger than the number of items. - cache.ShrinkToSize(100); - - // Check that item1 starts out as oldest - { - Cache::reverse_iterator iter = cache.rbegin(); - ASSERT_TRUE(iter != cache.rend()); - EXPECT_EQ(kItem1Key, iter->first); - EXPECT_EQ(item1.value, iter->second.value); - } - - // Check that Peek doesn't change ordering - { - Cache::iterator peekiter = cache.Peek(kItem1Key); - ASSERT_TRUE(peekiter != cache.end()); - - Cache::reverse_iterator iter = cache.rbegin(); - ASSERT_TRUE(iter != cache.rend()); - EXPECT_EQ(kItem1Key, iter->first); - EXPECT_EQ(item1.value, iter->second.value); - } -} - -TEST(MRUCacheTest, KeyReplacement) { - typedef base::MRUCache Cache; - Cache cache(Cache::NO_AUTO_EVICT); - - static const int kItem1Key = 1; - CachedItem item1(10); - cache.Put(kItem1Key, item1); - - static const int kItem2Key = 2; - CachedItem item2(20); - cache.Put(kItem2Key, item2); - - static const int kItem3Key = 3; - CachedItem item3(30); - cache.Put(kItem3Key, item3); - - static const int kItem4Key = 4; - CachedItem item4(40); - cache.Put(kItem4Key, item4); - - CachedItem item5(50); - cache.Put(kItem3Key, item5); - - EXPECT_EQ(4U, cache.size()); - for (int i = 0; i < 3; ++i) { - Cache::reverse_iterator iter = cache.rbegin(); - ASSERT_TRUE(iter != cache.rend()); - } - - // Make it so only the most important element is there. - cache.ShrinkToSize(1); - - Cache::iterator iter = cache.begin(); - EXPECT_EQ(kItem3Key, iter->first); - EXPECT_EQ(item5.value, iter->second.value); -} - -// Make sure that the owning version release its pointers properly. -TEST(MRUCacheTest, Owning) { - using Cache = base::MRUCache>; - Cache cache(Cache::NO_AUTO_EVICT); - - int initial_count = cached_item_live_count; - - // First insert and item and then overwrite it. - static const int kItem1Key = 1; - cache.Put(kItem1Key, WrapUnique(new CachedItem(20))); - cache.Put(kItem1Key, WrapUnique(new CachedItem(22))); - - // There should still be one item, and one extra live item. - Cache::iterator iter = cache.Get(kItem1Key); - EXPECT_EQ(1U, cache.size()); - EXPECT_TRUE(iter != cache.end()); - EXPECT_EQ(initial_count + 1, cached_item_live_count); - - // Now remove it. - cache.Erase(cache.begin()); - EXPECT_EQ(initial_count, cached_item_live_count); - - // Now try another cache that goes out of scope to make sure its pointers - // go away. - { - Cache cache2(Cache::NO_AUTO_EVICT); - cache2.Put(1, WrapUnique(new CachedItem(20))); - cache2.Put(2, WrapUnique(new CachedItem(20))); - } - - // There should be no objects leaked. - EXPECT_EQ(initial_count, cached_item_live_count); - - // Check that Clear() also frees things correctly. - { - Cache cache2(Cache::NO_AUTO_EVICT); - cache2.Put(1, WrapUnique(new CachedItem(20))); - cache2.Put(2, WrapUnique(new CachedItem(20))); - EXPECT_EQ(initial_count + 2, cached_item_live_count); - cache2.Clear(); - EXPECT_EQ(initial_count, cached_item_live_count); - } -} - -TEST(MRUCacheTest, AutoEvict) { - using Cache = base::MRUCache>; - static const Cache::size_type kMaxSize = 3; - - int initial_count = cached_item_live_count; - - { - Cache cache(kMaxSize); - - static const int kItem1Key = 1, kItem2Key = 2, kItem3Key = 3, kItem4Key = 4; - cache.Put(kItem1Key, std::make_unique(20)); - cache.Put(kItem2Key, std::make_unique(21)); - cache.Put(kItem3Key, std::make_unique(22)); - cache.Put(kItem4Key, std::make_unique(23)); - - // The cache should only have kMaxSize items in it even though we inserted - // more. - EXPECT_EQ(kMaxSize, cache.size()); - } - - // There should be no objects leaked. - EXPECT_EQ(initial_count, cached_item_live_count); -} - -TEST(MRUCacheTest, HashingMRUCache) { - // Very simple test to make sure that the hashing cache works correctly. - typedef base::HashingMRUCache Cache; - Cache cache(Cache::NO_AUTO_EVICT); - - CachedItem one(1); - cache.Put("First", one); - - CachedItem two(2); - cache.Put("Second", two); - - EXPECT_EQ(one.value, cache.Get("First")->second.value); - EXPECT_EQ(two.value, cache.Get("Second")->second.value); - cache.ShrinkToSize(1); - EXPECT_EQ(two.value, cache.Get("Second")->second.value); - EXPECT_TRUE(cache.Get("First") == cache.end()); -} - -TEST(MRUCacheTest, Swap) { - typedef base::MRUCache Cache; - Cache cache1(Cache::NO_AUTO_EVICT); - - // Insert two items into cache1. - static const int kItem1Key = 1; - CachedItem item1(2); - Cache::iterator inserted_item = cache1.Put(kItem1Key, item1); - EXPECT_EQ(1U, cache1.size()); - - static const int kItem2Key = 3; - CachedItem item2(4); - cache1.Put(kItem2Key, item2); - EXPECT_EQ(2U, cache1.size()); - - // Verify cache1's elements. - { - Cache::iterator iter = cache1.begin(); - ASSERT_TRUE(iter != cache1.end()); - EXPECT_EQ(kItem2Key, iter->first); - EXPECT_EQ(item2.value, iter->second.value); - - ++iter; - ASSERT_TRUE(iter != cache1.end()); - EXPECT_EQ(kItem1Key, iter->first); - EXPECT_EQ(item1.value, iter->second.value); - } - - // Create another cache2. - Cache cache2(Cache::NO_AUTO_EVICT); - - // Insert three items into cache2. - static const int kItem3Key = 5; - CachedItem item3(6); - inserted_item = cache2.Put(kItem3Key, item3); - EXPECT_EQ(1U, cache2.size()); - - static const int kItem4Key = 7; - CachedItem item4(8); - cache2.Put(kItem4Key, item4); - EXPECT_EQ(2U, cache2.size()); - - static const int kItem5Key = 9; - CachedItem item5(10); - cache2.Put(kItem5Key, item5); - EXPECT_EQ(3U, cache2.size()); - - // Verify cache2's elements. - { - Cache::iterator iter = cache2.begin(); - ASSERT_TRUE(iter != cache2.end()); - EXPECT_EQ(kItem5Key, iter->first); - EXPECT_EQ(item5.value, iter->second.value); - - ++iter; - ASSERT_TRUE(iter != cache2.end()); - EXPECT_EQ(kItem4Key, iter->first); - EXPECT_EQ(item4.value, iter->second.value); - - ++iter; - ASSERT_TRUE(iter != cache2.end()); - EXPECT_EQ(kItem3Key, iter->first); - EXPECT_EQ(item3.value, iter->second.value); - } - - // Swap cache1 and cache2 and verify cache2 has cache1's elements and cache1 - // has cache2's elements. - cache2.Swap(cache1); - - EXPECT_EQ(3U, cache1.size()); - EXPECT_EQ(2U, cache2.size()); - - // Verify cache1's elements. - { - Cache::iterator iter = cache1.begin(); - ASSERT_TRUE(iter != cache1.end()); - EXPECT_EQ(kItem5Key, iter->first); - EXPECT_EQ(item5.value, iter->second.value); - - ++iter; - ASSERT_TRUE(iter != cache1.end()); - EXPECT_EQ(kItem4Key, iter->first); - EXPECT_EQ(item4.value, iter->second.value); - - ++iter; - ASSERT_TRUE(iter != cache1.end()); - EXPECT_EQ(kItem3Key, iter->first); - EXPECT_EQ(item3.value, iter->second.value); - } - - // Verify cache2's elements. - { - Cache::iterator iter = cache2.begin(); - ASSERT_TRUE(iter != cache2.end()); - EXPECT_EQ(kItem2Key, iter->first); - EXPECT_EQ(item2.value, iter->second.value); - - ++iter; - ASSERT_TRUE(iter != cache2.end()); - EXPECT_EQ(kItem1Key, iter->first); - EXPECT_EQ(item1.value, iter->second.value); - } -} - -TEST(MRUCacheTest, EstimateMemory) { - base::MRUCache cache(10); - - const std::string key(100u, 'a'); - cache.Put(key, 1); - - EXPECT_GT(trace_event::EstimateMemoryUsage(cache), - trace_event::EstimateMemoryUsage(key)); -} - -} // namespace base diff --git a/containers/queue.h b/containers/queue.h deleted file mode 100644 index b5bc5c36e..000000000 --- a/containers/queue.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_QUEUE_H_ -#define BASE_CONTAINERS_QUEUE_H_ - -#include - -#include "base/containers/circular_deque.h" - -namespace base { - -// Provides a definition of base::queue that's like std::queue but uses a -// base::circular_deque instead of std::deque. Since std::queue is just a -// wrapper for an underlying type, we can just provide a typedef for it that -// defaults to the base circular_deque. -template > -using queue = std::queue; - -} // namespace base - -#endif // BASE_CONTAINERS_QUEUE_H_ diff --git a/containers/ring_buffer.h b/containers/ring_buffer.h deleted file mode 100644 index ca4a48ddc..000000000 --- a/containers/ring_buffer.h +++ /dev/null @@ -1,133 +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_CONTAINERS_RING_BUFFER_H_ -#define BASE_CONTAINERS_RING_BUFFER_H_ - -#include - -#include "base/logging.h" -#include "base/macros.h" - -namespace base { - -// base::RingBuffer uses a fixed-size array, unlike base::circular_deque and -// std::deque, and so, one can access only the last |kSize| elements. Also, you -// can add elements to the front and read/modify random elements, but cannot -// remove elements from the back. Therefore, it does not have a |Size| method, -// only |BufferSize|, which is a constant, and |CurrentIndex|, which is the -// number of elements added so far. -// -// If the above is sufficient for your use case, base::RingBuffer should be more -// efficient than base::circular_deque. -template -class RingBuffer { - public: - RingBuffer() : current_index_(0) {} - - size_t BufferSize() const { return kSize; } - - size_t CurrentIndex() const { return current_index_; } - - // Returns true if a value was saved to index |n|. - bool IsFilledIndex(size_t n) const { - return IsFilledIndexByBufferIndex(BufferIndex(n)); - } - - // Returns the element at index |n| (% |kSize|). - // - // n = 0 returns the oldest value and - // n = bufferSize() - 1 returns the most recent value. - const T& ReadBuffer(size_t n) const { - const size_t buffer_index = BufferIndex(n); - CHECK(IsFilledIndexByBufferIndex(buffer_index)); - return buffer_[buffer_index]; - } - - T* MutableReadBuffer(size_t n) { - const size_t buffer_index = BufferIndex(n); - CHECK(IsFilledIndexByBufferIndex(buffer_index)); - return &buffer_[buffer_index]; - } - - void SaveToBuffer(const T& value) { - buffer_[BufferIndex(0)] = value; - current_index_++; - } - - void Clear() { current_index_ = 0; } - - // Iterator has const access to the RingBuffer it got retrieved from. - class Iterator { - public: - size_t index() const { return index_; } - - const T* operator->() const { return &buffer_.ReadBuffer(index_); } - const T* operator*() const { return &buffer_.ReadBuffer(index_); } - - Iterator& operator++() { - index_++; - if (index_ == kSize) - out_of_range_ = true; - return *this; - } - - Iterator& operator--() { - if (index_ == 0) - out_of_range_ = true; - index_--; - return *this; - } - - operator bool() const { - return !out_of_range_ && buffer_.IsFilledIndex(index_); - } - - private: - Iterator(const RingBuffer& buffer, size_t index) - : buffer_(buffer), index_(index), out_of_range_(false) {} - - const RingBuffer& buffer_; - size_t index_; - bool out_of_range_; - - friend class RingBuffer; - }; - - // Returns an Iterator pointing to the oldest value in the buffer. - // Example usage (iterate from oldest to newest value): - // for (RingBuffer::Iterator it = ring_buffer.Begin(); it; ++it) {} - Iterator Begin() const { - if (current_index_ < kSize) - return Iterator(*this, kSize - current_index_); - return Iterator(*this, 0); - } - - // Returns an Iterator pointing to the newest value in the buffer. - // Example usage (iterate backwards from newest to oldest value): - // for (RingBuffer::Iterator it = ring_buffer.End(); it; --it) {} - Iterator End() const { return Iterator(*this, kSize - 1); } - - private: - inline size_t BufferIndex(size_t n) const { - return (current_index_ + n) % kSize; - } - - // This specialization of |IsFilledIndex| is a micro-optimization that enables - // us to do e.g. `CHECK(IsFilledIndex(n))` without calling |BufferIndex| - // twice. Since |BufferIndex| involves a % operation, it's not quite free at a - // micro-scale. - inline bool IsFilledIndexByBufferIndex(size_t buffer_index) const { - return buffer_index < current_index_; - } - - T buffer_[kSize]; - size_t current_index_; - - DISALLOW_COPY_AND_ASSIGN(RingBuffer); -}; - -} // namespace base - -#endif // BASE_CONTAINERS_RING_BUFFER_H_ diff --git a/containers/small_map.h b/containers/small_map.h deleted file mode 100644 index 495332fc3..000000000 --- a/containers/small_map.h +++ /dev/null @@ -1,660 +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_CONTAINERS_SMALL_MAP_H_ -#define BASE_CONTAINERS_SMALL_MAP_H_ - -#include - -#include -#include -#include -#include - -#include "base/containers/hash_tables.h" -#include "base/logging.h" - -namespace base { - -// small_map is a container with a std::map-like interface. It starts out -// backed by a unsorted array but switches to some other container type if it -// grows beyond this fixed size. -// -// Please see //base/containers/README.md for an overview of which container -// to select. -// -// PROS -// -// - Good memory locality and low overhead for smaller maps. -// - Handles large maps without the degenerate performance of flat_map. -// -// CONS -// -// - Larger code size than the alternatives. -// -// IMPORTANT NOTES -// -// - Iterators are invalidated across mutations. -// -// DETAILS -// -// base::small_map will pick up the comparator from the underlying map type. In -// std::map only a "less" operator is defined, which requires us to do two -// comparisons per element when doing the brute-force search in the simple -// array. std::unordered_map has a key_equal function which will be used. -// -// We define default overrides for the common map types to avoid this -// double-compare, but you should be aware of this if you use your own -// operator< for your map and supply yor own version of == to the small_map. -// You can use regular operator== by just doing: -// -// base::small_map, 4, std::equal_to> -// -// -// USAGE -// ----- -// -// NormalMap: The map type to fall back to. This also defines the key -// and value types for the small_map. -// kArraySize: The size of the initial array of results. This will be -// allocated with the small_map object rather than separately on -// the heap. Once the map grows beyond this size, the map type -// will be used instead. -// EqualKey: A functor which tests two keys for equality. If the wrapped -// map type has a "key_equal" member (hash_map does), then that will -// be used by default. If the wrapped map type has a strict weak -// ordering "key_compare" (std::map does), that will be used to -// implement equality by default. -// MapInit: A functor that takes a NormalMap* and uses it to initialize the map. -// This functor will be called at most once per small_map, when the map -// exceeds the threshold of kArraySize and we are about to copy values -// from the array to the map. The functor *must* initialize the -// NormalMap* argument with placement new, since after it runs we -// assume that the NormalMap has been initialized. -// -// example: -// base::small_map> days; -// days["sunday" ] = 0; -// days["monday" ] = 1; -// days["tuesday" ] = 2; -// days["wednesday"] = 3; -// days["thursday" ] = 4; -// days["friday" ] = 5; -// days["saturday" ] = 6; - -namespace internal { - -template -class small_map_default_init { - public: - void operator()(NormalMap* map) const { new (map) NormalMap(); } -}; - -// has_key_equal::value is true iff there exists a type M::key_equal. This is -// used to dispatch to one of the select_equal_key<> metafunctions below. -template -struct has_key_equal { - typedef char sml; // "small" is sometimes #defined so we use an abbreviation. - typedef struct { char dummy[2]; } big; - // Two functions, one accepts types that have a key_equal member, and one that - // accepts anything. They each return a value of a different size, so we can - // determine at compile-time which function would have been called. - template static big test(typename U::key_equal*); - template static sml test(...); - // Determines if M::key_equal exists by looking at the size of the return - // type of the compiler-chosen test() function. - static const bool value = (sizeof(test(0)) == sizeof(big)); -}; -template const bool has_key_equal::value; - -// Base template used for map types that do NOT have an M::key_equal member, -// e.g., std::map<>. These maps have a strict weak ordering comparator rather -// than an equality functor, so equality will be implemented in terms of that -// comparator. -// -// There's a partial specialization of this template below for map types that do -// have an M::key_equal member. -template -struct select_equal_key { - struct equal_key { - bool operator()(const typename M::key_type& left, - const typename M::key_type& right) { - // Implements equality in terms of a strict weak ordering comparator. - typename M::key_compare comp; - return !comp(left, right) && !comp(right, left); - } - }; -}; - -// Provide overrides to use operator== for key compare for the "normal" map and -// hash map types. If you override the default comparator or allocator for a -// map or hash_map, or use another type of map, this won't get used. -// -// If we switch to using std::unordered_map for base::hash_map, then the -// hash_map specialization can be removed. -template -struct select_equal_key, false> { - struct equal_key { - bool operator()(const KeyType& left, const KeyType& right) { - return left == right; - } - }; -}; -template -struct select_equal_key, false> { - struct equal_key { - bool operator()(const KeyType& left, const KeyType& right) { - return left == right; - } - }; -}; - -// Partial template specialization handles case where M::key_equal exists, e.g., -// hash_map<>. -template -struct select_equal_key { - typedef typename M::key_equal equal_key; -}; - -} // namespace internal - -template ::value>::equal_key, - typename MapInit = internal::small_map_default_init> -class small_map { - // We cannot rely on the compiler to reject array of size 0. In - // particular, gcc 2.95.3 does it but later versions allow 0-length - // arrays. Therefore, we explicitly reject non-positive kArraySize - // here. - static_assert(kArraySize > 0, "default initial size should be positive"); - - public: - typedef typename NormalMap::key_type key_type; - typedef typename NormalMap::mapped_type data_type; - typedef typename NormalMap::mapped_type mapped_type; - typedef typename NormalMap::value_type value_type; - typedef EqualKey key_equal; - - small_map() : size_(0), functor_(MapInit()) {} - - explicit small_map(const MapInit& functor) : size_(0), functor_(functor) {} - - // Allow copy-constructor and assignment, since STL allows them too. - small_map(const small_map& src) { - // size_ and functor_ are initted in InitFrom() - InitFrom(src); - } - void operator=(const small_map& src) { - if (&src == this) return; - - // This is not optimal. If src and dest are both using the small - // array, we could skip the teardown and reconstruct. One problem - // to be resolved is that the value_type itself is pair, and const K is not assignable. - Destroy(); - InitFrom(src); - } - ~small_map() { Destroy(); } - - class const_iterator; - - class iterator { - public: - typedef typename NormalMap::iterator::iterator_category iterator_category; - typedef typename NormalMap::iterator::value_type value_type; - typedef typename NormalMap::iterator::difference_type difference_type; - typedef typename NormalMap::iterator::pointer pointer; - typedef typename NormalMap::iterator::reference reference; - - inline iterator(): array_iter_(NULL) {} - - inline iterator& operator++() { - if (array_iter_ != NULL) { - ++array_iter_; - } else { - ++hash_iter_; - } - return *this; - } - inline iterator operator++(int /*unused*/) { - iterator result(*this); - ++(*this); - return result; - } - inline iterator& operator--() { - if (array_iter_ != NULL) { - --array_iter_; - } else { - --hash_iter_; - } - return *this; - } - inline iterator operator--(int /*unused*/) { - iterator result(*this); - --(*this); - return result; - } - inline value_type* operator->() const { - if (array_iter_ != NULL) { - return array_iter_; - } else { - return hash_iter_.operator->(); - } - } - - inline value_type& operator*() const { - if (array_iter_ != NULL) { - return *array_iter_; - } else { - return *hash_iter_; - } - } - - inline bool operator==(const iterator& other) const { - if (array_iter_ != NULL) { - return array_iter_ == other.array_iter_; - } else { - return other.array_iter_ == NULL && hash_iter_ == other.hash_iter_; - } - } - - inline bool operator!=(const iterator& other) const { - return !(*this == other); - } - - bool operator==(const const_iterator& other) const; - bool operator!=(const const_iterator& other) const; - - private: - friend class small_map; - friend class const_iterator; - inline explicit iterator(value_type* init) : array_iter_(init) {} - inline explicit iterator(const typename NormalMap::iterator& init) - : array_iter_(NULL), hash_iter_(init) {} - - value_type* array_iter_; - typename NormalMap::iterator hash_iter_; - }; - - class const_iterator { - public: - typedef typename NormalMap::const_iterator::iterator_category - iterator_category; - typedef typename NormalMap::const_iterator::value_type value_type; - typedef typename NormalMap::const_iterator::difference_type difference_type; - typedef typename NormalMap::const_iterator::pointer pointer; - typedef typename NormalMap::const_iterator::reference reference; - - inline const_iterator(): array_iter_(NULL) {} - // Non-explicit ctor lets us convert regular iterators to const iterators - inline const_iterator(const iterator& other) - : array_iter_(other.array_iter_), hash_iter_(other.hash_iter_) {} - - inline const_iterator& operator++() { - if (array_iter_ != NULL) { - ++array_iter_; - } else { - ++hash_iter_; - } - return *this; - } - inline const_iterator operator++(int /*unused*/) { - const_iterator result(*this); - ++(*this); - return result; - } - - inline const_iterator& operator--() { - if (array_iter_ != NULL) { - --array_iter_; - } else { - --hash_iter_; - } - return *this; - } - inline const_iterator operator--(int /*unused*/) { - const_iterator result(*this); - --(*this); - return result; - } - - inline const value_type* operator->() const { - if (array_iter_ != NULL) { - return array_iter_; - } else { - return hash_iter_.operator->(); - } - } - - inline const value_type& operator*() const { - if (array_iter_ != NULL) { - return *array_iter_; - } else { - return *hash_iter_; - } - } - - inline bool operator==(const const_iterator& other) const { - if (array_iter_ != NULL) { - return array_iter_ == other.array_iter_; - } else { - return other.array_iter_ == NULL && hash_iter_ == other.hash_iter_; - } - } - - inline bool operator!=(const const_iterator& other) const { - return !(*this == other); - } - - private: - friend class small_map; - inline explicit const_iterator(const value_type* init) - : array_iter_(init) {} - inline explicit const_iterator( - const typename NormalMap::const_iterator& init) - : array_iter_(NULL), hash_iter_(init) {} - - const value_type* array_iter_; - typename NormalMap::const_iterator hash_iter_; - }; - - iterator find(const key_type& key) { - key_equal compare; - if (size_ >= 0) { - for (int i = 0; i < size_; i++) { - if (compare(array_[i].first, key)) { - return iterator(array_ + i); - } - } - return iterator(array_ + size_); - } else { - return iterator(map()->find(key)); - } - } - - const_iterator find(const key_type& key) const { - key_equal compare; - if (size_ >= 0) { - for (int i = 0; i < size_; i++) { - if (compare(array_[i].first, key)) { - return const_iterator(array_ + i); - } - } - return const_iterator(array_ + size_); - } else { - return const_iterator(map()->find(key)); - } - } - - // Invalidates iterators. - data_type& operator[](const key_type& key) { - key_equal compare; - - if (size_ >= 0) { - // operator[] searches backwards, favoring recently-added - // elements. - for (int i = size_-1; i >= 0; --i) { - if (compare(array_[i].first, key)) { - return array_[i].second; - } - } - if (size_ == kArraySize) { - ConvertToRealMap(); - return map_[key]; - } else { - new (&array_[size_]) value_type(key, data_type()); - return array_[size_++].second; - } - } else { - return map_[key]; - } - } - - // Invalidates iterators. - std::pair insert(const value_type& x) { - key_equal compare; - - if (size_ >= 0) { - for (int i = 0; i < size_; i++) { - if (compare(array_[i].first, x.first)) { - return std::make_pair(iterator(array_ + i), false); - } - } - if (size_ == kArraySize) { - ConvertToRealMap(); // Invalidates all iterators! - std::pair ret = map_.insert(x); - return std::make_pair(iterator(ret.first), ret.second); - } else { - new (&array_[size_]) value_type(x); - return std::make_pair(iterator(array_ + size_++), true); - } - } else { - std::pair ret = map_.insert(x); - return std::make_pair(iterator(ret.first), ret.second); - } - } - - // Invalidates iterators. - template - void insert(InputIterator f, InputIterator l) { - while (f != l) { - insert(*f); - ++f; - } - } - - // Invalidates iterators. - template - std::pair emplace(Args&&... args) { - key_equal compare; - - if (size_ >= 0) { - value_type x(std::forward(args)...); - for (int i = 0; i < size_; i++) { - if (compare(array_[i].first, x.first)) { - return std::make_pair(iterator(array_ + i), false); - } - } - if (size_ == kArraySize) { - ConvertToRealMap(); // Invalidates all iterators! - std::pair ret = - map_.emplace(std::move(x)); - return std::make_pair(iterator(ret.first), ret.second); - } else { - new (&array_[size_]) value_type(std::move(x)); - return std::make_pair(iterator(array_ + size_++), true); - } - } else { - std::pair ret = - map_.emplace(std::forward(args)...); - return std::make_pair(iterator(ret.first), ret.second); - } - } - - iterator begin() { - if (size_ >= 0) { - return iterator(array_); - } else { - return iterator(map_.begin()); - } - } - const_iterator begin() const { - if (size_ >= 0) { - return const_iterator(array_); - } else { - return const_iterator(map_.begin()); - } - } - - iterator end() { - if (size_ >= 0) { - return iterator(array_ + size_); - } else { - return iterator(map_.end()); - } - } - const_iterator end() const { - if (size_ >= 0) { - return const_iterator(array_ + size_); - } else { - return const_iterator(map_.end()); - } - } - - void clear() { - if (size_ >= 0) { - for (int i = 0; i < size_; i++) { - array_[i].~value_type(); - } - } else { - map_.~NormalMap(); - } - size_ = 0; - } - - // Invalidates iterators. Returns iterator following the last removed element. - iterator erase(const iterator& position) { - if (size_ >= 0) { - int i = position.array_iter_ - array_; - array_[i].~value_type(); - --size_; - if (i != size_) { - new (&array_[i]) value_type(std::move(array_[size_])); - array_[size_].~value_type(); - return iterator(array_ + i); - } - return end(); - } - return iterator(map_.erase(position.hash_iter_)); - } - - size_t erase(const key_type& key) { - iterator iter = find(key); - if (iter == end()) return 0u; - erase(iter); - return 1u; - } - - size_t count(const key_type& key) const { - return (find(key) == end()) ? 0 : 1; - } - - size_t size() const { - if (size_ >= 0) { - return static_cast(size_); - } else { - return map_.size(); - } - } - - bool empty() const { - if (size_ >= 0) { - return (size_ == 0); - } else { - return map_.empty(); - } - } - - // Returns true if we have fallen back to using the underlying map - // representation. - bool UsingFullMap() const { - return size_ < 0; - } - - inline NormalMap* map() { - CHECK(UsingFullMap()); - return &map_; - } - inline const NormalMap* map() const { - CHECK(UsingFullMap()); - return &map_; - } - - private: - int size_; // negative = using hash_map - - MapInit functor_; - - // We want to call constructors and destructors manually, but we don't want to - // allocate and deallocate the memory used for them separately. Since array_ - // and map_ are mutually exclusive, we'll put them in a union. - union { - value_type array_[kArraySize]; - NormalMap map_; - }; - - void ConvertToRealMap() { - // Storage for the elements in the temporary array. This is intentionally - // declared as a union to avoid having to default-construct |kArraySize| - // elements, only to move construct over them in the initial loop. - union Storage { - Storage() {} - ~Storage() {} - value_type array[kArraySize]; - } temp; - - // Move the current elements into a temporary array. - for (int i = 0; i < kArraySize; i++) { - new (&temp.array[i]) value_type(std::move(array_[i])); - array_[i].~value_type(); - } - - // Initialize the map. - size_ = -1; - functor_(&map_); - - // Insert elements into it. - for (int i = 0; i < kArraySize; i++) { - map_.insert(std::move(temp.array[i])); - temp.array[i].~value_type(); - } - } - - // Helpers for constructors and destructors. - void InitFrom(const small_map& src) { - functor_ = src.functor_; - size_ = src.size_; - if (src.size_ >= 0) { - for (int i = 0; i < size_; i++) { - new (&array_[i]) value_type(src.array_[i]); - } - } else { - functor_(&map_); - map_ = src.map_; - } - } - void Destroy() { - if (size_ >= 0) { - for (int i = 0; i < size_; i++) { - array_[i].~value_type(); - } - } else { - map_.~NormalMap(); - } - } -}; - -template -inline bool small_map::iterator:: -operator==(const const_iterator& other) const { - return other == *this; -} -template -inline bool small_map::iterator:: -operator!=(const const_iterator& other) const { - return other != *this; -} - -} // namespace base - -#endif // BASE_CONTAINERS_SMALL_MAP_H_ diff --git a/containers/small_map_unittest.cc b/containers/small_map_unittest.cc deleted file mode 100644 index 6561851f9..000000000 --- a/containers/small_map_unittest.cc +++ /dev/null @@ -1,603 +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/containers/small_map.h" - -#include - -#include -#include -#include -#include - -#include "base/logging.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(SmallMap, General) { - small_map> m; - - EXPECT_TRUE(m.empty()); - - m[0] = 5; - - EXPECT_FALSE(m.empty()); - EXPECT_EQ(m.size(), 1u); - - m[9] = 2; - - EXPECT_FALSE(m.empty()); - EXPECT_EQ(m.size(), 2u); - - EXPECT_EQ(m[9], 2); - EXPECT_EQ(m[0], 5); - EXPECT_FALSE(m.UsingFullMap()); - - small_map>::iterator iter(m.begin()); - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, 0); - EXPECT_EQ(iter->second, 5); - ++iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ((*iter).first, 9); - EXPECT_EQ((*iter).second, 2); - ++iter; - EXPECT_TRUE(iter == m.end()); - - m[8] = 23; - m[1234] = 90; - m[-5] = 6; - - EXPECT_EQ(m[ 9], 2); - EXPECT_EQ(m[ 0], 5); - EXPECT_EQ(m[1234], 90); - EXPECT_EQ(m[ 8], 23); - EXPECT_EQ(m[ -5], 6); - EXPECT_EQ(m.size(), 5u); - EXPECT_FALSE(m.empty()); - EXPECT_TRUE(m.UsingFullMap()); - - iter = m.begin(); - for (int i = 0; i < 5; i++) { - EXPECT_TRUE(iter != m.end()); - ++iter; - } - EXPECT_TRUE(iter == m.end()); - - const small_map>& ref = m; - EXPECT_TRUE(ref.find(1234) != m.end()); - EXPECT_TRUE(ref.find(5678) == m.end()); -} - -TEST(SmallMap, PostFixIteratorIncrement) { - small_map> m; - m[0] = 5; - m[2] = 3; - - { - small_map>::iterator iter(m.begin()); - small_map>::iterator last(iter++); - ++last; - EXPECT_TRUE(last == iter); - } - - { - small_map>::const_iterator iter(m.begin()); - small_map>::const_iterator last(iter++); - ++last; - EXPECT_TRUE(last == iter); - } -} - -// Based on the General testcase. -TEST(SmallMap, CopyConstructor) { - small_map> src; - - { - small_map> m(src); - EXPECT_TRUE(m.empty()); - } - - src[0] = 5; - - { - small_map> m(src); - EXPECT_FALSE(m.empty()); - EXPECT_EQ(m.size(), 1u); - } - - src[9] = 2; - - { - small_map> m(src); - EXPECT_FALSE(m.empty()); - EXPECT_EQ(m.size(), 2u); - - EXPECT_EQ(m[9], 2); - EXPECT_EQ(m[0], 5); - EXPECT_FALSE(m.UsingFullMap()); - } - - src[8] = 23; - src[1234] = 90; - src[-5] = 6; - - { - small_map> m(src); - EXPECT_EQ(m[ 9], 2); - EXPECT_EQ(m[ 0], 5); - EXPECT_EQ(m[1234], 90); - EXPECT_EQ(m[ 8], 23); - EXPECT_EQ(m[ -5], 6); - EXPECT_EQ(m.size(), 5u); - EXPECT_FALSE(m.empty()); - EXPECT_TRUE(m.UsingFullMap()); - } -} - -template -static bool SmallMapIsSubset(small_map const& a, - small_map const& b) { - typename small_map::const_iterator it; - for (it = a.begin(); it != a.end(); ++it) { - typename small_map::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 -static bool SmallMapEqual(small_map const& a, - small_map const& b) { - return SmallMapIsSubset(a, b) && SmallMapIsSubset(b, a); -} - -TEST(SmallMap, AssignmentOperator) { - small_map> src_small; - small_map> src_large; - - src_small[1] = 20; - src_small[2] = 21; - src_small[3] = 22; - EXPECT_FALSE(src_small.UsingFullMap()); - - src_large[1] = 20; - src_large[2] = 21; - src_large[3] = 22; - src_large[5] = 23; - src_large[6] = 24; - src_large[7] = 25; - EXPECT_TRUE(src_large.UsingFullMap()); - - // Assignments to empty. - small_map> dest_small; - dest_small = src_small; - EXPECT_TRUE(SmallMapEqual(dest_small, src_small)); - EXPECT_EQ(dest_small.UsingFullMap(), - src_small.UsingFullMap()); - - small_map> dest_large; - dest_large = src_large; - EXPECT_TRUE(SmallMapEqual(dest_large, src_large)); - EXPECT_EQ(dest_large.UsingFullMap(), - src_large.UsingFullMap()); - - // Assignments which assign from full to small, and vice versa. - dest_small = src_large; - EXPECT_TRUE(SmallMapEqual(dest_small, src_large)); - EXPECT_EQ(dest_small.UsingFullMap(), - src_large.UsingFullMap()); - - dest_large = src_small; - EXPECT_TRUE(SmallMapEqual(dest_large, src_small)); - EXPECT_EQ(dest_large.UsingFullMap(), - src_small.UsingFullMap()); - - // Double check that SmallMapEqual works: - dest_large[42] = 666; - EXPECT_FALSE(SmallMapEqual(dest_large, src_small)); -} - -TEST(SmallMap, Insert) { - small_map> sm; - - // loop through the transition from small map to map. - for (int i = 1; i <= 10; ++i) { - VLOG(1) << "Iteration " << i; - // insert an element - std::pair>::iterator, bool> ret; - ret = sm.insert(std::make_pair(i, 100*i)); - EXPECT_TRUE(ret.second); - EXPECT_TRUE(ret.first == sm.find(i)); - EXPECT_EQ(ret.first->first, i); - EXPECT_EQ(ret.first->second, 100*i); - - // try to insert it again with different value, fails, but we still get an - // iterator back with the original value. - ret = sm.insert(std::make_pair(i, -i)); - EXPECT_FALSE(ret.second); - EXPECT_TRUE(ret.first == sm.find(i)); - EXPECT_EQ(ret.first->first, i); - EXPECT_EQ(ret.first->second, 100*i); - - // check the state of the map. - for (int j = 1; j <= i; ++j) { - small_map>::iterator it = sm.find(j); - EXPECT_TRUE(it != sm.end()); - EXPECT_EQ(it->first, j); - EXPECT_EQ(it->second, j * 100); - } - EXPECT_EQ(sm.size(), static_cast(i)); - EXPECT_FALSE(sm.empty()); - } -} - -TEST(SmallMap, InsertRange) { - // loop through the transition from small map to map. - for (int elements = 0; elements <= 10; ++elements) { - VLOG(1) << "Elements " << elements; - std::unordered_map normal_map; - for (int i = 1; i <= elements; ++i) { - normal_map.insert(std::make_pair(i, 100*i)); - } - - small_map> sm; - sm.insert(normal_map.begin(), normal_map.end()); - EXPECT_EQ(normal_map.size(), sm.size()); - for (int i = 1; i <= elements; ++i) { - VLOG(1) << "Iteration " << i; - EXPECT_TRUE(sm.find(i) != sm.end()); - EXPECT_EQ(sm.find(i)->first, i); - EXPECT_EQ(sm.find(i)->second, 100*i); - } - } -} - -TEST(SmallMap, Erase) { - small_map> m; - small_map>::iterator iter; - - m["monday"] = 1; - m["tuesday"] = 2; - m["wednesday"] = 3; - - EXPECT_EQ(m["monday" ], 1); - EXPECT_EQ(m["tuesday" ], 2); - EXPECT_EQ(m["wednesday"], 3); - EXPECT_EQ(m.count("tuesday"), 1u); - EXPECT_FALSE(m.UsingFullMap()); - - iter = m.begin(); - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, "monday"); - EXPECT_EQ(iter->second, 1); - ++iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, "tuesday"); - EXPECT_EQ(iter->second, 2); - ++iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, "wednesday"); - EXPECT_EQ(iter->second, 3); - ++iter; - EXPECT_TRUE(iter == m.end()); - - EXPECT_EQ(m.erase("tuesday"), 1u); - - EXPECT_EQ(m["monday" ], 1); - EXPECT_EQ(m["wednesday"], 3); - EXPECT_EQ(m.count("tuesday"), 0u); - EXPECT_EQ(m.erase("tuesday"), 0u); - - iter = m.begin(); - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, "monday"); - EXPECT_EQ(iter->second, 1); - ++iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, "wednesday"); - EXPECT_EQ(iter->second, 3); - ++iter; - EXPECT_TRUE(iter == m.end()); - - m["thursday"] = 4; - m["friday"] = 5; - EXPECT_EQ(m.size(), 4u); - EXPECT_FALSE(m.empty()); - EXPECT_FALSE(m.UsingFullMap()); - - m["saturday"] = 6; - EXPECT_TRUE(m.UsingFullMap()); - - EXPECT_EQ(m.count("friday"), 1u); - EXPECT_EQ(m.erase("friday"), 1u); - EXPECT_TRUE(m.UsingFullMap()); - EXPECT_EQ(m.count("friday"), 0u); - EXPECT_EQ(m.erase("friday"), 0u); - - EXPECT_EQ(m.size(), 4u); - EXPECT_FALSE(m.empty()); - EXPECT_EQ(m.erase("monday"), 1u); - EXPECT_EQ(m.size(), 3u); - EXPECT_FALSE(m.empty()); - - m.clear(); - EXPECT_FALSE(m.UsingFullMap()); - EXPECT_EQ(m.size(), 0u); - EXPECT_TRUE(m.empty()); -} - -TEST(SmallMap, EraseReturnsIteratorFollowingRemovedElement) { - small_map> m; - small_map>::iterator iter; - - m["a"] = 0; - m["b"] = 1; - m["c"] = 2; - - // Erase first item. - auto following_iter = m.erase(m.begin()); - EXPECT_EQ(m.begin(), following_iter); - EXPECT_EQ(2u, m.size()); - EXPECT_EQ(m.count("a"), 0u); - EXPECT_EQ(m.count("b"), 1u); - EXPECT_EQ(m.count("c"), 1u); - - // Iterate to last item and erase it. - ++following_iter; - following_iter = m.erase(following_iter); - ASSERT_EQ(1u, m.size()); - EXPECT_EQ(m.end(), following_iter); - EXPECT_EQ(m.count("b"), 0u); - EXPECT_EQ(m.count("c"), 1u); - - // Erase remaining item. - following_iter = m.erase(m.begin()); - EXPECT_TRUE(m.empty()); - EXPECT_EQ(m.end(), following_iter); -} - -TEST(SmallMap, NonHashMap) { - small_map, 4, std::equal_to> m; - EXPECT_TRUE(m.empty()); - - m[9] = 2; - m[0] = 5; - - EXPECT_EQ(m[9], 2); - EXPECT_EQ(m[0], 5); - EXPECT_EQ(m.size(), 2u); - EXPECT_FALSE(m.empty()); - EXPECT_FALSE(m.UsingFullMap()); - - small_map, 4, std::equal_to>::iterator iter( - m.begin()); - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, 9); - EXPECT_EQ(iter->second, 2); - ++iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, 0); - EXPECT_EQ(iter->second, 5); - ++iter; - EXPECT_TRUE(iter == m.end()); - --iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, 0); - EXPECT_EQ(iter->second, 5); - - m[8] = 23; - m[1234] = 90; - m[-5] = 6; - - EXPECT_EQ(m[ 9], 2); - EXPECT_EQ(m[ 0], 5); - EXPECT_EQ(m[1234], 90); - EXPECT_EQ(m[ 8], 23); - EXPECT_EQ(m[ -5], 6); - EXPECT_EQ(m.size(), 5u); - EXPECT_FALSE(m.empty()); - EXPECT_TRUE(m.UsingFullMap()); - - iter = m.begin(); - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, -5); - EXPECT_EQ(iter->second, 6); - ++iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, 0); - EXPECT_EQ(iter->second, 5); - ++iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, 8); - EXPECT_EQ(iter->second, 23); - ++iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, 9); - EXPECT_EQ(iter->second, 2); - ++iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, 1234); - EXPECT_EQ(iter->second, 90); - ++iter; - EXPECT_TRUE(iter == m.end()); - --iter; - ASSERT_TRUE(iter != m.end()); - EXPECT_EQ(iter->first, 1234); - EXPECT_EQ(iter->second, 90); -} - -TEST(SmallMap, DefaultEqualKeyWorks) { - // If these tests compile, they pass. The EXPECT calls are only there to avoid - // unused variable warnings. - small_map> hm; - EXPECT_EQ(0u, hm.size()); - small_map> m; - EXPECT_EQ(0u, m.size()); -} - -namespace { - -class unordered_map_add_item : public std::unordered_map { - public: - unordered_map_add_item() = default; - explicit unordered_map_add_item(const std::pair& item) { - insert(item); - } -}; - -void InitMap(unordered_map_add_item* map_ctor) { - new (map_ctor) unordered_map_add_item(std::make_pair(0, 0)); -} - -class unordered_map_add_item_initializer { - public: - explicit unordered_map_add_item_initializer(int item_to_add) - : item_(item_to_add) {} - unordered_map_add_item_initializer() : item_(0) {} - void operator()(unordered_map_add_item* map_ctor) const { - new (map_ctor) unordered_map_add_item(std::make_pair(item_, item_)); - } - - int item_; -}; - -} // anonymous namespace - -TEST(SmallMap, SubclassInitializationWithFunctionPointer) { - small_map, - void (&)(unordered_map_add_item*)> - m(InitMap); - - EXPECT_TRUE(m.empty()); - - m[1] = 1; - m[2] = 2; - m[3] = 3; - m[4] = 4; - - EXPECT_EQ(4u, m.size()); - EXPECT_EQ(0u, m.count(0)); - - m[5] = 5; - EXPECT_EQ(6u, m.size()); - // Our function adds an extra item when we convert to a map. - EXPECT_EQ(1u, m.count(0)); -} - -TEST(SmallMap, SubclassInitializationWithFunctionObject) { - small_map, - unordered_map_add_item_initializer> - m(unordered_map_add_item_initializer(-1)); - - EXPECT_TRUE(m.empty()); - - m[1] = 1; - m[2] = 2; - m[3] = 3; - m[4] = 4; - - EXPECT_EQ(4u, m.size()); - EXPECT_EQ(0u, m.count(-1)); - - m[5] = 5; - EXPECT_EQ(6u, m.size()); - // Our functor adds an extra item when we convert to a map. - EXPECT_EQ(1u, m.count(-1)); -} - -namespace { - -// This class acts as a basic implementation of a move-only type. The canonical -// example of such a type is scoped_ptr/unique_ptr. -template -class MoveOnlyType { - public: - MoveOnlyType() : value_(0) {} - explicit MoveOnlyType(V value) : value_(value) {} - - MoveOnlyType(MoveOnlyType&& other) { - *this = std::move(other); - } - - MoveOnlyType& operator=(MoveOnlyType&& other) { - value_ = other.value_; - other.value_ = 0; - return *this; - } - - MoveOnlyType(const MoveOnlyType&) = delete; - MoveOnlyType& operator=(const MoveOnlyType&) = delete; - - V value() const { return value_; } - - private: - V value_; -}; - -} // namespace - -TEST(SmallMap, MoveOnlyValueType) { - small_map>, 2> m; - - m[0] = MoveOnlyType(1); - m[1] = MoveOnlyType(2); - m.erase(m.begin()); - - // small_map will move m[1] to an earlier index in the internal array. - EXPECT_EQ(m.size(), 1u); - EXPECT_EQ(m[1].value(), 2); - - m[0] = MoveOnlyType(1); - // small_map must move the values from the array into the internal std::map. - m[2] = MoveOnlyType(3); - - EXPECT_EQ(m.size(), 3u); - EXPECT_EQ(m[0].value(), 1); - EXPECT_EQ(m[1].value(), 2); - EXPECT_EQ(m[2].value(), 3); - - m.erase(m.begin()); - - // small_map should also let internal std::map erase with a move-only type. - EXPECT_EQ(m.size(), 2u); - EXPECT_EQ(m[1].value(), 2); - EXPECT_EQ(m[2].value(), 3); -} - -TEST(SmallMap, Emplace) { - small_map>> sm; - - // loop through the transition from small map to map. - for (size_t i = 1; i <= 10; ++i) { - // insert an element - auto ret = sm.emplace(i, MoveOnlyType(100 * i)); - EXPECT_TRUE(ret.second); - EXPECT_TRUE(ret.first == sm.find(i)); - EXPECT_EQ(ret.first->first, i); - EXPECT_EQ(ret.first->second.value(), 100 * i); - - // try to insert it again with different value, fails, but we still get an - // iterator back with the original value. - ret = sm.emplace(i, MoveOnlyType(i)); - EXPECT_FALSE(ret.second); - EXPECT_TRUE(ret.first == sm.find(i)); - EXPECT_EQ(ret.first->first, i); - EXPECT_EQ(ret.first->second.value(), 100 * i); - - // check the state of the map. - for (size_t j = 1; j <= i; ++j) { - const auto it = sm.find(j); - EXPECT_TRUE(it != sm.end()); - EXPECT_EQ(it->first, j); - EXPECT_EQ(it->second.value(), j * 100); - } - EXPECT_EQ(sm.size(), i); - EXPECT_FALSE(sm.empty()); - } -} - -} // namespace base diff --git a/containers/span.h b/containers/span.h deleted file mode 100644 index f1e000056..000000000 --- a/containers/span.h +++ /dev/null @@ -1,453 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_SPAN_H_ -#define BASE_CONTAINERS_SPAN_H_ - -#include - -#include -#include -#include -#include -#include - -#include "base/logging.h" -#include "base/stl_util.h" - -namespace base { - -// [views.constants] -constexpr size_t dynamic_extent = static_cast(-1); - -template -class span; - -namespace internal { - -template -struct IsSpanImpl : std::false_type {}; - -template -struct IsSpanImpl> : std::true_type {}; - -template -using IsSpan = IsSpanImpl>; - -template -struct IsStdArrayImpl : std::false_type {}; - -template -struct IsStdArrayImpl> : std::true_type {}; - -template -using IsStdArray = IsStdArrayImpl>; - -template -using IsCArray = std::is_array>; - -template -using IsLegalDataConversion = std::is_convertible; - -template -using ContainerHasConvertibleData = IsLegalDataConversion< - std::remove_pointer_t()))>, - T>; - -template -using ContainerHasIntegralSize = - std::is_integral()))>; - -template -using EnableIfLegalSpanConversion = - std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) && - IsLegalDataConversion::value>; - -// SFINAE check if Array can be converted to a span. -template -using EnableIfSpanCompatibleArray = - std::enable_if_t<(Extent == dynamic_extent || Extent == N) && - ContainerHasConvertibleData::value>; - -// SFINAE check if Container can be converted to a span. -template -using EnableIfSpanCompatibleContainer = - std::enable_if_t::value && - !internal::IsStdArray::value && - !internal::IsCArray::value && - ContainerHasConvertibleData::value && - ContainerHasIntegralSize::value>; - -} // namespace internal - -// A span is a value type that represents an array of elements of type T. Since -// it only consists of a pointer to memory with an associated size, it is very -// light-weight. It is cheap to construct, copy, move and use spans, so that -// users are encouraged to use it as a pass-by-value parameter. A span does not -// own the underlying memory, so care must be taken to ensure that a span does -// not outlive the backing store. -// -// span is somewhat analogous to StringPiece, but with arbitrary element types, -// allowing mutation if T is non-const. -// -// span is implicitly convertible from C++ arrays, as well as most [1] -// container-like types that provide a data() and size() method (such as -// std::vector). A mutable span can also be implicitly converted to an -// immutable span. -// -// Consider using a span for functions that take a data pointer and size -// parameter: it allows the function to still act on an array-like type, while -// allowing the caller code to be a bit more concise. -// -// For read-only data access pass a span: the caller can supply either -// a span or a span, while the callee will have a read-only view. -// For read-write access a mutable span is required. -// -// Without span: -// Read-Only: -// // std::string HexEncode(const uint8_t* data, size_t size); -// std::vector data_buffer = GenerateData(); -// std::string r = HexEncode(data_buffer.data(), data_buffer.size()); -// -// Mutable: -// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...); -// char str_buffer[100]; -// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14); -// -// With span: -// Read-Only: -// // std::string HexEncode(base::span data); -// std::vector data_buffer = GenerateData(); -// std::string r = HexEncode(data_buffer); -// -// Mutable: -// // ssize_t SafeSNPrintf(base::span, const char* fmt, Args...); -// char str_buffer[100]; -// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14); -// -// Spans with "const" and pointers -// ------------------------------- -// -// Const and pointers can get confusing. Here are vectors of pointers and their -// corresponding spans: -// -// const std::vector => base::span -// std::vector => base::span -// const std::vector => base::span -// -// Differences from the working group proposal -// ------------------------------------------- -// -// https://wg21.link/P0122 is the latest working group proposal, Chromium -// currently implements R7. Differences between the proposal and the -// implementation are documented in subsections below. -// -// Differences from [span.objectrep]: -// - as_bytes() and as_writable_bytes() return spans of uint8_t instead of -// std::byte -// -// Differences in constants and types: -// - index_type is aliased to size_t -// -// Differences from [span.sub]: -// - using size_t instead of ptrdiff_t for indexing -// -// Differences from [span.obs]: -// - using size_t instead of ptrdiff_t to represent size() -// -// Differences from [span.elem]: -// - using size_t instead of ptrdiff_t for indexing -// -// Furthermore, all constructors and methods are marked noexcept due to the lack -// of exceptions in Chromium. -// -// Due to the lack of class template argument deduction guides in C++14 -// appropriate make_span() utility functions are provided. - -// [span], class template span -template -class span { - public: - using element_type = T; - using value_type = std::remove_cv_t; - using index_type = size_t; - using difference_type = ptrdiff_t; - using pointer = T*; - using reference = T&; - using iterator = T*; - using const_iterator = const T*; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - static constexpr index_type extent = Extent; - - // [span.cons], span constructors, copy, assignment, and destructor - constexpr span() noexcept : data_(nullptr), size_(0) { - static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent"); - } - - constexpr span(T* data, size_t size) noexcept : data_(data), size_(size) { - CHECK(Extent == dynamic_extent || Extent == size); - } - - // Artificially templatized to break ambiguity for span(ptr, 0). - template - constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) { - // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. - CHECK(begin <= end); - } - - template < - size_t N, - typename = internal::EnableIfSpanCompatibleArray> - constexpr span(T (&array)[N]) noexcept : span(base::data(array), N) {} - - template < - size_t N, - typename = internal:: - EnableIfSpanCompatibleArray&, N, T, Extent>> - constexpr span(std::array& array) noexcept - : span(base::data(array), N) {} - - template &, - N, - T, - Extent>> - constexpr span(const std::array& array) noexcept - : span(base::data(array), N) {} - - // Conversion from a container that has compatible base::data() and integral - // base::size(). - template > - constexpr span(Container& container) noexcept - : span(base::data(container), base::size(container)) {} - - template < - typename Container, - typename = internal::EnableIfSpanCompatibleContainer> - span(const Container& container) noexcept - : span(base::data(container), base::size(container)) {} - - constexpr span(const span& other) noexcept = default; - - // Conversions from spans of compatible types and extents: this allows a - // span to be seamlessly used as a span, but not the other way - // around. If extent is not dynamic, OtherExtent has to be equal to Extent. - template < - typename U, - size_t OtherExtent, - typename = - internal::EnableIfLegalSpanConversion> - constexpr span(const span& other) - : span(other.data(), other.size()) {} - - constexpr span& operator=(const span& other) noexcept = default; - ~span() noexcept = default; - - // [span.sub], span subviews - template - constexpr span first() const noexcept { - static_assert(Extent == dynamic_extent || Count <= Extent, - "Count must not exceed Extent"); - CHECK(Extent != dynamic_extent || Count <= size()); - return {data(), Count}; - } - - template - constexpr span last() const noexcept { - static_assert(Extent == dynamic_extent || Count <= Extent, - "Count must not exceed Extent"); - CHECK(Extent != dynamic_extent || Count <= size()); - return {data() + (size() - Count), Count}; - } - - template - constexpr span - subspan() const noexcept { - static_assert(Extent == dynamic_extent || Offset <= Extent, - "Offset must not exceed Extent"); - static_assert(Extent == dynamic_extent || Count == dynamic_extent || - Count <= Extent - Offset, - "Count must not exceed Extent - Offset"); - CHECK(Extent != dynamic_extent || Offset <= size()); - CHECK(Extent != dynamic_extent || Count == dynamic_extent || - Count <= size() - Offset); - return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset}; - } - - constexpr span first(size_t count) const noexcept { - // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. - CHECK(count <= size()); - return {data(), count}; - } - - constexpr span last(size_t count) const noexcept { - // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. - CHECK(count <= size()); - return {data() + (size() - count), count}; - } - - constexpr span subspan(size_t offset, - size_t count = dynamic_extent) const - noexcept { - // Note: CHECK_LE is not constexpr, hence regular CHECK must be used. - CHECK(offset <= size()); - CHECK(count == dynamic_extent || count <= size() - offset); - return {data() + offset, count != dynamic_extent ? count : size() - offset}; - } - - // [span.obs], span observers - constexpr size_t size() const noexcept { return size_; } - constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); } - constexpr bool empty() const noexcept { return size() == 0; } - - // [span.elem], span element access - constexpr T& operator[](size_t idx) const noexcept { - // Note: CHECK_LT is not constexpr, hence regular CHECK must be used. - CHECK(idx < size()); - return *(data() + idx); - } - - constexpr T& operator()(size_t idx) const noexcept { - // Note: CHECK_LT is not constexpr, hence regular CHECK must be used. - CHECK(idx < size()); - return *(data() + idx); - } - - constexpr T* data() const noexcept { return data_; } - - // [span.iter], span iterator support - constexpr iterator begin() const noexcept { return data(); } - constexpr iterator end() const noexcept { return data() + size(); } - - constexpr const_iterator cbegin() const noexcept { return begin(); } - constexpr const_iterator cend() const noexcept { return end(); } - - constexpr reverse_iterator rbegin() const noexcept { - return reverse_iterator(end()); - } - constexpr reverse_iterator rend() const noexcept { - return reverse_iterator(begin()); - } - - constexpr const_reverse_iterator crbegin() const noexcept { - return const_reverse_iterator(cend()); - } - constexpr const_reverse_iterator crend() const noexcept { - return const_reverse_iterator(cbegin()); - } - - private: - T* data_; - size_t size_; -}; - -// span::extent can not be declared inline prior to C++17, hence this -// definition is required. -template -constexpr size_t span::extent; - -// [span.comparison], span comparison operators -// Relational operators. Equality is a element-wise comparison. -template -constexpr bool operator==(span lhs, span rhs) noexcept { - return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()); -} - -template -constexpr bool operator!=(span lhs, span rhs) noexcept { - return !(lhs == rhs); -} - -template -constexpr bool operator<(span lhs, span rhs) noexcept { - return std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), - rhs.cend()); -} - -template -constexpr bool operator<=(span lhs, span rhs) noexcept { - return !(rhs < lhs); -} - -template -constexpr bool operator>(span lhs, span rhs) noexcept { - return rhs < lhs; -} - -template -constexpr bool operator>=(span lhs, span rhs) noexcept { - return !(lhs < rhs); -} - -// [span.objectrep], views of object representation -template -span -as_bytes(span s) noexcept { - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -template ::value>> -span -as_writable_bytes(span s) noexcept { - return {reinterpret_cast(s.data()), s.size_bytes()}; -} - -// Type-deducing helpers for constructing a span. -template -constexpr span make_span(T* data, size_t size) noexcept { - return {data, size}; -} - -template -constexpr span make_span(T* begin, T* end) noexcept { - return {begin, end}; -} - -template -constexpr span make_span(T (&array)[N]) noexcept { - return array; -} - -template -constexpr span make_span(std::array& array) noexcept { - return array; -} - -template -constexpr span make_span(const std::array& array) noexcept { - return array; -} - -template > -constexpr span make_span(Container& container) noexcept { - return container; -} - -template < - typename Container, - typename T = const typename Container::value_type, - typename = internal::EnableIfSpanCompatibleContainer> -constexpr span make_span(const Container& container) noexcept { - return container; -} - -template -constexpr span make_span(const span& span) noexcept { - return span; -} - -} // namespace base - -#endif // BASE_CONTAINERS_SPAN_H_ diff --git a/containers/span_unittest.cc b/containers/span_unittest.cc deleted file mode 100644 index de5e40186..000000000 --- a/containers/span_unittest.cc +++ /dev/null @@ -1,1170 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/containers/span.h" - -#include - -#include -#include -#include -#include - -#include "base/macros.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::ElementsAre; -using ::testing::Eq; -using ::testing::Pointwise; - -namespace base { - -TEST(SpanTest, DefaultConstructor) { - span dynamic_span; - EXPECT_EQ(nullptr, dynamic_span.data()); - EXPECT_EQ(0u, dynamic_span.size()); - - constexpr span static_span; - static_assert(nullptr == static_span.data(), ""); - static_assert(0u == static_span.size(), ""); -} - -TEST(SpanTest, ConstructFromDataAndSize) { - constexpr span empty_span(nullptr, 0); - EXPECT_TRUE(empty_span.empty()); - EXPECT_EQ(nullptr, empty_span.data()); - - std::vector vector = {1, 1, 2, 3, 5, 8}; - - span dynamic_span(vector.data(), vector.size()); - EXPECT_EQ(vector.data(), dynamic_span.data()); - EXPECT_EQ(vector.size(), dynamic_span.size()); - - for (size_t i = 0; i < dynamic_span.size(); ++i) - EXPECT_EQ(vector[i], dynamic_span[i]); - - span static_span(vector.data(), vector.size()); - EXPECT_EQ(vector.data(), static_span.data()); - EXPECT_EQ(vector.size(), static_span.size()); - - for (size_t i = 0; i < static_span.size(); ++i) - EXPECT_EQ(vector[i], static_span[i]); -} - -TEST(SpanTest, ConstructFromPointerPair) { - constexpr span empty_span(nullptr, nullptr); - EXPECT_TRUE(empty_span.empty()); - EXPECT_EQ(nullptr, empty_span.data()); - - std::vector vector = {1, 1, 2, 3, 5, 8}; - - span dynamic_span(vector.data(), vector.data() + vector.size() / 2); - EXPECT_EQ(vector.data(), dynamic_span.data()); - EXPECT_EQ(vector.size() / 2, dynamic_span.size()); - - for (size_t i = 0; i < dynamic_span.size(); ++i) - EXPECT_EQ(vector[i], dynamic_span[i]); - - span static_span(vector.data(), vector.data() + vector.size() / 2); - EXPECT_EQ(vector.data(), static_span.data()); - EXPECT_EQ(vector.size() / 2, static_span.size()); - - for (size_t i = 0; i < static_span.size(); ++i) - EXPECT_EQ(vector[i], static_span[i]); -} - -TEST(SpanTest, ConstructFromConstexprArray) { - static constexpr int kArray[] = {5, 4, 3, 2, 1}; - - constexpr span dynamic_span(kArray); - static_assert(kArray == dynamic_span.data(), ""); - static_assert(base::size(kArray) == dynamic_span.size(), ""); - - static_assert(kArray[0] == dynamic_span[0], ""); - static_assert(kArray[1] == dynamic_span[1], ""); - static_assert(kArray[2] == dynamic_span[2], ""); - static_assert(kArray[3] == dynamic_span[3], ""); - static_assert(kArray[4] == dynamic_span[4], ""); - - constexpr span static_span(kArray); - static_assert(kArray == static_span.data(), ""); - static_assert(base::size(kArray) == static_span.size(), ""); - - static_assert(kArray[0] == static_span[0], ""); - static_assert(kArray[1] == static_span[1], ""); - static_assert(kArray[2] == static_span[2], ""); - static_assert(kArray[3] == static_span[3], ""); - static_assert(kArray[4] == static_span[4], ""); -} - -TEST(SpanTest, ConstructFromArray) { - int array[] = {5, 4, 3, 2, 1}; - - span const_span(array); - EXPECT_EQ(array, const_span.data()); - EXPECT_EQ(arraysize(array), const_span.size()); - for (size_t i = 0; i < const_span.size(); ++i) - EXPECT_EQ(array[i], const_span[i]); - - span dynamic_span(array); - EXPECT_EQ(array, dynamic_span.data()); - EXPECT_EQ(base::size(array), dynamic_span.size()); - for (size_t i = 0; i < dynamic_span.size(); ++i) - EXPECT_EQ(array[i], dynamic_span[i]); - - span static_span(array); - EXPECT_EQ(array, static_span.data()); - EXPECT_EQ(base::size(array), static_span.size()); - for (size_t i = 0; i < static_span.size(); ++i) - EXPECT_EQ(array[i], static_span[i]); -} - -TEST(SpanTest, ConstructFromStdArray) { - // Note: Constructing a constexpr span from a constexpr std::array does not - // work prior to C++17 due to non-constexpr std::array::data. - std::array array = {{5, 4, 3, 2, 1}}; - - span const_span(array); - EXPECT_EQ(array.data(), const_span.data()); - EXPECT_EQ(array.size(), const_span.size()); - for (size_t i = 0; i < const_span.size(); ++i) - EXPECT_EQ(array[i], const_span[i]); - - span dynamic_span(array); - EXPECT_EQ(array.data(), dynamic_span.data()); - EXPECT_EQ(array.size(), dynamic_span.size()); - for (size_t i = 0; i < dynamic_span.size(); ++i) - EXPECT_EQ(array[i], dynamic_span[i]); - - span static_span(array); - EXPECT_EQ(array.data(), static_span.data()); - EXPECT_EQ(array.size(), static_span.size()); - for (size_t i = 0; i < static_span.size(); ++i) - EXPECT_EQ(array[i], static_span[i]); -} - -TEST(SpanTest, ConstructFromInitializerList) { - std::initializer_list il = {1, 1, 2, 3, 5, 8}; - - span const_span(il); - EXPECT_EQ(il.begin(), const_span.data()); - EXPECT_EQ(il.size(), const_span.size()); - - for (size_t i = 0; i < const_span.size(); ++i) - EXPECT_EQ(il.begin()[i], const_span[i]); - - span static_span(il); - EXPECT_EQ(il.begin(), static_span.data()); - EXPECT_EQ(il.size(), static_span.size()); - - for (size_t i = 0; i < static_span.size(); ++i) - EXPECT_EQ(il.begin()[i], static_span[i]); -} - -TEST(SpanTest, ConstructFromStdString) { - std::string str = "foobar"; - - span const_span(str); - EXPECT_EQ(str.data(), const_span.data()); - EXPECT_EQ(str.size(), const_span.size()); - - for (size_t i = 0; i < const_span.size(); ++i) - EXPECT_EQ(str[i], const_span[i]); - - span dynamic_span(str); - EXPECT_EQ(str.data(), dynamic_span.data()); - EXPECT_EQ(str.size(), dynamic_span.size()); - - for (size_t i = 0; i < dynamic_span.size(); ++i) - EXPECT_EQ(str[i], dynamic_span[i]); - - span static_span(str); - EXPECT_EQ(str.data(), static_span.data()); - EXPECT_EQ(str.size(), static_span.size()); - - for (size_t i = 0; i < static_span.size(); ++i) - EXPECT_EQ(str[i], static_span[i]); -} - -TEST(SpanTest, ConstructFromConstContainer) { - const std::vector vector = {1, 1, 2, 3, 5, 8}; - - span const_span(vector); - EXPECT_EQ(vector.data(), const_span.data()); - EXPECT_EQ(vector.size(), const_span.size()); - - for (size_t i = 0; i < const_span.size(); ++i) - EXPECT_EQ(vector[i], const_span[i]); - - span static_span(vector); - EXPECT_EQ(vector.data(), static_span.data()); - EXPECT_EQ(vector.size(), static_span.size()); - - for (size_t i = 0; i < static_span.size(); ++i) - EXPECT_EQ(vector[i], static_span[i]); -} - -TEST(SpanTest, ConstructFromContainer) { - std::vector vector = {1, 1, 2, 3, 5, 8}; - - span const_span(vector); - EXPECT_EQ(vector.data(), const_span.data()); - EXPECT_EQ(vector.size(), const_span.size()); - - for (size_t i = 0; i < const_span.size(); ++i) - EXPECT_EQ(vector[i], const_span[i]); - - span dynamic_span(vector); - EXPECT_EQ(vector.data(), dynamic_span.data()); - EXPECT_EQ(vector.size(), dynamic_span.size()); - - for (size_t i = 0; i < dynamic_span.size(); ++i) - EXPECT_EQ(vector[i], dynamic_span[i]); - - span static_span(vector); - EXPECT_EQ(vector.data(), static_span.data()); - EXPECT_EQ(vector.size(), static_span.size()); - - for (size_t i = 0; i < static_span.size(); ++i) - EXPECT_EQ(vector[i], static_span[i]); -} - -TEST(SpanTest, ConvertNonConstIntegralToConst) { - std::vector vector = {1, 1, 2, 3, 5, 8}; - - span int_span(vector.data(), vector.size()); - span const_span(int_span); - EXPECT_THAT(const_span, Pointwise(Eq(), int_span)); - - span static_int_span(vector.data(), vector.size()); - span static_const_span(static_int_span); - EXPECT_THAT(static_const_span, Pointwise(Eq(), static_int_span)); -} - -TEST(SpanTest, ConvertNonConstPointerToConst) { - auto a = std::make_unique(11); - auto b = std::make_unique(22); - auto c = std::make_unique(33); - std::vector vector = {a.get(), b.get(), c.get()}; - - span non_const_pointer_span(vector); - EXPECT_THAT(non_const_pointer_span, Pointwise(Eq(), vector)); - span const_pointer_span(non_const_pointer_span); - EXPECT_THAT(const_pointer_span, Pointwise(Eq(), non_const_pointer_span)); - // Note: no test for conversion from span to span, since that - // would imply a conversion from int** to const int**, which is unsafe. - // - // Note: no test for conversion from span to span, - // due to CWG Defect 330: - // http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#330 - - span static_non_const_pointer_span(vector); - EXPECT_THAT(static_non_const_pointer_span, Pointwise(Eq(), vector)); - span static_const_pointer_span(static_non_const_pointer_span); - EXPECT_THAT(static_const_pointer_span, - Pointwise(Eq(), static_non_const_pointer_span)); -} - -TEST(SpanTest, ConvertBetweenEquivalentTypes) { - std::vector vector = {2, 4, 8, 16, 32}; - - span int32_t_span(vector); - span converted_span(int32_t_span); - EXPECT_EQ(int32_t_span, converted_span); - - span static_int32_t_span(vector); - span static_converted_span(static_int32_t_span); - EXPECT_EQ(static_int32_t_span, static_converted_span); -} - -TEST(SpanTest, TemplatedFirst) { - static constexpr int array[] = {1, 2, 3}; - constexpr span span(array); - - { - constexpr auto subspan = span.first<0>(); - static_assert(span.data() == subspan.data(), ""); - static_assert(0u == subspan.size(), ""); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - constexpr auto subspan = span.first<1>(); - static_assert(span.data() == subspan.data(), ""); - static_assert(1u == subspan.size(), ""); - static_assert(1u == decltype(subspan)::extent, ""); - static_assert(1 == subspan[0], ""); - } - - { - constexpr auto subspan = span.first<2>(); - static_assert(span.data() == subspan.data(), ""); - static_assert(2u == subspan.size(), ""); - static_assert(2u == decltype(subspan)::extent, ""); - static_assert(1 == subspan[0], ""); - static_assert(2 == subspan[1], ""); - } - - { - constexpr auto subspan = span.first<3>(); - static_assert(span.data() == subspan.data(), ""); - static_assert(3u == subspan.size(), ""); - static_assert(3u == decltype(subspan)::extent, ""); - static_assert(1 == subspan[0], ""); - static_assert(2 == subspan[1], ""); - static_assert(3 == subspan[2], ""); - } -} - -TEST(SpanTest, TemplatedLast) { - static constexpr int array[] = {1, 2, 3}; - constexpr span span(array); - - { - constexpr auto subspan = span.last<0>(); - static_assert(span.data() + 3 == subspan.data(), ""); - static_assert(0u == subspan.size(), ""); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - constexpr auto subspan = span.last<1>(); - static_assert(span.data() + 2 == subspan.data(), ""); - static_assert(1u == subspan.size(), ""); - static_assert(1u == decltype(subspan)::extent, ""); - static_assert(3 == subspan[0], ""); - } - - { - constexpr auto subspan = span.last<2>(); - static_assert(span.data() + 1 == subspan.data(), ""); - static_assert(2u == subspan.size(), ""); - static_assert(2u == decltype(subspan)::extent, ""); - static_assert(2 == subspan[0], ""); - static_assert(3 == subspan[1], ""); - } - - { - constexpr auto subspan = span.last<3>(); - static_assert(span.data() == subspan.data(), ""); - static_assert(3u == subspan.size(), ""); - static_assert(3u == decltype(subspan)::extent, ""); - static_assert(1 == subspan[0], ""); - static_assert(2 == subspan[1], ""); - static_assert(3 == subspan[2], ""); - } -} - -TEST(SpanTest, TemplatedSubspan) { - static constexpr int array[] = {1, 2, 3}; - constexpr span span(array); - - { - constexpr auto subspan = span.subspan<0>(); - static_assert(span.data() == subspan.data(), ""); - static_assert(3u == subspan.size(), ""); - static_assert(3u == decltype(subspan)::extent, ""); - static_assert(1 == subspan[0], ""); - static_assert(2 == subspan[1], ""); - static_assert(3 == subspan[2], ""); - } - - { - constexpr auto subspan = span.subspan<1>(); - static_assert(span.data() + 1 == subspan.data(), ""); - static_assert(2u == subspan.size(), ""); - static_assert(2u == decltype(subspan)::extent, ""); - static_assert(2 == subspan[0], ""); - static_assert(3 == subspan[1], ""); - } - - { - constexpr auto subspan = span.subspan<2>(); - static_assert(span.data() + 2 == subspan.data(), ""); - static_assert(1u == subspan.size(), ""); - static_assert(1u == decltype(subspan)::extent, ""); - static_assert(3 == subspan[0], ""); - } - - { - constexpr auto subspan = span.subspan<3>(); - static_assert(span.data() + 3 == subspan.data(), ""); - static_assert(0u == subspan.size(), ""); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - constexpr auto subspan = span.subspan<0, 0>(); - static_assert(span.data() == subspan.data(), ""); - static_assert(0u == subspan.size(), ""); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - constexpr auto subspan = span.subspan<1, 0>(); - static_assert(span.data() + 1 == subspan.data(), ""); - static_assert(0u == subspan.size(), ""); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - constexpr auto subspan = span.subspan<2, 0>(); - static_assert(span.data() + 2 == subspan.data(), ""); - static_assert(0u == subspan.size(), ""); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - constexpr auto subspan = span.subspan<0, 1>(); - static_assert(span.data() == subspan.data(), ""); - static_assert(1u == subspan.size(), ""); - static_assert(1u == decltype(subspan)::extent, ""); - static_assert(1 == subspan[0], ""); - } - - { - constexpr auto subspan = span.subspan<1, 1>(); - static_assert(span.data() + 1 == subspan.data(), ""); - static_assert(1u == subspan.size(), ""); - static_assert(1u == decltype(subspan)::extent, ""); - static_assert(2 == subspan[0], ""); - } - - { - constexpr auto subspan = span.subspan<2, 1>(); - static_assert(span.data() + 2 == subspan.data(), ""); - static_assert(1u == subspan.size(), ""); - static_assert(1u == decltype(subspan)::extent, ""); - static_assert(3 == subspan[0], ""); - } - - { - constexpr auto subspan = span.subspan<0, 2>(); - static_assert(span.data() == subspan.data(), ""); - static_assert(2u == subspan.size(), ""); - static_assert(2u == decltype(subspan)::extent, ""); - static_assert(1 == subspan[0], ""); - static_assert(2 == subspan[1], ""); - } - - { - constexpr auto subspan = span.subspan<1, 2>(); - static_assert(span.data() + 1 == subspan.data(), ""); - static_assert(2u == subspan.size(), ""); - static_assert(2u == decltype(subspan)::extent, ""); - static_assert(2 == subspan[0], ""); - static_assert(3 == subspan[1], ""); - } - - { - constexpr auto subspan = span.subspan<0, 3>(); - static_assert(span.data() == subspan.data(), ""); - static_assert(3u == subspan.size(), ""); - static_assert(3u == decltype(subspan)::extent, ""); - static_assert(1 == subspan[0], ""); - static_assert(2 == subspan[1], ""); - static_assert(3 == subspan[2], ""); - } -} - -TEST(SpanTest, TemplatedFirstOnDynamicSpan) { - int array[] = {1, 2, 3}; - span span(array); - - { - auto subspan = span.first<0>(); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(0u, subspan.size()); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - auto subspan = span.first<1>(); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(1u, subspan.size()); - static_assert(1u == decltype(subspan)::extent, ""); - EXPECT_EQ(1, subspan[0]); - } - - { - auto subspan = span.first<2>(); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(2u, subspan.size()); - static_assert(2u == decltype(subspan)::extent, ""); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - } - - { - auto subspan = span.first<3>(); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(3u, subspan.size()); - static_assert(3u == decltype(subspan)::extent, ""); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - EXPECT_EQ(3, subspan[2]); - } -} - -TEST(SpanTest, TemplatedLastOnDynamicSpan) { - int array[] = {1, 2, 3}; - span span(array); - - { - auto subspan = span.last<0>(); - EXPECT_EQ(span.data() + 3, subspan.data()); - EXPECT_EQ(0u, subspan.size()); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - auto subspan = span.last<1>(); - EXPECT_EQ(span.data() + 2, subspan.data()); - EXPECT_EQ(1u, subspan.size()); - static_assert(1u == decltype(subspan)::extent, ""); - EXPECT_EQ(3, subspan[0]); - } - - { - auto subspan = span.last<2>(); - EXPECT_EQ(span.data() + 1, subspan.data()); - EXPECT_EQ(2u, subspan.size()); - static_assert(2u == decltype(subspan)::extent, ""); - EXPECT_EQ(2, subspan[0]); - EXPECT_EQ(3, subspan[1]); - } - - { - auto subspan = span.last<3>(); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(3u, subspan.size()); - static_assert(3u == decltype(subspan)::extent, ""); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - EXPECT_EQ(3, subspan[2]); - } -} - -TEST(SpanTest, TemplatedSubspanFromDynamicSpan) { - int array[] = {1, 2, 3}; - span span(array); - - { - auto subspan = span.subspan<0>(); - EXPECT_EQ(span.data(), subspan.data()); - static_assert(3u == decltype(subspan)::extent, ""); - EXPECT_EQ(3u, subspan.size()); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - EXPECT_EQ(3, subspan[2]); - } - - { - auto subspan = span.subspan<1>(); - EXPECT_EQ(span.data() + 1, subspan.data()); - EXPECT_EQ(2u, subspan.size()); - static_assert(2u == decltype(subspan)::extent, ""); - EXPECT_EQ(2, subspan[0]); - EXPECT_EQ(3, subspan[1]); - } - - { - auto subspan = span.subspan<2>(); - EXPECT_EQ(span.data() + 2, subspan.data()); - EXPECT_EQ(1u, subspan.size()); - static_assert(1u == decltype(subspan)::extent, ""); - EXPECT_EQ(3, subspan[0]); - } - - { - auto subspan = span.subspan<3>(); - EXPECT_EQ(span.data() + 3, subspan.data()); - EXPECT_EQ(0u, subspan.size()); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - auto subspan = span.subspan<0, 0>(); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(0u, subspan.size()); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - auto subspan = span.subspan<1, 0>(); - EXPECT_EQ(span.data() + 1, subspan.data()); - EXPECT_EQ(0u, subspan.size()); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - auto subspan = span.subspan<2, 0>(); - EXPECT_EQ(span.data() + 2, subspan.data()); - EXPECT_EQ(0u, subspan.size()); - static_assert(0u == decltype(subspan)::extent, ""); - } - - { - auto subspan = span.subspan<0, 1>(); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(1u, subspan.size()); - static_assert(1u == decltype(subspan)::extent, ""); - EXPECT_EQ(1, subspan[0]); - } - - { - auto subspan = span.subspan<1, 1>(); - EXPECT_EQ(span.data() + 1, subspan.data()); - EXPECT_EQ(1u, subspan.size()); - static_assert(1u == decltype(subspan)::extent, ""); - EXPECT_EQ(2, subspan[0]); - } - - { - auto subspan = span.subspan<2, 1>(); - EXPECT_EQ(span.data() + 2, subspan.data()); - EXPECT_EQ(1u, subspan.size()); - static_assert(1u == decltype(subspan)::extent, ""); - EXPECT_EQ(3, subspan[0]); - } - - { - auto subspan = span.subspan<0, 2>(); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(2u, subspan.size()); - static_assert(2u == decltype(subspan)::extent, ""); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - } - - { - auto subspan = span.subspan<1, 2>(); - EXPECT_EQ(span.data() + 1, subspan.data()); - EXPECT_EQ(2u, subspan.size()); - static_assert(2u == decltype(subspan)::extent, ""); - EXPECT_EQ(2, subspan[0]); - EXPECT_EQ(3, subspan[1]); - } - - { - auto subspan = span.subspan<0, 3>(); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(3u, subspan.size()); - static_assert(3u == decltype(subspan)::extent, ""); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - EXPECT_EQ(3, subspan[2]); - } -} - -TEST(SpanTest, First) { - int array[] = {1, 2, 3}; - span span(array); - - { - auto subspan = span.first(0); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(0u, subspan.size()); - } - - { - auto subspan = span.first(1); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(1u, subspan.size()); - EXPECT_EQ(1, subspan[0]); - } - - { - auto subspan = span.first(2); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(2u, subspan.size()); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - } - - { - auto subspan = span.first(3); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(3u, subspan.size()); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - EXPECT_EQ(3, subspan[2]); - } -} - -TEST(SpanTest, Last) { - int array[] = {1, 2, 3}; - span span(array); - - { - auto subspan = span.last(0); - EXPECT_EQ(span.data() + 3, subspan.data()); - EXPECT_EQ(0u, subspan.size()); - } - - { - auto subspan = span.last(1); - EXPECT_EQ(span.data() + 2, subspan.data()); - EXPECT_EQ(1u, subspan.size()); - EXPECT_EQ(3, subspan[0]); - } - - { - auto subspan = span.last(2); - EXPECT_EQ(span.data() + 1, subspan.data()); - EXPECT_EQ(2u, subspan.size()); - EXPECT_EQ(2, subspan[0]); - EXPECT_EQ(3, subspan[1]); - } - - { - auto subspan = span.last(3); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(3u, subspan.size()); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - EXPECT_EQ(3, subspan[2]); - } -} - -TEST(SpanTest, Subspan) { - int array[] = {1, 2, 3}; - span span(array); - - { - auto subspan = span.subspan(0); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(3u, subspan.size()); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - EXPECT_EQ(3, subspan[2]); - } - - { - auto subspan = span.subspan(1); - EXPECT_EQ(span.data() + 1, subspan.data()); - EXPECT_EQ(2u, subspan.size()); - EXPECT_EQ(2, subspan[0]); - EXPECT_EQ(3, subspan[1]); - } - - { - auto subspan = span.subspan(2); - EXPECT_EQ(span.data() + 2, subspan.data()); - EXPECT_EQ(1u, subspan.size()); - EXPECT_EQ(3, subspan[0]); - } - - { - auto subspan = span.subspan(3); - EXPECT_EQ(span.data() + 3, subspan.data()); - EXPECT_EQ(0u, subspan.size()); - } - - { - auto subspan = span.subspan(0, 0); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(0u, subspan.size()); - } - - { - auto subspan = span.subspan(1, 0); - EXPECT_EQ(span.data() + 1, subspan.data()); - EXPECT_EQ(0u, subspan.size()); - } - - { - auto subspan = span.subspan(2, 0); - EXPECT_EQ(span.data() + 2, subspan.data()); - EXPECT_EQ(0u, subspan.size()); - } - - { - auto subspan = span.subspan(0, 1); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(1u, subspan.size()); - EXPECT_EQ(1, subspan[0]); - } - - { - auto subspan = span.subspan(1, 1); - EXPECT_EQ(span.data() + 1, subspan.data()); - EXPECT_EQ(1u, subspan.size()); - EXPECT_EQ(2, subspan[0]); - } - - { - auto subspan = span.subspan(2, 1); - EXPECT_EQ(span.data() + 2, subspan.data()); - EXPECT_EQ(1u, subspan.size()); - EXPECT_EQ(3, subspan[0]); - } - - { - auto subspan = span.subspan(0, 2); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(2u, subspan.size()); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - } - - { - auto subspan = span.subspan(1, 2); - EXPECT_EQ(span.data() + 1, subspan.data()); - EXPECT_EQ(2u, subspan.size()); - EXPECT_EQ(2, subspan[0]); - EXPECT_EQ(3, subspan[1]); - } - - { - auto subspan = span.subspan(0, 3); - EXPECT_EQ(span.data(), subspan.data()); - EXPECT_EQ(span.size(), subspan.size()); - EXPECT_EQ(1, subspan[0]); - EXPECT_EQ(2, subspan[1]); - EXPECT_EQ(3, subspan[2]); - } -} - -TEST(SpanTest, Size) { - { - span span; - EXPECT_EQ(0u, span.size()); - } - - { - int array[] = {1, 2, 3}; - span span(array); - EXPECT_EQ(3u, span.size()); - } -} - -TEST(SpanTest, SizeBytes) { - { - span span; - EXPECT_EQ(0u, span.size_bytes()); - } - - { - int array[] = {1, 2, 3}; - span span(array); - EXPECT_EQ(3u * sizeof(int), span.size_bytes()); - } -} - -TEST(SpanTest, Empty) { - { - span span; - EXPECT_TRUE(span.empty()); - } - - { - int array[] = {1, 2, 3}; - span span(array); - EXPECT_FALSE(span.empty()); - } -} - -TEST(SpanTest, OperatorAt) { - static constexpr int kArray[] = {1, 6, 1, 8, 0}; - constexpr span span(kArray); - - static_assert(kArray[0] == span[0], "span[0] does not equal kArray[0]"); - static_assert(kArray[1] == span[1], "span[1] does not equal kArray[1]"); - static_assert(kArray[2] == span[2], "span[2] does not equal kArray[2]"); - static_assert(kArray[3] == span[3], "span[3] does not equal kArray[3]"); - static_assert(kArray[4] == span[4], "span[4] does not equal kArray[4]"); - - static_assert(kArray[0] == span(0), "span(0) does not equal kArray[0]"); - static_assert(kArray[1] == span(1), "span(1) does not equal kArray[1]"); - static_assert(kArray[2] == span(2), "span(2) does not equal kArray[2]"); - static_assert(kArray[3] == span(3), "span(3) does not equal kArray[3]"); - static_assert(kArray[4] == span(4), "span(4) does not equal kArray[4]"); -} - -TEST(SpanTest, Iterator) { - static constexpr int kArray[] = {1, 6, 1, 8, 0}; - constexpr span span(kArray); - - std::vector results; - for (int i : span) - results.emplace_back(i); - EXPECT_THAT(results, ElementsAre(1, 6, 1, 8, 0)); -} - -TEST(SpanTest, ReverseIterator) { - static constexpr int kArray[] = {1, 6, 1, 8, 0}; - constexpr span span(kArray); - - EXPECT_TRUE(std::equal(std::rbegin(kArray), std::rend(kArray), span.rbegin(), - span.rend())); - EXPECT_TRUE(std::equal(std::crbegin(kArray), std::crend(kArray), - span.crbegin(), span.crend())); -} - -TEST(SpanTest, Equality) { - static constexpr int kArray1[] = {3, 1, 4, 1, 5}; - static constexpr int kArray2[] = {3, 1, 4, 1, 5}; - constexpr span span1(kArray1); - constexpr span span2(kArray2); - - EXPECT_EQ(span1, span2); - - static constexpr int kArray3[] = {2, 7, 1, 8, 3}; - constexpr span span3(kArray3); - - EXPECT_FALSE(span1 == span3); - - static double kArray4[] = {2.0, 7.0, 1.0, 8.0, 3.0}; - span span4(kArray4); - - EXPECT_EQ(span3, span4); -} - -TEST(SpanTest, Inequality) { - static constexpr int kArray1[] = {2, 3, 5, 7, 11}; - static constexpr int kArray2[] = {1, 4, 6, 8, 9}; - constexpr span span1(kArray1); - constexpr span span2(kArray2); - - EXPECT_NE(span1, span2); - - static constexpr int kArray3[] = {2, 3, 5, 7, 11}; - constexpr span span3(kArray3); - - EXPECT_FALSE(span1 != span3); - - static double kArray4[] = {1.0, 4.0, 6.0, 8.0, 9.0}; - span span4(kArray4); - - EXPECT_NE(span3, span4); -} - -TEST(SpanTest, LessThan) { - static constexpr int kArray1[] = {2, 3, 5, 7, 11}; - static constexpr int kArray2[] = {2, 3, 5, 7, 11, 13}; - constexpr span span1(kArray1); - constexpr span span2(kArray2); - - EXPECT_LT(span1, span2); - - static constexpr int kArray3[] = {2, 3, 5, 7, 11}; - constexpr span span3(kArray3); - - EXPECT_FALSE(span1 < span3); - - static double kArray4[] = {2.0, 3.0, 5.0, 7.0, 11.0, 13.0}; - span span4(kArray4); - - EXPECT_LT(span3, span4); -} - -TEST(SpanTest, LessEqual) { - static constexpr int kArray1[] = {2, 3, 5, 7, 11}; - static constexpr int kArray2[] = {2, 3, 5, 7, 11, 13}; - constexpr span span1(kArray1); - constexpr span span2(kArray2); - - EXPECT_LE(span1, span1); - EXPECT_LE(span1, span2); - - static constexpr int kArray3[] = {2, 3, 5, 7, 10}; - constexpr span span3(kArray3); - - EXPECT_FALSE(span1 <= span3); - - static double kArray4[] = {2.0, 3.0, 5.0, 7.0, 11.0, 13.0}; - span span4(kArray4); - - EXPECT_LE(span3, span4); -} - -TEST(SpanTest, GreaterThan) { - static constexpr int kArray1[] = {2, 3, 5, 7, 11, 13}; - static constexpr int kArray2[] = {2, 3, 5, 7, 11}; - constexpr span span1(kArray1); - constexpr span span2(kArray2); - - EXPECT_GT(span1, span2); - - static constexpr int kArray3[] = {2, 3, 5, 7, 11, 13}; - constexpr span span3(kArray3); - - EXPECT_FALSE(span1 > span3); - - static double kArray4[] = {2.0, 3.0, 5.0, 7.0, 11.0}; - span span4(kArray4); - - EXPECT_GT(span3, span4); -} - -TEST(SpanTest, GreaterEqual) { - static constexpr int kArray1[] = {2, 3, 5, 7, 11, 13}; - static constexpr int kArray2[] = {2, 3, 5, 7, 11}; - constexpr span span1(kArray1); - constexpr span span2(kArray2); - - EXPECT_GE(span1, span1); - EXPECT_GE(span1, span2); - - static constexpr int kArray3[] = {2, 3, 5, 7, 12}; - constexpr span span3(kArray3); - - EXPECT_FALSE(span1 >= span3); - - static double kArray4[] = {2.0, 3.0, 5.0, 7.0, 11.0}; - span span4(kArray4); - - EXPECT_GE(span3, span4); -} - -TEST(SpanTest, AsBytes) { - { - constexpr int kArray[] = {2, 3, 5, 7, 11, 13}; - span bytes_span = - as_bytes(make_span(kArray)); - EXPECT_EQ(reinterpret_cast(kArray), bytes_span.data()); - EXPECT_EQ(sizeof(kArray), bytes_span.size()); - EXPECT_EQ(bytes_span.size(), bytes_span.size_bytes()); - } - - { - std::vector vec = {1, 1, 2, 3, 5, 8}; - span mutable_span(vec); - span bytes_span = as_bytes(mutable_span); - EXPECT_EQ(reinterpret_cast(vec.data()), bytes_span.data()); - EXPECT_EQ(sizeof(int) * vec.size(), bytes_span.size()); - EXPECT_EQ(bytes_span.size(), bytes_span.size_bytes()); - } -} - -TEST(SpanTest, AsWritableBytes) { - std::vector vec = {1, 1, 2, 3, 5, 8}; - span mutable_span(vec); - span writable_bytes_span = as_writable_bytes(mutable_span); - EXPECT_EQ(reinterpret_cast(vec.data()), writable_bytes_span.data()); - EXPECT_EQ(sizeof(int) * vec.size(), writable_bytes_span.size()); - EXPECT_EQ(writable_bytes_span.size(), writable_bytes_span.size_bytes()); - - // Set the first entry of vec to zero while writing through the span. - std::fill(writable_bytes_span.data(), - writable_bytes_span.data() + sizeof(int), 0); - EXPECT_EQ(0, vec[0]); -} - -TEST(SpanTest, MakeSpanFromDataAndSize) { - int* nullint = nullptr; - auto empty_span = make_span(nullint, 0); - EXPECT_TRUE(empty_span.empty()); - EXPECT_EQ(nullptr, empty_span.data()); - - std::vector vector = {1, 1, 2, 3, 5, 8}; - span span(vector.data(), vector.size()); - auto made_span = make_span(vector.data(), vector.size()); - EXPECT_EQ(span, made_span); - static_assert(decltype(made_span)::extent == dynamic_extent, ""); -} - -TEST(SpanTest, MakeSpanFromPointerPair) { - int* nullint = nullptr; - auto empty_span = make_span(nullint, nullint); - EXPECT_TRUE(empty_span.empty()); - EXPECT_EQ(nullptr, empty_span.data()); - - std::vector vector = {1, 1, 2, 3, 5, 8}; - span span(vector.data(), vector.size()); - auto made_span = make_span(vector.data(), vector.data() + vector.size()); - EXPECT_EQ(span, made_span); - static_assert(decltype(made_span)::extent == dynamic_extent, ""); -} - -TEST(SpanTest, MakeSpanFromConstexprArray) { - static constexpr int kArray[] = {1, 2, 3, 4, 5}; - constexpr span span(kArray); - EXPECT_EQ(span, make_span(kArray)); - static_assert(decltype(make_span(kArray))::extent == 5, ""); -} - -TEST(SpanTest, MakeSpanFromStdArray) { - const std::array kArray = {{1, 2, 3, 4, 5}}; - span span(kArray); - EXPECT_EQ(span, make_span(kArray)); - static_assert(decltype(make_span(kArray))::extent == 5, ""); -} - -TEST(SpanTest, MakeSpanFromConstContainer) { - const std::vector vector = {-1, -2, -3, -4, -5}; - span span(vector); - EXPECT_EQ(span, make_span(vector)); - static_assert(decltype(make_span(vector))::extent == dynamic_extent, ""); -} - -TEST(SpanTest, MakeSpanFromContainer) { - std::vector vector = {-1, -2, -3, -4, -5}; - span span(vector); - EXPECT_EQ(span, make_span(vector)); - static_assert(decltype(make_span(vector))::extent == dynamic_extent, ""); -} - -TEST(SpanTest, MakeSpanFromDynamicSpan) { - static constexpr int kArray[] = {1, 2, 3, 4, 5}; - constexpr span span(kArray); - static_assert(std::is_same::value, - "make_span(span) should have the same element_type as span"); - - static_assert(span.data() == make_span(span).data(), - "make_span(span) should have the same data() as span"); - - static_assert(span.size() == make_span(span).size(), - "make_span(span) should have the same size() as span"); - - static_assert(decltype(make_span(span))::extent == decltype(span)::extent, - "make_span(span) should have the same extent as span"); -} - -TEST(SpanTest, MakeSpanFromStaticSpan) { - static constexpr int kArray[] = {1, 2, 3, 4, 5}; - constexpr span span(kArray); - static_assert(std::is_same::value, - "make_span(span) should have the same element_type as span"); - - static_assert(span.data() == make_span(span).data(), - "make_span(span) should have the same data() as span"); - - static_assert(span.size() == make_span(span).size(), - "make_span(span) should have the same size() as span"); - - static_assert(decltype(make_span(span))::extent == decltype(span)::extent, - "make_span(span) should have the same extent as span"); -} - -TEST(SpanTest, EnsureConstexprGoodness) { - static constexpr int kArray[] = {5, 4, 3, 2, 1}; - constexpr span constexpr_span(kArray); - const size_t size = 2; - - const size_t start = 1; - constexpr span subspan = - constexpr_span.subspan(start, start + size); - for (size_t i = 0; i < subspan.size(); ++i) - EXPECT_EQ(kArray[start + i], subspan[i]); - - constexpr span firsts = constexpr_span.first(size); - for (size_t i = 0; i < firsts.size(); ++i) - EXPECT_EQ(kArray[i], firsts[i]); - - constexpr span lasts = constexpr_span.last(size); - for (size_t i = 0; i < lasts.size(); ++i) { - const size_t j = (arraysize(kArray) - size) + i; - EXPECT_EQ(kArray[j], lasts[i]); - } - - constexpr int item = constexpr_span[size]; - EXPECT_EQ(kArray[size], item); -} - -} // namespace base diff --git a/containers/span_unittest.nc b/containers/span_unittest.nc deleted file mode 100644 index 0d2af89ae..000000000 --- a/containers/span_unittest.nc +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a "No Compile Test" suite. -// http://dev.chromium.org/developers/testing/no-compile-tests - -#include "base/containers/span.h" - -#include -#include -#include - -namespace base { - -class Base { -}; - -class Derived : Base { -}; - -#if defined(NCTEST_DEFAULT_SPAN_WITH_NON_ZERO_STATIC_EXTENT_DISALLOWED) // [r"fatal error: static_assert failed \"Invalid Extent\""] - -// A default constructed span must have an extent of 0 or dynamic_extent. -void WontCompile() { - span span; -} - -#elif defined(NCTEST_SPAN_FROM_ARRAY_WITH_NON_MATCHING_STATIC_EXTENT_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span'"] - -// A span with static extent constructed from an array must match the size of -// the array. -void WontCompile() { - int array[] = {1, 2, 3}; - span span(array); -} - -#elif defined(NCTEST_SPAN_FROM_STD_ARRAY_WITH_NON_MATCHING_STATIC_EXTENT_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span'"] - -// A span with static extent constructed from std::array must match the size of -// the array. -void WontCompile() { - std::array array = {1, 2, 3}; - span span(array); -} - -#elif defined(NCTEST_SPAN_FROM_CONST_STD_ARRAY_WITH_NON_MATCHING_STATIC_EXTENT_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span'"] - -// A span with static extent constructed from std::array must match the size of -// the array. -void WontCompile() { - const std::array array = {1, 2, 3}; - span span(array); -} - -#elif defined(NCTEST_SPAN_FROM_OTHER_SPAN_WITH_MISMATCHING_EXTENT_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span'"] - -// A span with static extent constructed from another span must match the -// extent. -void WontCompile() { - std::array array = {1, 2, 3}; - span span3(array); - span span4(span3); -} - -#elif defined(NCTEST_DYNAMIC_SPAN_TO_STATIC_SPAN_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span'"] - -// Converting a dynamic span to a static span should not be allowed. -void WontCompile() { - span dynamic_span; - span static_span(dynamic_span); -} - -#elif defined(NCTEST_DERIVED_TO_BASE_CONVERSION_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span'"] - -// Internally, this is represented as a pointer to pointers to Derived. An -// implicit conversion to a pointer to pointers to Base must not be allowed. -// If it were allowed, then something like this would be possible. -// Cat** cats = GetCats(); -// Animals** animals = cats; -// animals[0] = new Dog(); // Uhoh! -void WontCompile() { - span derived_span; - span base_span(derived_span); -} - -#elif defined(NCTEST_PTR_TO_CONSTPTR_CONVERSION_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span'"] - -// Similarly, converting a span to span requires internally -// converting T** to const T**. This is also disallowed, as it would allow code -// to violate the contract of const. -void WontCompile() { - span non_const_span; - span const_span(non_const_span); -} - -#elif defined(NCTEST_CONST_CONTAINER_TO_MUTABLE_CONVERSION_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span'"] - -// A const container should not be convertible to a mutable span. -void WontCompile() { - const std::vector v = {1, 2, 3}; - span span(v); -} - -#elif defined(NCTEST_STD_SET_CONVERSION_DISALLOWED) // [r"fatal error: no matching constructor for initialization of 'span'"] - -// A std::set() should not satisfy the requirements for conversion to a span. -void WontCompile() { - std::set set; - span span(set); -} - -#elif defined(NCTEST_STATIC_FRONT_WITH_EXCEEDING_COUNT_DISALLOWED) // [r"fatal error: static_assert failed \"Count must not exceed Extent\""] - -// Static first called on a span with static extent must not exceed the size. -void WontCompile() { - std::array array = {1, 2, 3}; - span span(array); - auto first = span.first<4>(); -} - -#elif defined(NCTEST_STATIC_LAST_WITH_EXCEEDING_COUNT_DISALLOWED) // [r"fatal error: static_assert failed \"Count must not exceed Extent\""] - -// Static last called on a span with static extent must not exceed the size. -void WontCompile() { - std::array array = {1, 2, 3}; - span span(array); - auto last = span.last<4>(); -} - -#elif defined(NCTEST_STATIC_SUBSPAN_WITH_EXCEEDING_OFFSET_DISALLOWED) // [r"fatal error: static_assert failed \"Offset must not exceed Extent\""] - -// Static subspan called on a span with static extent must not exceed the size. -void WontCompile() { - std::array array = {1, 2, 3}; - span span(array); - auto subspan = span.subspan<4>(); -} - -#elif defined(NCTEST_STATIC_SUBSPAN_WITH_EXCEEDING_COUNT_DISALLOWED) // [r"fatal error: static_assert failed \"Count must not exceed Extent - Offset\""] - -// Static subspan called on a span with static extent must not exceed the size. -void WontCompile() { - std::array array = {1, 2, 3}; - span span(array); - auto subspan = span.subspan<0, 4>(); -} - -#elif defined(NCTEST_AS_WRITABLE_BYTES_WITH_CONST_CONTAINER_DISALLOWED) // [r"fatal error: no matching function for call to 'as_writable_bytes'"] - -// as_writable_bytes should not be possible for a const container. -void WontCompile() { - const std::vector v = {1, 2, 3}; - span bytes = as_writable_bytes(make_span(v)); -} - -#elif defined(NCTEST_MAKE_SPAN_FROM_SET_CONVERSION_DISALLOWED) // [r"fatal error: no matching function for call to 'make_span'"] - -// A std::set() should not satisfy the requirements for conversion to a span. -void WontCompile() { - std::set set; - auto span = make_span(set); -} - -#endif - -} // namespace base diff --git a/containers/stack.h b/containers/stack.h deleted file mode 100644 index 5cf06f825..000000000 --- a/containers/stack.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_STACK_H_ -#define BASE_CONTAINERS_STACK_H_ - -#include - -#include "base/containers/circular_deque.h" - -namespace base { - -// Provides a definition of base::stack that's like std::stack but uses a -// base::circular_deque instead of std::deque. Since std::stack is just a -// wrapper for an underlying type, we can just provide a typedef for it that -// defaults to the base circular_deque. -template > -using stack = std::stack; - -} // namespace base - -#endif // BASE_CONTAINERS_STACK_H_ diff --git a/containers/stack_container.h b/containers/stack_container.h deleted file mode 100644 index c77574430..000000000 --- a/containers/stack_container.h +++ /dev/null @@ -1,229 +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_CONTAINERS_STACK_CONTAINER_H_ -#define BASE_CONTAINERS_STACK_CONTAINER_H_ - -#include - -#include - -#include "base/macros.h" -#include "build/build_config.h" - -namespace base { - -// This allocator can be used with STL containers to provide a stack buffer -// from which to allocate memory and overflows onto the heap. This stack buffer -// would be allocated on the stack and allows us to avoid heap operations in -// some situations. -// -// STL likes to make copies of allocators, so the allocator itself can't hold -// the data. Instead, we make the creator responsible for creating a -// StackAllocator::Source which contains the data. Copying the allocator -// merely copies the pointer to this shared source, so all allocators created -// based on our allocator will share the same stack buffer. -// -// This stack buffer implementation is very simple. The first allocation that -// fits in the stack buffer will use the stack buffer. Any subsequent -// allocations will not use the stack buffer, even if there is unused room. -// This makes it appropriate for array-like containers, but the caller should -// be sure to reserve() in the container up to the stack buffer size. Otherwise -// the container will allocate a small array which will "use up" the stack -// buffer. -template -class StackAllocator : public std::allocator { - public: - typedef typename std::allocator::pointer pointer; - typedef typename std::allocator::size_type size_type; - - // Backing store for the allocator. The container owner is responsible for - // maintaining this for as long as any containers using this allocator are - // live. - struct Source { - Source() : used_stack_buffer_(false) { - } - - // Casts the buffer in its right type. - T* stack_buffer() { return reinterpret_cast(stack_buffer_); } - const T* stack_buffer() const { - return reinterpret_cast(&stack_buffer_); - } - - // The buffer itself. It is not of type T because we don't want the - // constructors and destructors to be automatically called. Define a POD - // buffer of the right size instead. - alignas(T) char stack_buffer_[sizeof(T[stack_capacity])]; -#if defined(__GNUC__) && !defined(ARCH_CPU_X86_FAMILY) - static_assert(alignof(T) <= 16, "http://crbug.com/115612"); -#endif - - // Set when the stack buffer is used for an allocation. We do not track - // how much of the buffer is used, only that somebody is using it. - bool used_stack_buffer_; - }; - - // Used by containers when they want to refer to an allocator of type U. - template - struct rebind { - typedef StackAllocator other; - }; - - // For the straight up copy c-tor, we can share storage. - StackAllocator(const StackAllocator& rhs) - : std::allocator(), source_(rhs.source_) { - } - - // ISO C++ requires the following constructor to be defined, - // and std::vector in VC++2008SP1 Release fails with an error - // in the class _Container_base_aux_alloc_real (from ) - // if the constructor does not exist. - // For this constructor, we cannot share storage; there's - // no guarantee that the Source buffer of Ts is large enough - // for Us. - // TODO: If we were fancy pants, perhaps we could share storage - // iff sizeof(T) == sizeof(U). - template - StackAllocator(const StackAllocator& other) - : 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) { - } - - // Actually do the allocation. Use the stack buffer if nobody has used it yet - // and the size requested fits. Otherwise, fall through to the standard - // allocator. - pointer allocate(size_type n, void* hint = 0) { - if (source_ != NULL && !source_->used_stack_buffer_ - && n <= stack_capacity) { - source_->used_stack_buffer_ = true; - return source_->stack_buffer(); - } else { - return std::allocator::allocate(n, hint); - } - } - - // Free: when trying to free the stack buffer, just mark it as free. For - // non-stack-buffer pointers, just fall though to the standard allocator. - void deallocate(pointer p, size_type n) { - if (source_ != NULL && p == source_->stack_buffer()) - source_->used_stack_buffer_ = false; - else - std::allocator::deallocate(p, n); - } - - private: - Source* source_; -}; - -// A wrapper around STL containers that maintains a stack-sized buffer that the -// initial capacity of the vector is based on. Growing the container beyond the -// stack capacity will transparently overflow onto the heap. The container must -// support reserve(). -// -// This will not work with std::string since some implementations allocate -// more bytes than requested in calls to reserve(), forcing the allocation onto -// the heap. http://crbug.com/709273 -// -// WATCH OUT: the ContainerType MUST use the proper StackAllocator for this -// type. This object is really intended to be used only internally. You'll want -// to use the wrappers below for different types. -template -class StackContainer { - public: - typedef TContainerType ContainerType; - typedef typename ContainerType::value_type ContainedType; - typedef StackAllocator Allocator; - - // Allocator must be constructed before the container! - StackContainer() : allocator_(&stack_data_), container_(allocator_) { - // Make the container use the stack allocation by reserving our buffer size - // before doing anything else. - container_.reserve(stack_capacity); - } - - // Getters for the actual container. - // - // Danger: any copies of this made using the copy constructor must have - // shorter lifetimes than the source. The copy will share the same allocator - // and therefore the same stack buffer as the original. Use std::copy to - // copy into a "real" container for longer-lived objects. - ContainerType& container() { return container_; } - const ContainerType& container() const { return container_; } - - // Support operator-> to get to the container. This allows nicer syntax like: - // StackContainer<...> foo; - // std::sort(foo->begin(), foo->end()); - ContainerType* operator->() { return &container_; } - const ContainerType* operator->() const { return &container_; } - -#ifdef UNIT_TEST - // Retrieves the stack source so that that unit tests can verify that the - // buffer is being used properly. - const typename Allocator::Source& stack_data() const { - return stack_data_; - } -#endif - - protected: - typename Allocator::Source stack_data_; - Allocator allocator_; - ContainerType container_; - - private: - DISALLOW_COPY_AND_ASSIGN(StackContainer); -}; - -// StackVector ----------------------------------------------------------------- - -// Example: -// StackVector foo; -// foo->push_back(22); // we have overloaded operator-> -// foo[0] = 10; // as well as operator[] -template -class StackVector : public StackContainer< - std::vector >, - stack_capacity> { - public: - StackVector() : StackContainer< - std::vector >, - stack_capacity>() { - } - - // We need to put this in STL containers sometimes, which requires a copy - // constructor. We can't call the regular copy constructor because that will - // take the stack buffer from the original. Here, we create an empty object - // and make a stack buffer of its own. - StackVector(const StackVector& other) - : StackContainer< - std::vector >, - stack_capacity>() { - this->container().assign(other->begin(), other->end()); - } - - StackVector& operator=( - const StackVector& other) { - this->container().assign(other->begin(), other->end()); - return *this; - } - - // Vectors are commonly indexed, which isn't very convenient even with - // operator-> (using "->at()" does exception stuff we don't want). - T& operator[](size_t i) { return this->container().operator[](i); } - const T& operator[](size_t i) const { - return this->container().operator[](i); - } -}; - -} // namespace base - -#endif // BASE_CONTAINERS_STACK_CONTAINER_H_ diff --git a/containers/stack_container_unittest.cc b/containers/stack_container_unittest.cc deleted file mode 100644 index b6bb9b635..000000000 --- a/containers/stack_container_unittest.cc +++ /dev/null @@ -1,145 +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/containers/stack_container.h" - -#include - -#include - -#include "base/memory/ref_counted.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -class Dummy : public base::RefCounted { - public: - explicit Dummy(int* alive) : alive_(alive) { - ++*alive_; - } - - private: - friend class base::RefCounted; - - ~Dummy() { - --*alive_; - } - - int* const alive_; -}; - -} // namespace - -TEST(StackContainer, Vector) { - const int stack_size = 3; - StackVector vect; - const int* stack_buffer = &vect.stack_data().stack_buffer()[0]; - - // The initial |stack_size| elements should appear in the stack buffer. - EXPECT_EQ(static_cast(stack_size), vect.container().capacity()); - for (int i = 0; i < stack_size; i++) { - vect.container().push_back(i); - EXPECT_EQ(stack_buffer, &vect.container()[0]); - EXPECT_TRUE(vect.stack_data().used_stack_buffer_); - } - - // Adding more elements should push the array onto the heap. - for (int i = 0; i < stack_size; i++) { - vect.container().push_back(i + stack_size); - EXPECT_NE(stack_buffer, &vect.container()[0]); - EXPECT_FALSE(vect.stack_data().used_stack_buffer_); - } - - // The array should still be in order. - for (int i = 0; i < stack_size * 2; i++) - EXPECT_EQ(i, vect.container()[i]); - - // Resize to smaller. Our STL implementation won't reallocate in this case, - // otherwise it might use our stack buffer. We reserve right after the resize - // to guarantee it isn't using the stack buffer, even though it doesn't have - // much data. - vect.container().resize(stack_size); - vect.container().reserve(stack_size * 2); - EXPECT_FALSE(vect.stack_data().used_stack_buffer_); - - // Copying the small vector to another should use the same allocator and use - // the now-unused stack buffer. GENERALLY CALLERS SHOULD NOT DO THIS since - // they have to get the template types just right and it can cause errors. - std::vector > other(vect.container()); - EXPECT_EQ(stack_buffer, &other.front()); - EXPECT_TRUE(vect.stack_data().used_stack_buffer_); - for (int i = 0; i < stack_size; i++) - EXPECT_EQ(i, other[i]); -} - -TEST(StackContainer, VectorDoubleDelete) { - // Regression testing for double-delete. - typedef StackVector, 2> Vector; - typedef Vector::ContainerType Container; - Vector vect; - - int alive = 0; - scoped_refptr dummy(new Dummy(&alive)); - EXPECT_EQ(alive, 1); - - vect->push_back(dummy); - EXPECT_EQ(alive, 1); - - Dummy* dummy_unref = dummy.get(); - dummy = nullptr; - EXPECT_EQ(alive, 1); - - Container::iterator itr = std::find(vect->begin(), vect->end(), dummy_unref); - EXPECT_EQ(itr->get(), dummy_unref); - vect->erase(itr); - EXPECT_EQ(alive, 0); - - // Shouldn't crash at exit. -} - -namespace { - -template -class AlignedData { - public: - AlignedData() { memset(data_, 0, alignment); } - ~AlignedData() = default; - alignas(alignment) char data_[alignment]; -}; - -} // anonymous namespace - -#define EXPECT_ALIGNED(ptr, align) \ - EXPECT_EQ(0u, reinterpret_cast(ptr) & (align - 1)) - -TEST(StackContainer, BufferAlignment) { - StackVector text; - text->push_back(L'A'); - EXPECT_ALIGNED(&text[0], alignof(wchar_t)); - - StackVector doubles; - doubles->push_back(0.0); - EXPECT_ALIGNED(&doubles[0], alignof(double)); - - StackVector, 1> aligned16; - aligned16->push_back(AlignedData<16>()); - EXPECT_ALIGNED(&aligned16[0], 16); - -#if !defined(__GNUC__) || defined(ARCH_CPU_X86_FAMILY) - // It seems that non-X86 gcc doesn't respect greater than 16 byte alignment. - // See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33721 for details. - // TODO(sbc):re-enable this if GCC starts respecting higher alignments. - StackVector, 1> aligned256; - aligned256->push_back(AlignedData<256>()); - EXPECT_ALIGNED(&aligned256[0], 256); -#endif -} - -template class StackVector; -template class StackVector, 2>; - -} // namespace base diff --git a/containers/unique_ptr_adapters.h b/containers/unique_ptr_adapters.h deleted file mode 100644 index 42fab190a..000000000 --- a/containers/unique_ptr_adapters.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_UNIQUE_PTR_ADAPTERS_H_ -#define BASE_CONTAINERS_UNIQUE_PTR_ADAPTERS_H_ - -#include - -namespace base { - -// This transparent comparator allows to lookup by raw pointer in -// a container of unique pointers. This functionality is based on C++14 -// extensions to std::set/std::map interface, and can also be used -// with base::flat_set/base::flat_map. -// -// Example usage: -// Foo* foo = ... -// std::set, base::UniquePtrComparator> set; -// set.insert(std::unique_ptr(foo)); -// ... -// auto it = set.find(foo); -// EXPECT_EQ(foo, it->get()); -// -// You can find more information about transparent comparisons here: -// http://en.cppreference.com/w/cpp/utility/functional/less_void -struct UniquePtrComparator { - using is_transparent = int; - - template - bool operator()(const std::unique_ptr& lhs, - const std::unique_ptr& rhs) const { - return lhs < rhs; - } - - template - bool operator()(const T* lhs, const std::unique_ptr& rhs) const { - return lhs < rhs.get(); - } - - template - bool operator()(const std::unique_ptr& lhs, const T* rhs) const { - return lhs.get() < rhs; - } -}; - -// UniquePtrMatcher is useful for finding an element in a container of -// unique_ptrs when you have the raw pointer. -// -// Example usage: -// std::vector> vector; -// Foo* element = ... -// auto iter = std::find_if(vector.begin(), vector.end(), -// MatchesUniquePtr(element)); -// -// Example of erasing from container: -// EraseIf(v, MatchesUniquePtr(element)); -// -template > -struct UniquePtrMatcher { - explicit UniquePtrMatcher(T* t) : t_(t) {} - - bool operator()(const std::unique_ptr& o) { - return o.get() == t_; - } - - private: - T* const t_; -}; - -template > -UniquePtrMatcher MatchesUniquePtr(T* t) { - return UniquePtrMatcher(t); -} - -} // namespace base - -#endif // BASE_CONTAINERS_UNIQUE_PTR_ADAPTERS_H_ diff --git a/containers/unique_ptr_adapters_unittest.cc b/containers/unique_ptr_adapters_unittest.cc deleted file mode 100644 index 5b8f1fc02..000000000 --- a/containers/unique_ptr_adapters_unittest.cc +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/containers/unique_ptr_adapters.h" - -#include -#include - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -class Foo { - public: - Foo() { instance_count++; } - ~Foo() { instance_count--; } - static int instance_count; -}; - -int Foo::instance_count = 0; - -TEST(UniquePtrComparatorTest, Basic) { - std::set, UniquePtrComparator> set; - Foo* foo1 = new Foo(); - Foo* foo2 = new Foo(); - Foo* foo3 = new Foo(); - EXPECT_EQ(3, Foo::instance_count); - - set.emplace(foo1); - set.emplace(foo2); - - auto it1 = set.find(foo1); - EXPECT_TRUE(it1 != set.end()); - EXPECT_EQ(foo1, it1->get()); - - { - auto it2 = set.find(foo2); - EXPECT_TRUE(it2 != set.end()); - EXPECT_EQ(foo2, it2->get()); - } - - EXPECT_TRUE(set.find(foo3) == set.end()); - - set.erase(it1); - EXPECT_EQ(2, Foo::instance_count); - - EXPECT_TRUE(set.find(foo1) == set.end()); - - { - auto it2 = set.find(foo2); - EXPECT_TRUE(it2 != set.end()); - EXPECT_EQ(foo2, it2->get()); - } - - set.clear(); - EXPECT_EQ(1, Foo::instance_count); - - EXPECT_TRUE(set.find(foo1) == set.end()); - EXPECT_TRUE(set.find(foo2) == set.end()); - EXPECT_TRUE(set.find(foo3) == set.end()); - - delete foo3; - EXPECT_EQ(0, Foo::instance_count); -} - -TEST(UniquePtrMatcherTest, Basic) { - std::vector> v; - auto foo_ptr1 = std::make_unique(); - Foo* foo1 = foo_ptr1.get(); - v.push_back(std::move(foo_ptr1)); - auto foo_ptr2 = std::make_unique(); - Foo* foo2 = foo_ptr2.get(); - v.push_back(std::move(foo_ptr2)); - - { - auto iter = std::find_if(v.begin(), v.end(), UniquePtrMatcher(foo1)); - ASSERT_TRUE(iter != v.end()); - EXPECT_EQ(foo1, iter->get()); - } - - { - auto iter = std::find_if(v.begin(), v.end(), UniquePtrMatcher(foo2)); - ASSERT_TRUE(iter != v.end()); - EXPECT_EQ(foo2, iter->get()); - } - - { - auto iter = std::find_if(v.begin(), v.end(), MatchesUniquePtr(foo2)); - ASSERT_TRUE(iter != v.end()); - EXPECT_EQ(foo2, iter->get()); - } -} - -class TestDeleter { - public: - void operator()(Foo* foo) { delete foo; } -}; - -TEST(UniquePtrMatcherTest, Deleter) { - using UniqueFoo = std::unique_ptr; - std::vector v; - UniqueFoo foo_ptr1(new Foo); - Foo* foo1 = foo_ptr1.get(); - v.push_back(std::move(foo_ptr1)); - UniqueFoo foo_ptr2(new Foo); - Foo* foo2 = foo_ptr2.get(); - v.push_back(std::move(foo_ptr2)); - - { - auto iter = std::find_if(v.begin(), v.end(), - UniquePtrMatcher(foo1)); - ASSERT_TRUE(iter != v.end()); - EXPECT_EQ(foo1, iter->get()); - } - - { - auto iter = std::find_if(v.begin(), v.end(), - UniquePtrMatcher(foo2)); - ASSERT_TRUE(iter != v.end()); - EXPECT_EQ(foo2, iter->get()); - } - - { - auto iter = std::find_if(v.begin(), v.end(), - MatchesUniquePtr(foo2)); - ASSERT_TRUE(iter != v.end()); - EXPECT_EQ(foo2, iter->get()); - } -} - -} // namespace -} // namespace base diff --git a/containers/vector_buffer.h b/containers/vector_buffer.h deleted file mode 100644 index a72c1ed95..000000000 --- a/containers/vector_buffer.h +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CONTAINERS_VECTOR_BUFFERS_H_ -#define BASE_CONTAINERS_VECTOR_BUFFERS_H_ - -#include -#include - -#include -#include - -#include "base/logging.h" -#include "base/macros.h" - -namespace base { -namespace internal { - -// Internal implementation detail of base/containers. -// -// Implements a vector-like buffer that holds a certain capacity of T. Unlike -// std::vector, VectorBuffer never constructs or destructs its arguments, and -// can't change sizes. But it does implement templates to assist in efficient -// moving and destruction of those items manually. -// -// In particular, the destructor function does not iterate over the items if -// there is no destructor. Moves should be implemented as a memcpy/memmove for -// trivially copyable objects (POD) otherwise, it should be a std::move if -// possible, and as a last resort it falls back to a copy. This behavior is -// similar to std::vector. -// -// No special consideration is done for noexcept move constructors since -// we compile without exceptions. -// -// The current API does not support moving overlapping ranges. -template -class VectorBuffer { - public: - constexpr VectorBuffer() = default; - -#if defined(__clang__) && !defined(__native_client__) - // This constructor converts an uninitialized void* to a T* which triggers - // clang Control Flow Integrity. Since this is as-designed, disable. - __attribute__((no_sanitize("cfi-unrelated-cast", "vptr"))) -#endif - VectorBuffer(size_t count) - : buffer_(reinterpret_cast(malloc(sizeof(T) * count))), - capacity_(count) { - } - VectorBuffer(VectorBuffer&& other) noexcept - : buffer_(other.buffer_), capacity_(other.capacity_) { - other.buffer_ = nullptr; - other.capacity_ = 0; - } - - ~VectorBuffer() { free(buffer_); } - - VectorBuffer& operator=(VectorBuffer&& other) { - free(buffer_); - buffer_ = other.buffer_; - capacity_ = other.capacity_; - - other.buffer_ = nullptr; - other.capacity_ = 0; - return *this; - } - - size_t capacity() const { return capacity_; } - - T& operator[](size_t i) { return buffer_[i]; } - const T& operator[](size_t i) const { return buffer_[i]; } - T* begin() { return buffer_; } - T* end() { return &buffer_[capacity_]; } - - // DestructRange ------------------------------------------------------------ - - // Trivially destructible objects need not have their destructors called. - template ::value, - int>::type = 0> - void DestructRange(T* begin, T* end) {} - - // Non-trivially destructible objects must have their destructors called - // individually. - template ::value, - int>::type = 0> - void DestructRange(T* begin, T* end) { - while (begin != end) { - begin->~T(); - begin++; - } - } - - // MoveRange ---------------------------------------------------------------- - // - // The destructor will be called (as necessary) for all moved types. The - // ranges must not overlap. - // - // The parameters and begin and end (one past the last) of the input buffer, - // and the address of the first element to copy to. There must be sufficient - // room in the destination for all items in the range [begin, end). - - // Trivially copyable types can use memcpy. trivially copyable implies - // that there is a trivial destructor as we don't have to call it. - template ::value, - int>::type = 0> - static void MoveRange(T* from_begin, T* from_end, T* to) { - DCHECK(!RangesOverlap(from_begin, from_end, to)); - memcpy(to, from_begin, (from_end - from_begin) * sizeof(T)); - } - - // Not trivially copyable, but movable: call the move constructor and - // destruct the original. - template ::value && - !base::is_trivially_copyable::value, - int>::type = 0> - static void MoveRange(T* from_begin, T* from_end, T* to) { - DCHECK(!RangesOverlap(from_begin, from_end, to)); - while (from_begin != from_end) { - new (to) T(std::move(*from_begin)); - from_begin->~T(); - from_begin++; - to++; - } - } - - // Not movable, not trivially copyable: call the copy constructor and - // destruct the original. - template ::value && - !base::is_trivially_copyable::value, - int>::type = 0> - static void MoveRange(T* from_begin, T* from_end, T* to) { - DCHECK(!RangesOverlap(from_begin, from_end, to)); - while (from_begin != from_end) { - new (to) T(*from_begin); - from_begin->~T(); - from_begin++; - to++; - } - } - - private: - static bool RangesOverlap(const T* from_begin, - const T* from_end, - const T* to) { - return !(to >= from_end || to + (from_end - from_begin) <= from_begin); - } - - T* buffer_ = nullptr; - size_t capacity_ = 0; - - DISALLOW_COPY_AND_ASSIGN(VectorBuffer); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_CONTAINERS_VECTOR_BUFFERS_H_ diff --git a/containers/vector_buffer_unittest.cc b/containers/vector_buffer_unittest.cc deleted file mode 100644 index 6d49505d4..000000000 --- a/containers/vector_buffer_unittest.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/containers/vector_buffer.h" - -#include "base/test/copy_only_int.h" -#include "base/test/move_only_int.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace internal { - -TEST(VectorBuffer, DeletePOD) { - constexpr int size = 10; - VectorBuffer buffer(size); - for (int i = 0; i < size; i++) - buffer.begin()[i] = i + 1; - - buffer.DestructRange(buffer.begin(), buffer.end()); - - // Delete should do nothing. - for (int i = 0; i < size; i++) - EXPECT_EQ(i + 1, buffer.begin()[i]); -} - -TEST(VectorBuffer, DeleteMoveOnly) { - constexpr int size = 10; - VectorBuffer buffer(size); - for (int i = 0; i < size; i++) - new (buffer.begin() + i) MoveOnlyInt(i + 1); - - buffer.DestructRange(buffer.begin(), buffer.end()); - - // Delete should have reset all of the values to 0. - for (int i = 0; i < size; i++) - EXPECT_EQ(0, buffer.begin()[i].data()); -} - -TEST(VectorBuffer, PODMove) { - constexpr int size = 10; - VectorBuffer dest(size); - - VectorBuffer original(size); - for (int i = 0; i < size; i++) - original.begin()[i] = i + 1; - - original.MoveRange(original.begin(), original.end(), dest.begin()); - for (int i = 0; i < size; i++) - EXPECT_EQ(i + 1, dest.begin()[i]); -} - -TEST(VectorBuffer, MovableMove) { - constexpr int size = 10; - VectorBuffer dest(size); - - VectorBuffer original(size); - for (int i = 0; i < size; i++) - new (original.begin() + i) MoveOnlyInt(i + 1); - - original.MoveRange(original.begin(), original.end(), dest.begin()); - - // Moving from a MoveOnlyInt resets to 0. - for (int i = 0; i < size; i++) { - EXPECT_EQ(0, original.begin()[i].data()); - EXPECT_EQ(i + 1, dest.begin()[i].data()); - } -} - -TEST(VectorBuffer, CopyToMove) { - constexpr int size = 10; - VectorBuffer dest(size); - - VectorBuffer original(size); - for (int i = 0; i < size; i++) - new (original.begin() + i) CopyOnlyInt(i + 1); - - original.MoveRange(original.begin(), original.end(), dest.begin()); - - // The original should have been destructed, which should reset the value to - // 0. Technically this dereferences the destructed object. - for (int i = 0; i < size; i++) { - EXPECT_EQ(0, original.begin()[i].data()); - EXPECT_EQ(i + 1, dest.begin()[i].data()); - } -} - -} // namespace internal -} // namespace base diff --git a/cpu.cc b/cpu.cc deleted file mode 100644 index cd9066f53..000000000 --- a/cpu.cc +++ /dev/null @@ -1,242 +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/cpu.h" - -#include -#include -#include -#include - -#include -#include - -#include "base/macros.h" -#include "build/build_config.h" - -#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) -#include "base/files/file_util.h" -#endif - -#if defined(ARCH_CPU_X86_FAMILY) -#if defined(COMPILER_MSVC) -#include -#include // For _xgetbv() -#endif -#endif - -namespace base { - -CPU::CPU() - : signature_(0), - type_(0), - family_(0), - model_(0), - stepping_(0), - ext_model_(0), - ext_family_(0), - has_mmx_(false), - has_sse_(false), - has_sse2_(false), - has_sse3_(false), - has_ssse3_(false), - has_sse41_(false), - has_sse42_(false), - has_popcnt_(false), - has_avx_(false), - has_avx2_(false), - has_aesni_(false), - has_non_stop_time_stamp_counter_(false), - cpu_vendor_("unknown") { - Initialize(); -} - -namespace { - -#if defined(ARCH_CPU_X86_FAMILY) -#if !defined(COMPILER_MSVC) - -#if defined(__pic__) && defined(__i386__) - -void __cpuid(int cpu_info[4], int info_type) { - __asm__ volatile( - "mov %%ebx, %%edi\n" - "cpuid\n" - "xchg %%edi, %%ebx\n" - : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), - "=d"(cpu_info[3]) - : "a"(info_type), "c"(0)); -} - -#else - -void __cpuid(int cpu_info[4], int info_type) { - __asm__ volatile("cpuid\n" - : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), - "=d"(cpu_info[3]) - : "a"(info_type), "c"(0)); -} - -#endif - -// _xgetbv returns the value of an Intel Extended Control Register (XCR). -// Currently only XCR0 is defined by Intel so |xcr| should always be zero. -uint64_t _xgetbv(uint32_t xcr) { - uint32_t eax, edx; - - __asm__ volatile ( - "xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr)); - return (static_cast(edx) << 32) | eax; -} - -#endif // !defined(COMPILER_MSVC) -#endif // ARCH_CPU_X86_FAMILY - -#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) -std::string* CpuInfoBrand() { - static std::string* brand = []() { - // This function finds the value from /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. - const char kModelNamePrefix[] = "model name\t: "; - const char kProcessorPrefix[] = "Processor\t: "; - - std::string contents; - ReadFileToString(FilePath("/proc/cpuinfo"), &contents); - DCHECK(!contents.empty()); - - std::istringstream iss(contents); - std::string line; - while (std::getline(iss, line)) { - if (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0) - return new std::string(line.substr(strlen(kModelNamePrefix))); - if (line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0) - return new std::string(line.substr(strlen(kProcessorPrefix))); - } - - return new std::string(); - }(); - - return brand; -} -#endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || - // defined(OS_LINUX)) - -} // namespace - -void CPU::Initialize() { -#if defined(ARCH_CPU_X86_FAMILY) - int cpu_info[4] = {-1}; - // This array is used to temporarily hold the vendor name and then the brand - // name. Thus it has to be big enough for both use cases. There are - // static_asserts below for each of the use cases to make sure this array is - // big enough. - char cpu_string[sizeof(cpu_info) * 3 + 1]; - - // __cpuid with an InfoType argument of 0 returns the number of - // valid Ids in CPUInfo[0] and the CPU identification string in - // the other three array elements. The CPU identification string is - // not in linear order. The code below arranges the information - // in a human readable form. The human readable order is CPUInfo[1] | - // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped - // before using memcpy() to copy these three array elements to |cpu_string|. - __cpuid(cpu_info, 0); - int num_ids = cpu_info[0]; - std::swap(cpu_info[2], cpu_info[3]); - static constexpr size_t kVendorNameSize = 3 * sizeof(cpu_info[1]); - static_assert(kVendorNameSize < arraysize(cpu_string), - "cpu_string too small"); - memcpy(cpu_string, &cpu_info[1], kVendorNameSize); - cpu_string[kVendorNameSize] = '\0'; - cpu_vendor_ = cpu_string; - - // Interpret CPU feature information. - if (num_ids > 0) { - int cpu_info7[4] = {0}; - __cpuid(cpu_info, 1); - if (num_ids >= 7) { - __cpuid(cpu_info7, 7); - } - signature_ = cpu_info[0]; - stepping_ = cpu_info[0] & 0xf; - model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0); - family_ = (cpu_info[0] >> 8) & 0xf; - type_ = (cpu_info[0] >> 12) & 0x3; - ext_model_ = (cpu_info[0] >> 16) & 0xf; - ext_family_ = (cpu_info[0] >> 20) & 0xff; - has_mmx_ = (cpu_info[3] & 0x00800000) != 0; - has_sse_ = (cpu_info[3] & 0x02000000) != 0; - has_sse2_ = (cpu_info[3] & 0x04000000) != 0; - has_sse3_ = (cpu_info[2] & 0x00000001) != 0; - has_ssse3_ = (cpu_info[2] & 0x00000200) != 0; - has_sse41_ = (cpu_info[2] & 0x00080000) != 0; - has_sse42_ = (cpu_info[2] & 0x00100000) != 0; - has_popcnt_ = (cpu_info[2] & 0x00800000) != 0; - - // AVX instructions will generate an illegal instruction exception unless - // a) they are supported by the CPU, - // 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_ = - (cpu_info[2] & 0x10000000) != 0 && - (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; - has_avx2_ = has_avx_ && (cpu_info7[1] & 0x00000020) != 0; - } - - // Get the brand string of the cpu. - __cpuid(cpu_info, 0x80000000); - const int max_parameter = cpu_info[0]; - - static constexpr int kParameterStart = 0x80000002; - static constexpr int kParameterEnd = 0x80000004; - static constexpr int kParameterSize = kParameterEnd - kParameterStart + 1; - static_assert(kParameterSize * sizeof(cpu_info) + 1 == arraysize(cpu_string), - "cpu_string has wrong size"); - - if (max_parameter >= kParameterEnd) { - size_t i = 0; - for (int parameter = kParameterStart; parameter <= kParameterEnd; - ++parameter) { - __cpuid(cpu_info, parameter); - memcpy(&cpu_string[i], cpu_info, sizeof(cpu_info)); - i += sizeof(cpu_info); - } - cpu_string[i] = '\0'; - cpu_brand_ = cpu_string; - } - - static constexpr int kParameterContainingNonStopTimeStampCounter = 0x80000007; - if (max_parameter >= kParameterContainingNonStopTimeStampCounter) { - __cpuid(cpu_info, kParameterContainingNonStopTimeStampCounter); - has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0; - } -#elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) - cpu_brand_ = *CpuInfoBrand(); -#endif -} - -CPU::IntelMicroArchitecture CPU::GetIntelMicroArchitecture() const { - if (has_avx2()) return AVX2; - if (has_avx()) return AVX; - if (has_sse42()) return SSE42; - if (has_sse41()) return SSE41; - if (has_ssse3()) return SSSE3; - if (has_sse3()) return SSE3; - if (has_sse2()) return SSE2; - if (has_sse()) return SSE; - return PENTIUM; -} - -} // namespace base diff --git a/cpu.h b/cpu.h deleted file mode 100644 index 2c6caeafd..000000000 --- a/cpu.h +++ /dev/null @@ -1,88 +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_CPU_H_ -#define BASE_CPU_H_ - -#include - -#include "base/base_export.h" - -namespace base { - -// Query information about the processor. -class BASE_EXPORT CPU final { - public: - CPU(); - - enum IntelMicroArchitecture { - PENTIUM, - SSE, - SSE2, - SSE3, - SSSE3, - SSE41, - SSE42, - AVX, - AVX2, - MAX_INTEL_MICRO_ARCHITECTURE - }; - - // Accessors for CPU information. - const std::string& vendor_name() const { return cpu_vendor_; } - int signature() const { return signature_; } - int stepping() const { return stepping_; } - int model() const { return model_; } - int family() const { return family_; } - int type() const { return type_; } - int extended_model() const { return ext_model_; } - int extended_family() const { return ext_family_; } - bool has_mmx() const { return has_mmx_; } - bool has_sse() const { return has_sse_; } - bool has_sse2() const { return has_sse2_; } - bool has_sse3() const { return has_sse3_; } - bool has_ssse3() const { return has_ssse3_; } - bool has_sse41() const { return has_sse41_; } - bool has_sse42() const { return has_sse42_; } - bool has_popcnt() const { return has_popcnt_; } - bool has_avx() const { return has_avx_; } - bool has_avx2() const { return has_avx2_; } - bool has_aesni() const { return has_aesni_; } - bool has_non_stop_time_stamp_counter() const { - return has_non_stop_time_stamp_counter_; - } - - IntelMicroArchitecture GetIntelMicroArchitecture() const; - const std::string& cpu_brand() const { return cpu_brand_; } - - private: - // Query the processor for CPUID information. - void Initialize(); - - int signature_; // raw form of type, family, model, and stepping - int type_; // process type - int family_; // family of the processor - int model_; // model of processor - int stepping_; // processor revision number - int ext_model_; - int ext_family_; - bool has_mmx_; - bool has_sse_; - bool has_sse2_; - bool has_sse3_; - bool has_ssse3_; - bool has_sse41_; - bool has_sse42_; - bool has_popcnt_; - bool has_avx_; - bool has_avx2_; - bool has_aesni_; - bool has_non_stop_time_stamp_counter_; - std::string cpu_vendor_; - std::string cpu_brand_; -}; - -} // namespace base - -#endif // BASE_CPU_H_ diff --git a/cpu_unittest.cc b/cpu_unittest.cc deleted file mode 100644 index 8a68ea078..000000000 --- a/cpu_unittest.cc +++ /dev/null @@ -1,134 +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/cpu.h" -#include "base/stl_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if _MSC_VER >= 1700 -// C4752: found Intel(R) Advanced Vector Extensions; consider using /arch:AVX. -#pragma warning(disable: 4752) -#endif - -// Tests whether we can run extended instructions represented by the CPU -// information. This test actually executes some extended instructions (such as -// MMX, SSE, etc.) supported by the CPU and sees we can run them without -// "undefined instruction" exceptions. That is, this test succeeds when this -// test finishes without a crash. -TEST(CPU, RunExtendedInstructions) { -#if defined(ARCH_CPU_X86_FAMILY) - // Retrieve the CPU information. - base::CPU cpu; - - ASSERT_TRUE(cpu.has_mmx()); - ASSERT_TRUE(cpu.has_sse()); - ASSERT_TRUE(cpu.has_sse2()); - -// GCC and clang instruction test. -#if defined(COMPILER_GCC) - // Execute an MMX instruction. - __asm__ __volatile__("emms\n" : : : "mm0"); - - // Execute an SSE instruction. - __asm__ __volatile__("xorps %%xmm0, %%xmm0\n" : : : "xmm0"); - - // Execute an SSE 2 instruction. - __asm__ __volatile__("psrldq $0, %%xmm0\n" : : : "xmm0"); - - if (cpu.has_sse3()) { - // Execute an SSE 3 instruction. - __asm__ __volatile__("addsubpd %%xmm0, %%xmm0\n" : : : "xmm0"); - } - - if (cpu.has_ssse3()) { - // Execute a Supplimental SSE 3 instruction. - __asm__ __volatile__("psignb %%xmm0, %%xmm0\n" : : : "xmm0"); - } - - if (cpu.has_sse41()) { - // Execute an SSE 4.1 instruction. - __asm__ __volatile__("pmuldq %%xmm0, %%xmm0\n" : : : "xmm0"); - } - - if (cpu.has_sse42()) { - // Execute an SSE 4.2 instruction. - __asm__ __volatile__("crc32 %%eax, %%eax\n" : : : "eax"); - } - - if (cpu.has_popcnt()) { - // Execute a POPCNT instruction. - __asm__ __volatile__("popcnt %%eax, %%eax\n" : : : "eax"); - } - - if (cpu.has_avx()) { - // Execute an AVX instruction. - __asm__ __volatile__("vzeroupper\n" : : : "xmm0"); - } - - if (cpu.has_avx2()) { - // Execute an AVX 2 instruction. - __asm__ __volatile__("vpunpcklbw %%ymm0, %%ymm0, %%ymm0\n" : : : "xmm0"); - } - -// Visual C 32 bit and ClangCL 32/64 bit test. -#elif defined(COMPILER_MSVC) && (defined(ARCH_CPU_32_BITS) || \ - (defined(ARCH_CPU_64_BITS) && defined(__clang__))) - - // Execute an MMX instruction. - __asm emms; - - // Execute an SSE instruction. - __asm xorps xmm0, xmm0; - - // Execute an SSE 2 instruction. - __asm psrldq xmm0, 0; - - if (cpu.has_sse3()) { - // Execute an SSE 3 instruction. - __asm addsubpd xmm0, xmm0; - } - - if (cpu.has_ssse3()) { - // Execute a Supplimental SSE 3 instruction. - __asm psignb xmm0, xmm0; - } - - if (cpu.has_sse41()) { - // Execute an SSE 4.1 instruction. - __asm pmuldq xmm0, xmm0; - } - - if (cpu.has_sse42()) { - // Execute an SSE 4.2 instruction. - __asm crc32 eax, eax; - } - - if (cpu.has_popcnt()) { - // Execute a POPCNT instruction. - __asm popcnt eax, eax; - } - -// Visual C 2012 required for AVX. -#if _MSC_VER >= 1700 - if (cpu.has_avx()) { - // Execute an AVX instruction. - __asm vzeroupper; - } - - if (cpu.has_avx2()) { - // Execute an AVX 2 instruction. - __asm vpunpcklbw ymm0, ymm0, ymm0 - } -#endif // _MSC_VER >= 1700 -#endif // defined(COMPILER_GCC) -#endif // defined(ARCH_CPU_X86_FAMILY) -} - -// For https://crbug.com/249713 -TEST(CPU, BrandAndVendorContainsNoNUL) { - base::CPU cpu; - EXPECT_FALSE(base::ContainsValue(cpu.cpu_brand(), '\0')); - EXPECT_FALSE(base::ContainsValue(cpu.vendor_name(), '\0')); -} diff --git a/critical_closure.h b/critical_closure.h deleted file mode 100644 index 94c618dfb..000000000 --- a/critical_closure.h +++ /dev/null @@ -1,76 +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_CRITICAL_CLOSURE_H_ -#define BASE_CRITICAL_CLOSURE_H_ - -#include - -#include "base/callback.h" -#include "base/macros.h" -#include "build/build_config.h" - -#if defined(OS_IOS) -#include "base/bind.h" -#include "base/ios/scoped_critical_action.h" -#endif - -namespace base { - -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|. -class CriticalClosure { - public: - explicit CriticalClosure(OnceClosure closure); - ~CriticalClosure(); - void Run(); - - private: - ios::ScopedCriticalAction critical_action_; - OnceClosure closure_; - - DISALLOW_COPY_AND_ASSIGN(CriticalClosure); -}; -#endif // defined(OS_IOS) - -} // namespace internal - -// Returns a closure 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. -// -// Example: -// file_task_runner_->PostTask( -// FROM_HERE, -// MakeCriticalClosure(base::Bind(&WriteToDiskTask, path_, data))); -// -// Note new closures might be posted in this closure. If the new closures need -// background running time, |MakeCriticalClosure| should be applied on them -// before posting. -#if defined(OS_IOS) -inline OnceClosure MakeCriticalClosure(OnceClosure closure) { - DCHECK(internal::IsMultiTaskingSupported()); - return base::BindOnce( - &internal::CriticalClosure::Run, - Owned(new internal::CriticalClosure(std::move(closure)))); -} -#else // defined(OS_IOS) -inline OnceClosure MakeCriticalClosure(OnceClosure 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) - -} // namespace base - -#endif // BASE_CRITICAL_CLOSURE_H_ diff --git a/critical_closure_internal_ios.mm b/critical_closure_internal_ios.mm deleted file mode 100644 index e35eca0c7..000000000 --- a/critical_closure_internal_ios.mm +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/critical_closure.h" - -#import - -namespace base { -namespace internal { - -bool IsMultiTaskingSupported() { - return [[UIDevice currentDevice] isMultitaskingSupported]; -} - -CriticalClosure::CriticalClosure(OnceClosure closure) - : closure_(std::move(closure)) {} - -CriticalClosure::~CriticalClosure() {} - -void CriticalClosure::Run() { - std::move(closure_).Run(); -} - -} // namespace internal -} // namespace base diff --git a/debug/OWNERS b/debug/OWNERS deleted file mode 100644 index 61502576c..000000000 --- a/debug/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# For activity tracking: -per-file activity_*=bcwhite@chromium.org diff --git a/debug/activity_analyzer.cc b/debug/activity_analyzer.cc deleted file mode 100644 index d78782957..000000000 --- a/debug/activity_analyzer.cc +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 2016 The Chromium 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/activity_analyzer.h" - -#include -#include - -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/files/memory_mapped_file.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/histogram_macros.h" -#include "base/stl_util.h" -#include "base/strings/string_util.h" - -namespace base { -namespace debug { - -namespace { -// An empty snapshot that can be returned when there otherwise is none. -LazyInstance::Leaky g_empty_user_data_snapshot; - -// DO NOT CHANGE VALUES. This is logged persistently in a histogram. -enum AnalyzerCreationError { - kInvalidMemoryMappedFile, - kPmaBadFile, - kPmaUninitialized, - kPmaDeleted, - kPmaCorrupt, - kAnalyzerCreationErrorMax // Keep this last. -}; - -void LogAnalyzerCreationError(AnalyzerCreationError error) { - UMA_HISTOGRAM_ENUMERATION("ActivityTracker.Collect.AnalyzerCreationError", - error, kAnalyzerCreationErrorMax); -} - -} // namespace - -ThreadActivityAnalyzer::Snapshot::Snapshot() = default; -ThreadActivityAnalyzer::Snapshot::~Snapshot() = default; - -ThreadActivityAnalyzer::ThreadActivityAnalyzer( - const ThreadActivityTracker& tracker) - : activity_snapshot_valid_(tracker.CreateSnapshot(&activity_snapshot_)) {} - -ThreadActivityAnalyzer::ThreadActivityAnalyzer(void* base, size_t size) - : ThreadActivityAnalyzer(ThreadActivityTracker(base, size)) {} - -ThreadActivityAnalyzer::ThreadActivityAnalyzer( - PersistentMemoryAllocator* allocator, - PersistentMemoryAllocator::Reference reference) - : ThreadActivityAnalyzer(allocator->GetAsArray( - reference, - GlobalActivityTracker::kTypeIdActivityTracker, - PersistentMemoryAllocator::kSizeAny), - allocator->GetAllocSize(reference)) {} - -ThreadActivityAnalyzer::~ThreadActivityAnalyzer() = default; - -void ThreadActivityAnalyzer::AddGlobalInformation( - GlobalActivityAnalyzer* global) { - if (!IsValid()) - return; - - // User-data is held at the global scope even though it's referenced at the - // thread scope. - activity_snapshot_.user_data_stack.clear(); - for (auto& activity : activity_snapshot_.activity_stack) { - // The global GetUserDataSnapshot will return an empty snapshot if the ref - // or id is not valid. - activity_snapshot_.user_data_stack.push_back(global->GetUserDataSnapshot( - activity_snapshot_.process_id, activity.user_data_ref, - activity.user_data_id)); - } -} - -GlobalActivityAnalyzer::GlobalActivityAnalyzer( - std::unique_ptr allocator) - : allocator_(std::move(allocator)), - analysis_stamp_(0LL), - allocator_iterator_(allocator_.get()) { - DCHECK(allocator_); -} - -GlobalActivityAnalyzer::~GlobalActivityAnalyzer() = default; - -// static -std::unique_ptr -GlobalActivityAnalyzer::CreateWithAllocator( - std::unique_ptr allocator) { - if (allocator->GetMemoryState() == - PersistentMemoryAllocator::MEMORY_UNINITIALIZED) { - LogAnalyzerCreationError(kPmaUninitialized); - return nullptr; - } - if (allocator->GetMemoryState() == - PersistentMemoryAllocator::MEMORY_DELETED) { - LogAnalyzerCreationError(kPmaDeleted); - return nullptr; - } - if (allocator->IsCorrupt()) { - LogAnalyzerCreationError(kPmaCorrupt); - return nullptr; - } - - return WrapUnique(new GlobalActivityAnalyzer(std::move(allocator))); -} - -#if !defined(OS_NACL) -// static -std::unique_ptr GlobalActivityAnalyzer::CreateWithFile( - const FilePath& file_path) { - // Map the file read-write so it can guarantee consistency between - // the analyzer and any trackers that my still be active. - std::unique_ptr mmfile(new MemoryMappedFile()); - mmfile->Initialize(file_path, MemoryMappedFile::READ_WRITE); - if (!mmfile->IsValid()) { - LogAnalyzerCreationError(kInvalidMemoryMappedFile); - return nullptr; - } - - if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) { - LogAnalyzerCreationError(kPmaBadFile); - return nullptr; - } - - return CreateWithAllocator(std::make_unique( - std::move(mmfile), 0, 0, StringPiece(), /*readonly=*/true)); -} -#endif // !defined(OS_NACL) - -// static -std::unique_ptr -GlobalActivityAnalyzer::CreateWithSharedMemory( - std::unique_ptr shm) { - if (shm->mapped_size() == 0 || - !SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) { - return nullptr; - } - return CreateWithAllocator(std::make_unique( - std::move(shm), 0, StringPiece(), /*readonly=*/true)); -} - -// static -std::unique_ptr -GlobalActivityAnalyzer::CreateWithSharedMemoryHandle( - const SharedMemoryHandle& handle, - size_t size) { - std::unique_ptr shm( - new SharedMemory(handle, /*readonly=*/true)); - if (!shm->Map(size)) - return nullptr; - return CreateWithSharedMemory(std::move(shm)); -} - -int64_t GlobalActivityAnalyzer::GetFirstProcess() { - PrepareAllAnalyzers(); - return GetNextProcess(); -} - -int64_t GlobalActivityAnalyzer::GetNextProcess() { - if (process_ids_.empty()) - return 0; - int64_t pid = process_ids_.back(); - process_ids_.pop_back(); - return pid; -} - -ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetFirstAnalyzer(int64_t pid) { - analyzers_iterator_ = analyzers_.begin(); - analyzers_iterator_pid_ = pid; - if (analyzers_iterator_ == analyzers_.end()) - return nullptr; - int64_t create_stamp; - if (analyzers_iterator_->second->GetProcessId(&create_stamp) == pid && - create_stamp <= analysis_stamp_) { - return analyzers_iterator_->second.get(); - } - return GetNextAnalyzer(); -} - -ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetNextAnalyzer() { - DCHECK(analyzers_iterator_ != analyzers_.end()); - int64_t create_stamp; - do { - ++analyzers_iterator_; - if (analyzers_iterator_ == analyzers_.end()) - return nullptr; - } while (analyzers_iterator_->second->GetProcessId(&create_stamp) != - analyzers_iterator_pid_ || - create_stamp > analysis_stamp_); - return analyzers_iterator_->second.get(); -} - -ThreadActivityAnalyzer* GlobalActivityAnalyzer::GetAnalyzerForThread( - const ThreadKey& key) { - auto found = analyzers_.find(key); - if (found == analyzers_.end()) - return nullptr; - return found->second.get(); -} - -ActivityUserData::Snapshot GlobalActivityAnalyzer::GetUserDataSnapshot( - int64_t pid, - uint32_t ref, - uint32_t id) { - ActivityUserData::Snapshot snapshot; - - void* memory = allocator_->GetAsArray( - ref, GlobalActivityTracker::kTypeIdUserDataRecord, - PersistentMemoryAllocator::kSizeAny); - if (memory) { - size_t size = allocator_->GetAllocSize(ref); - const ActivityUserData user_data(memory, size); - user_data.CreateSnapshot(&snapshot); - int64_t process_id; - int64_t create_stamp; - if (!ActivityUserData::GetOwningProcessId(memory, &process_id, - &create_stamp) || - process_id != pid || user_data.id() != id) { - // This allocation has been overwritten since it was created. Return an - // empty snapshot because whatever was captured is incorrect. - snapshot.clear(); - } - } - - return snapshot; -} - -const ActivityUserData::Snapshot& -GlobalActivityAnalyzer::GetProcessDataSnapshot(int64_t pid) { - auto iter = process_data_.find(pid); - if (iter == process_data_.end()) - return g_empty_user_data_snapshot.Get(); - if (iter->second.create_stamp > analysis_stamp_) - return g_empty_user_data_snapshot.Get(); - DCHECK_EQ(pid, iter->second.process_id); - return iter->second.data; -} - -std::vector GlobalActivityAnalyzer::GetLogMessages() { - std::vector messages; - PersistentMemoryAllocator::Reference ref; - - PersistentMemoryAllocator::Iterator iter(allocator_.get()); - while ((ref = iter.GetNextOfType( - GlobalActivityTracker::kTypeIdGlobalLogMessage)) != 0) { - const char* message = allocator_->GetAsArray( - ref, GlobalActivityTracker::kTypeIdGlobalLogMessage, - PersistentMemoryAllocator::kSizeAny); - if (message) - messages.push_back(message); - } - - return messages; -} - -std::vector -GlobalActivityAnalyzer::GetModules(int64_t pid) { - std::vector modules; - - PersistentMemoryAllocator::Iterator iter(allocator_.get()); - const GlobalActivityTracker::ModuleInfoRecord* record; - while ( - (record = - iter.GetNextOfObject()) != - nullptr) { - int64_t process_id; - int64_t create_stamp; - if (!OwningProcess::GetOwningProcessId(&record->owner, &process_id, - &create_stamp) || - pid != process_id || create_stamp > analysis_stamp_) { - continue; - } - GlobalActivityTracker::ModuleInfo info; - if (record->DecodeTo(&info, allocator_->GetAllocSize( - allocator_->GetAsReference(record)))) { - modules.push_back(std::move(info)); - } - } - - return modules; -} - -GlobalActivityAnalyzer::ProgramLocation -GlobalActivityAnalyzer::GetProgramLocationFromAddress(uint64_t address) { - // TODO(bcwhite): Implement this. - return { 0, 0 }; -} - -bool GlobalActivityAnalyzer::IsDataComplete() const { - DCHECK(allocator_); - return !allocator_->IsFull(); -} - -GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot() = default; -GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot( - const UserDataSnapshot& rhs) = default; -GlobalActivityAnalyzer::UserDataSnapshot::UserDataSnapshot( - UserDataSnapshot&& rhs) = default; -GlobalActivityAnalyzer::UserDataSnapshot::~UserDataSnapshot() = default; - -void GlobalActivityAnalyzer::PrepareAllAnalyzers() { - // Record the time when analysis started. - analysis_stamp_ = base::Time::Now().ToInternalValue(); - - // Fetch all the records. This will retrieve only ones created since the - // last run since the PMA iterator will continue from where it left off. - uint32_t type; - PersistentMemoryAllocator::Reference ref; - while ((ref = allocator_iterator_.GetNext(&type)) != 0) { - switch (type) { - case GlobalActivityTracker::kTypeIdActivityTracker: - case GlobalActivityTracker::kTypeIdActivityTrackerFree: - case GlobalActivityTracker::kTypeIdProcessDataRecord: - case GlobalActivityTracker::kTypeIdProcessDataRecordFree: - case PersistentMemoryAllocator::kTypeIdTransitioning: - // Active, free, or transitioning: add it to the list of references - // for later analysis. - memory_references_.insert(ref); - break; - } - } - - // Clear out any old information. - analyzers_.clear(); - process_data_.clear(); - process_ids_.clear(); - std::set seen_pids; - - // Go through all the known references and create objects for them with - // snapshots of the current state. - for (PersistentMemoryAllocator::Reference memory_ref : memory_references_) { - // Get the actual data segment for the tracker. Any type will do since it - // is checked below. - void* const base = allocator_->GetAsArray( - memory_ref, PersistentMemoryAllocator::kTypeIdAny, - PersistentMemoryAllocator::kSizeAny); - const size_t size = allocator_->GetAllocSize(memory_ref); - if (!base) - continue; - - switch (allocator_->GetType(memory_ref)) { - case GlobalActivityTracker::kTypeIdActivityTracker: { - // Create the analyzer on the data. This will capture a snapshot of the - // tracker state. This can fail if the tracker is somehow corrupted or - // is in the process of shutting down. - std::unique_ptr analyzer( - new ThreadActivityAnalyzer(base, size)); - if (!analyzer->IsValid()) - continue; - analyzer->AddGlobalInformation(this); - - // Track PIDs. - int64_t pid = analyzer->GetProcessId(); - if (seen_pids.find(pid) == seen_pids.end()) { - process_ids_.push_back(pid); - seen_pids.insert(pid); - } - - // Add this analyzer to the map of known ones, indexed by a unique - // thread - // identifier. - DCHECK(!base::ContainsKey(analyzers_, analyzer->GetThreadKey())); - analyzer->allocator_reference_ = ref; - analyzers_[analyzer->GetThreadKey()] = std::move(analyzer); - } break; - - case GlobalActivityTracker::kTypeIdProcessDataRecord: { - // Get the PID associated with this data record. - int64_t process_id; - int64_t create_stamp; - ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp); - DCHECK(!base::ContainsKey(process_data_, process_id)); - - // Create a snapshot of the data. This can fail if the data is somehow - // corrupted or the process shutdown and the memory being released. - UserDataSnapshot& snapshot = process_data_[process_id]; - snapshot.process_id = process_id; - snapshot.create_stamp = create_stamp; - const ActivityUserData process_data(base, size); - if (!process_data.CreateSnapshot(&snapshot.data)) - break; - - // Check that nothing changed. If it did, forget what was recorded. - ActivityUserData::GetOwningProcessId(base, &process_id, &create_stamp); - if (process_id != snapshot.process_id || - create_stamp != snapshot.create_stamp) { - process_data_.erase(process_id); - break; - } - - // Track PIDs. - if (seen_pids.find(process_id) == seen_pids.end()) { - process_ids_.push_back(process_id); - seen_pids.insert(process_id); - } - } break; - } - } - - // Reverse the list of PIDs so that they get popped in the order found. - std::reverse(process_ids_.begin(), process_ids_.end()); -} - -} // namespace debug -} // namespace base diff --git a/debug/activity_analyzer.h b/debug/activity_analyzer.h deleted file mode 100644 index 9add85a9e..000000000 --- a/debug/activity_analyzer.h +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2016 The Chromium 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_ACTIVITY_ANALYZER_H_ -#define BASE_DEBUG_ACTIVITY_ANALYZER_H_ - -#include -#include -#include -#include -#include - -#include "base/base_export.h" -#include "base/debug/activity_tracker.h" - -namespace base { -namespace debug { - -class GlobalActivityAnalyzer; - -// This class provides analysis of data captured from a ThreadActivityTracker. -// When created, it takes a snapshot of the data held by the tracker and -// makes that information available to other code. -class BASE_EXPORT ThreadActivityAnalyzer { - public: - struct BASE_EXPORT Snapshot : ThreadActivityTracker::Snapshot { - Snapshot(); - ~Snapshot(); - - // The user-data snapshot for an activity, matching the |activity_stack| - // of ThreadActivityTracker::Snapshot, if any. - std::vector user_data_stack; - }; - - // This class provides keys that uniquely identify a thread, even across - // multiple processes. - class ThreadKey { - public: - ThreadKey(int64_t pid, int64_t tid) : pid_(pid), tid_(tid) {} - - bool operator<(const ThreadKey& rhs) const { - if (pid_ != rhs.pid_) - return pid_ < rhs.pid_; - return tid_ < rhs.tid_; - } - - bool operator==(const ThreadKey& rhs) const { - return (pid_ == rhs.pid_ && tid_ == rhs.tid_); - } - - private: - int64_t pid_; - int64_t tid_; - }; - - // Creates an analyzer for an existing activity |tracker|. A snapshot is taken - // immediately and the tracker is not referenced again. - explicit ThreadActivityAnalyzer(const ThreadActivityTracker& tracker); - - // Creates an analyzer for a block of memory currently or previously in-use - // by an activity-tracker. A snapshot is taken immediately and the memory - // is not referenced again. - ThreadActivityAnalyzer(void* base, size_t size); - - // Creates an analyzer for a block of memory held within a persistent-memory - // |allocator| at the given |reference|. A snapshot is taken immediately and - // the memory is not referenced again. - ThreadActivityAnalyzer(PersistentMemoryAllocator* allocator, - PersistentMemoryAllocator::Reference reference); - - ~ThreadActivityAnalyzer(); - - // Adds information from the global analyzer. - void AddGlobalInformation(GlobalActivityAnalyzer* global); - - // Returns true iff the contained data is valid. Results from all other - // methods are undefined if this returns false. - bool IsValid() { return activity_snapshot_valid_; } - - // Gets the process id and its creation stamp. - int64_t GetProcessId(int64_t* out_stamp = nullptr) { - if (out_stamp) - *out_stamp = activity_snapshot_.create_stamp; - return activity_snapshot_.process_id; - } - - // Gets the name of the thread. - const std::string& GetThreadName() { - return activity_snapshot_.thread_name; - } - - // Gets the TheadKey for this thread. - ThreadKey GetThreadKey() { - return ThreadKey(activity_snapshot_.process_id, - activity_snapshot_.thread_id); - } - - const Snapshot& activity_snapshot() { return activity_snapshot_; } - - private: - friend class GlobalActivityAnalyzer; - - // The snapshot of the activity tracker taken at the moment of construction. - Snapshot activity_snapshot_; - - // Flag indicating if the snapshot data is valid. - bool activity_snapshot_valid_; - - // A reference into a persistent memory allocator, used by the global - // analyzer to know where this tracker came from. - PersistentMemoryAllocator::Reference allocator_reference_ = 0; - - DISALLOW_COPY_AND_ASSIGN(ThreadActivityAnalyzer); -}; - - -// This class manages analyzers for all known processes and threads as stored -// in a persistent memory allocator. It supports retrieval of them through -// iteration and directly using a ThreadKey, which allows for cross-references -// to be resolved. -// Note that though atomic snapshots are used and everything has its snapshot -// taken at the same time, the multi-snapshot itself is not atomic and thus may -// show small inconsistencies between threads if attempted on a live system. -class BASE_EXPORT GlobalActivityAnalyzer { - public: - struct ProgramLocation { - int module; - uintptr_t offset; - }; - - using ThreadKey = ThreadActivityAnalyzer::ThreadKey; - - // Creates a global analyzer from a persistent memory allocator. - explicit GlobalActivityAnalyzer( - std::unique_ptr allocator); - - ~GlobalActivityAnalyzer(); - - // Creates a global analyzer using a given persistent-memory |allocator|. - static std::unique_ptr CreateWithAllocator( - std::unique_ptr allocator); - -#if !defined(OS_NACL) - // Creates a global analyzer using the contents of a file given in - // |file_path|. - static std::unique_ptr CreateWithFile( - const FilePath& file_path); -#endif // !defined(OS_NACL) - - // Like above but accesses an allocator in a mapped shared-memory segment. - static std::unique_ptr CreateWithSharedMemory( - std::unique_ptr shm); - - // Like above but takes a handle to an existing shared memory segment and - // maps it before creating the tracker. - static std::unique_ptr CreateWithSharedMemoryHandle( - const SharedMemoryHandle& handle, - size_t size); - - // Iterates over all known valid processes and returns their PIDs or zero - // if there are no more. Calls to GetFirstProcess() will perform a global - // snapshot in order to provide a relatively consistent state across the - // future calls to GetNextProcess() and GetFirst/NextAnalyzer(). PIDs are - // returned in the order they're found meaning that a first-launched - // controlling process will be found first. Note, however, that space - // freed by an exiting process may be re-used by a later process. - int64_t GetFirstProcess(); - int64_t GetNextProcess(); - - // Iterates over all known valid analyzers for the a given process or returns - // null if there are no more. - // - // GetFirstProcess() must be called first in order to capture a global - // snapshot! Ownership stays with the global analyzer object and all existing - // analyzer pointers are invalidated when GetFirstProcess() is called. - ThreadActivityAnalyzer* GetFirstAnalyzer(int64_t pid); - ThreadActivityAnalyzer* GetNextAnalyzer(); - - // Gets the analyzer for a specific thread or null if there is none. - // Ownership stays with the global analyzer object. - ThreadActivityAnalyzer* GetAnalyzerForThread(const ThreadKey& key); - - // Extract user data based on a reference and its identifier. - ActivityUserData::Snapshot GetUserDataSnapshot(int64_t pid, - uint32_t ref, - uint32_t id); - - // Extract the data for a specific process. An empty snapshot will be - // returned if the process is not known. - const ActivityUserData::Snapshot& GetProcessDataSnapshot(int64_t pid); - - // Gets all log messages stored within. - std::vector GetLogMessages(); - - // Gets modules corresponding to a pid. This pid must come from a call to - // GetFirst/NextProcess. Only modules that were first registered prior to - // GetFirstProcess's snapshot are returned. - std::vector GetModules(int64_t pid); - - // Gets the corresponding "program location" for a given "program counter". - // This will return {0,0} if no mapping could be found. - ProgramLocation GetProgramLocationFromAddress(uint64_t address); - - // Returns whether the data is complete. Data can be incomplete if the - // recording size quota is hit. - bool IsDataComplete() const; - - private: - using AnalyzerMap = - std::map>; - - struct UserDataSnapshot { - // Complex class needs out-of-line ctor/dtor. - UserDataSnapshot(); - UserDataSnapshot(const UserDataSnapshot& rhs); - UserDataSnapshot(UserDataSnapshot&& rhs); - ~UserDataSnapshot(); - - int64_t process_id; - int64_t create_stamp; - ActivityUserData::Snapshot data; - }; - - // Finds, creates, and indexes analyzers for all known processes and threads. - void PrepareAllAnalyzers(); - - // The persistent memory allocator holding all tracking data. - std::unique_ptr allocator_; - - // The time stamp when analysis began. This is used to prevent looking into - // process IDs that get reused when analyzing a live system. - int64_t analysis_stamp_; - - // The iterator for finding tracking information in the allocator. - PersistentMemoryAllocator::Iterator allocator_iterator_; - - // A set of all interesting memory references found within the allocator. - std::set memory_references_; - - // A set of all process-data memory references found within the allocator. - std::map process_data_; - - // A set of all process IDs collected during PrepareAllAnalyzers. These are - // popped and returned one-by-one with calls to GetFirst/NextProcess(). - std::vector process_ids_; - - // A map, keyed by ThreadKey, of all valid activity analyzers. - AnalyzerMap analyzers_; - - // The iterator within the analyzers_ map for returning analyzers through - // first/next iteration. - AnalyzerMap::iterator analyzers_iterator_; - int64_t analyzers_iterator_pid_; - - DISALLOW_COPY_AND_ASSIGN(GlobalActivityAnalyzer); -}; - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_ACTIVITY_ANALYZER_H_ diff --git a/debug/activity_analyzer_unittest.cc b/debug/activity_analyzer_unittest.cc deleted file mode 100644 index e08b43aff..000000000 --- a/debug/activity_analyzer_unittest.cc +++ /dev/null @@ -1,546 +0,0 @@ -// Copyright 2016 The Chromium 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/activity_analyzer.h" - -#include -#include - -#include "base/auto_reset.h" -#include "base/bind.h" -#include "base/debug/activity_tracker.h" -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/memory_mapped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/ptr_util.h" -#include "base/pending_task.h" -#include "base/process/process.h" -#include "base/stl_util.h" -#include "base/synchronization/condition_variable.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/spin_wait.h" -#include "base/threading/platform_thread.h" -#include "base/threading/simple_thread.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace debug { - -namespace { - -class TestActivityTracker : public ThreadActivityTracker { - public: - TestActivityTracker(std::unique_ptr memory, size_t mem_size) - : ThreadActivityTracker(memset(memory.get(), 0, mem_size), mem_size), - mem_segment_(std::move(memory)) {} - - ~TestActivityTracker() override = default; - - private: - std::unique_ptr mem_segment_; -}; - -} // namespace - - -class ActivityAnalyzerTest : public testing::Test { - public: - const int kMemorySize = 1 << 20; // 1MiB - const int kStackSize = 1 << 10; // 1KiB - - ActivityAnalyzerTest() = default; - - ~ActivityAnalyzerTest() override { - GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); - if (global_tracker) { - global_tracker->ReleaseTrackerForCurrentThreadForTesting(); - delete global_tracker; - } - } - - std::unique_ptr CreateActivityTracker() { - std::unique_ptr memory(new char[kStackSize]); - return std::make_unique(std::move(memory), kStackSize); - } - - template - void AsOtherProcess(int64_t pid, Function function) { - std::unique_ptr old_global = - GlobalActivityTracker::ReleaseForTesting(); - ASSERT_TRUE(old_global); - - PersistentMemoryAllocator* old_allocator = old_global->allocator(); - std::unique_ptr new_allocator( - std::make_unique( - const_cast(old_allocator->data()), old_allocator->size(), 0, - 0, "", false)); - GlobalActivityTracker::CreateWithAllocator(std::move(new_allocator), 3, - pid); - - function(); - - GlobalActivityTracker::ReleaseForTesting(); - GlobalActivityTracker::SetForTesting(std::move(old_global)); - } - - static void DoNothing() {} -}; - -TEST_F(ActivityAnalyzerTest, ThreadAnalyzerConstruction) { - std::unique_ptr tracker = CreateActivityTracker(); - { - ThreadActivityAnalyzer analyzer(*tracker); - EXPECT_TRUE(analyzer.IsValid()); - EXPECT_EQ(PlatformThread::GetName(), analyzer.GetThreadName()); - } - - // TODO(bcwhite): More tests once Analyzer does more. -} - - -// GlobalActivityAnalyzer tests below. - -namespace { - -class SimpleActivityThread : public SimpleThread { - public: - SimpleActivityThread(const std::string& name, - const void* source, - Activity::Type activity, - const ActivityData& data) - : SimpleThread(name, Options()), - source_(source), - activity_(activity), - data_(data), - ready_(false), - exit_(false), - exit_condition_(&lock_) {} - - ~SimpleActivityThread() override = default; - - void Run() override { - ThreadActivityTracker::ActivityId id = - GlobalActivityTracker::Get() - ->GetOrCreateTrackerForCurrentThread() - ->PushActivity(source_, activity_, data_); - - { - AutoLock auto_lock(lock_); - ready_.store(true, std::memory_order_release); - while (!exit_.load(std::memory_order_relaxed)) - exit_condition_.Wait(); - } - - GlobalActivityTracker::Get()->GetTrackerForCurrentThread()->PopActivity(id); - } - - void Exit() { - AutoLock auto_lock(lock_); - exit_.store(true, std::memory_order_relaxed); - exit_condition_.Signal(); - } - - void WaitReady() { - SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_.load(std::memory_order_acquire)); - } - - private: - const void* source_; - Activity::Type activity_; - ActivityData data_; - - std::atomic ready_; - std::atomic exit_; - Lock lock_; - ConditionVariable exit_condition_; - - DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread); -}; - -} // namespace - -TEST_F(ActivityAnalyzerTest, GlobalAnalyzerConstruction) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - GlobalActivityTracker::Get()->process_data().SetString("foo", "bar"); - - PersistentMemoryAllocator* allocator = - GlobalActivityTracker::Get()->allocator(); - GlobalActivityAnalyzer analyzer(std::make_unique( - const_cast(allocator->data()), allocator->size(), 0, 0, "", true)); - - // The only thread at this point is the test thread of this process. - const int64_t pid = analyzer.GetFirstProcess(); - ASSERT_NE(0, pid); - ThreadActivityAnalyzer* ta1 = analyzer.GetFirstAnalyzer(pid); - ASSERT_TRUE(ta1); - EXPECT_FALSE(analyzer.GetNextAnalyzer()); - ThreadActivityAnalyzer::ThreadKey tk1 = ta1->GetThreadKey(); - EXPECT_EQ(ta1, analyzer.GetAnalyzerForThread(tk1)); - EXPECT_EQ(0, analyzer.GetNextProcess()); - - // Create a second thread that will do something. - SimpleActivityThread t2("t2", nullptr, Activity::ACT_TASK, - ActivityData::ForTask(11)); - t2.Start(); - t2.WaitReady(); - - // Now there should be two. Calling GetFirstProcess invalidates any - // previously returned analyzer pointers. - ASSERT_EQ(pid, analyzer.GetFirstProcess()); - EXPECT_TRUE(analyzer.GetFirstAnalyzer(pid)); - EXPECT_TRUE(analyzer.GetNextAnalyzer()); - EXPECT_FALSE(analyzer.GetNextAnalyzer()); - EXPECT_EQ(0, analyzer.GetNextProcess()); - - // Let thread exit. - t2.Exit(); - t2.Join(); - - // Now there should be only one again. - ASSERT_EQ(pid, analyzer.GetFirstProcess()); - ThreadActivityAnalyzer* ta2 = analyzer.GetFirstAnalyzer(pid); - ASSERT_TRUE(ta2); - EXPECT_FALSE(analyzer.GetNextAnalyzer()); - ThreadActivityAnalyzer::ThreadKey tk2 = ta2->GetThreadKey(); - EXPECT_EQ(ta2, analyzer.GetAnalyzerForThread(tk2)); - EXPECT_EQ(tk1, tk2); - EXPECT_EQ(0, analyzer.GetNextProcess()); - - // Verify that there is process data. - const ActivityUserData::Snapshot& data_snapshot = - analyzer.GetProcessDataSnapshot(pid); - ASSERT_LE(1U, data_snapshot.size()); - EXPECT_EQ("bar", data_snapshot.at("foo").GetString()); -} - -TEST_F(ActivityAnalyzerTest, GlobalAnalyzerFromSharedMemory) { - SharedMemoryHandle handle1; - SharedMemoryHandle handle2; - - { - std::unique_ptr shmem(new SharedMemory()); - ASSERT_TRUE(shmem->CreateAndMapAnonymous(kMemorySize)); - handle1 = shmem->handle().Duplicate(); - ASSERT_TRUE(handle1.IsValid()); - handle2 = shmem->handle().Duplicate(); - ASSERT_TRUE(handle2.IsValid()); - } - - GlobalActivityTracker::CreateWithSharedMemoryHandle(handle1, kMemorySize, 0, - "", 3); - GlobalActivityTracker::Get()->process_data().SetString("foo", "bar"); - - std::unique_ptr analyzer = - GlobalActivityAnalyzer::CreateWithSharedMemoryHandle(handle2, - kMemorySize); - - const int64_t pid = analyzer->GetFirstProcess(); - ASSERT_NE(0, pid); - const ActivityUserData::Snapshot& data_snapshot = - analyzer->GetProcessDataSnapshot(pid); - ASSERT_LE(1U, data_snapshot.size()); - EXPECT_EQ("bar", data_snapshot.at("foo").GetString()); -} - -TEST_F(ActivityAnalyzerTest, UserDataSnapshotTest) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - ThreadActivityAnalyzer::Snapshot tracker_snapshot; - - const char string1a[] = "string1a"; - const char string1b[] = "string1b"; - const char string2a[] = "string2a"; - const char string2b[] = "string2b"; - - PersistentMemoryAllocator* allocator = - GlobalActivityTracker::Get()->allocator(); - GlobalActivityAnalyzer global_analyzer( - std::make_unique( - const_cast(allocator->data()), allocator->size(), 0, 0, "", - true)); - - ThreadActivityTracker* tracker = - GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); - - { - ScopedActivity activity1(1, 11, 111); - ActivityUserData& user_data1 = activity1.user_data(); - user_data1.Set("raw1", "foo1", 4); - user_data1.SetString("string1", "bar1"); - user_data1.SetChar("char1", '1'); - user_data1.SetInt("int1", -1111); - user_data1.SetUint("uint1", 1111); - user_data1.SetBool("bool1", true); - user_data1.SetReference("ref1", string1a, sizeof(string1a)); - user_data1.SetStringReference("sref1", string1b); - - { - ScopedActivity activity2(2, 22, 222); - ActivityUserData& user_data2 = activity2.user_data(); - user_data2.Set("raw2", "foo2", 4); - user_data2.SetString("string2", "bar2"); - user_data2.SetChar("char2", '2'); - user_data2.SetInt("int2", -2222); - user_data2.SetUint("uint2", 2222); - user_data2.SetBool("bool2", false); - user_data2.SetReference("ref2", string2a, sizeof(string2a)); - user_data2.SetStringReference("sref2", string2b); - - ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot)); - ASSERT_EQ(2U, tracker_snapshot.activity_stack.size()); - - ThreadActivityAnalyzer analyzer(*tracker); - analyzer.AddGlobalInformation(&global_analyzer); - const ThreadActivityAnalyzer::Snapshot& analyzer_snapshot = - analyzer.activity_snapshot(); - ASSERT_EQ(2U, analyzer_snapshot.user_data_stack.size()); - const ActivityUserData::Snapshot& user_data = - analyzer_snapshot.user_data_stack.at(1); - EXPECT_EQ(8U, user_data.size()); - ASSERT_TRUE(ContainsKey(user_data, "raw2")); - EXPECT_EQ("foo2", user_data.at("raw2").Get().as_string()); - ASSERT_TRUE(ContainsKey(user_data, "string2")); - EXPECT_EQ("bar2", user_data.at("string2").GetString().as_string()); - ASSERT_TRUE(ContainsKey(user_data, "char2")); - EXPECT_EQ('2', user_data.at("char2").GetChar()); - ASSERT_TRUE(ContainsKey(user_data, "int2")); - EXPECT_EQ(-2222, user_data.at("int2").GetInt()); - ASSERT_TRUE(ContainsKey(user_data, "uint2")); - EXPECT_EQ(2222U, user_data.at("uint2").GetUint()); - ASSERT_TRUE(ContainsKey(user_data, "bool2")); - EXPECT_FALSE(user_data.at("bool2").GetBool()); - ASSERT_TRUE(ContainsKey(user_data, "ref2")); - EXPECT_EQ(string2a, user_data.at("ref2").GetReference().data()); - EXPECT_EQ(sizeof(string2a), user_data.at("ref2").GetReference().size()); - ASSERT_TRUE(ContainsKey(user_data, "sref2")); - EXPECT_EQ(string2b, user_data.at("sref2").GetStringReference().data()); - EXPECT_EQ(strlen(string2b), - user_data.at("sref2").GetStringReference().size()); - } - - ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot)); - ASSERT_EQ(1U, tracker_snapshot.activity_stack.size()); - - ThreadActivityAnalyzer analyzer(*tracker); - analyzer.AddGlobalInformation(&global_analyzer); - const ThreadActivityAnalyzer::Snapshot& analyzer_snapshot = - analyzer.activity_snapshot(); - ASSERT_EQ(1U, analyzer_snapshot.user_data_stack.size()); - const ActivityUserData::Snapshot& user_data = - analyzer_snapshot.user_data_stack.at(0); - EXPECT_EQ(8U, user_data.size()); - EXPECT_EQ("foo1", user_data.at("raw1").Get().as_string()); - EXPECT_EQ("bar1", user_data.at("string1").GetString().as_string()); - EXPECT_EQ('1', user_data.at("char1").GetChar()); - EXPECT_EQ(-1111, user_data.at("int1").GetInt()); - EXPECT_EQ(1111U, user_data.at("uint1").GetUint()); - EXPECT_TRUE(user_data.at("bool1").GetBool()); - EXPECT_EQ(string1a, user_data.at("ref1").GetReference().data()); - EXPECT_EQ(sizeof(string1a), user_data.at("ref1").GetReference().size()); - EXPECT_EQ(string1b, user_data.at("sref1").GetStringReference().data()); - EXPECT_EQ(strlen(string1b), - user_data.at("sref1").GetStringReference().size()); - } - - ASSERT_TRUE(tracker->CreateSnapshot(&tracker_snapshot)); - ASSERT_EQ(0U, tracker_snapshot.activity_stack.size()); -} - -TEST_F(ActivityAnalyzerTest, GlobalUserDataTest) { - const int64_t pid = GetCurrentProcId(); - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - - const char string1[] = "foo"; - const char string2[] = "bar"; - - PersistentMemoryAllocator* allocator = - GlobalActivityTracker::Get()->allocator(); - GlobalActivityAnalyzer global_analyzer( - std::make_unique( - const_cast(allocator->data()), allocator->size(), 0, 0, "", - true)); - - ActivityUserData& process_data = GlobalActivityTracker::Get()->process_data(); - ASSERT_NE(0U, process_data.id()); - process_data.Set("raw", "foo", 3); - process_data.SetString("string", "bar"); - process_data.SetChar("char", '9'); - process_data.SetInt("int", -9999); - process_data.SetUint("uint", 9999); - process_data.SetBool("bool", true); - process_data.SetReference("ref", string1, sizeof(string1)); - process_data.SetStringReference("sref", string2); - - int64_t first_pid = global_analyzer.GetFirstProcess(); - DCHECK_EQ(pid, first_pid); - const ActivityUserData::Snapshot& snapshot = - global_analyzer.GetProcessDataSnapshot(pid); - ASSERT_TRUE(ContainsKey(snapshot, "raw")); - EXPECT_EQ("foo", snapshot.at("raw").Get().as_string()); - ASSERT_TRUE(ContainsKey(snapshot, "string")); - EXPECT_EQ("bar", snapshot.at("string").GetString().as_string()); - ASSERT_TRUE(ContainsKey(snapshot, "char")); - EXPECT_EQ('9', snapshot.at("char").GetChar()); - ASSERT_TRUE(ContainsKey(snapshot, "int")); - EXPECT_EQ(-9999, snapshot.at("int").GetInt()); - ASSERT_TRUE(ContainsKey(snapshot, "uint")); - EXPECT_EQ(9999U, snapshot.at("uint").GetUint()); - ASSERT_TRUE(ContainsKey(snapshot, "bool")); - EXPECT_TRUE(snapshot.at("bool").GetBool()); - ASSERT_TRUE(ContainsKey(snapshot, "ref")); - EXPECT_EQ(string1, snapshot.at("ref").GetReference().data()); - EXPECT_EQ(sizeof(string1), snapshot.at("ref").GetReference().size()); - ASSERT_TRUE(ContainsKey(snapshot, "sref")); - EXPECT_EQ(string2, snapshot.at("sref").GetStringReference().data()); - EXPECT_EQ(strlen(string2), snapshot.at("sref").GetStringReference().size()); -} - -TEST_F(ActivityAnalyzerTest, GlobalModulesTest) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - - PersistentMemoryAllocator* allocator = global->allocator(); - GlobalActivityAnalyzer global_analyzer( - std::make_unique( - const_cast(allocator->data()), allocator->size(), 0, 0, "", - true)); - - GlobalActivityTracker::ModuleInfo info1; - info1.is_loaded = true; - info1.address = 0x12345678; - info1.load_time = 1111; - info1.size = 0xABCDEF; - info1.timestamp = 111; - info1.age = 11; - info1.identifier[0] = 1; - info1.file = "anything"; - info1.debug_file = "elsewhere"; - - global->RecordModuleInfo(info1); - std::vector modules1; - modules1 = global_analyzer.GetModules(global_analyzer.GetFirstProcess()); - ASSERT_EQ(1U, modules1.size()); - GlobalActivityTracker::ModuleInfo& stored1a = modules1[0]; - EXPECT_EQ(info1.is_loaded, stored1a.is_loaded); - EXPECT_EQ(info1.address, stored1a.address); - EXPECT_NE(info1.load_time, stored1a.load_time); - EXPECT_EQ(info1.size, stored1a.size); - EXPECT_EQ(info1.timestamp, stored1a.timestamp); - EXPECT_EQ(info1.age, stored1a.age); - EXPECT_EQ(info1.identifier[0], stored1a.identifier[0]); - EXPECT_EQ(info1.file, stored1a.file); - EXPECT_EQ(info1.debug_file, stored1a.debug_file); - - info1.is_loaded = false; - global->RecordModuleInfo(info1); - modules1 = global_analyzer.GetModules(global_analyzer.GetFirstProcess()); - ASSERT_EQ(1U, modules1.size()); - GlobalActivityTracker::ModuleInfo& stored1b = modules1[0]; - EXPECT_EQ(info1.is_loaded, stored1b.is_loaded); - EXPECT_EQ(info1.address, stored1b.address); - EXPECT_NE(info1.load_time, stored1b.load_time); - EXPECT_EQ(info1.size, stored1b.size); - EXPECT_EQ(info1.timestamp, stored1b.timestamp); - EXPECT_EQ(info1.age, stored1b.age); - EXPECT_EQ(info1.identifier[0], stored1b.identifier[0]); - EXPECT_EQ(info1.file, stored1b.file); - EXPECT_EQ(info1.debug_file, stored1b.debug_file); - - GlobalActivityTracker::ModuleInfo info2; - info2.is_loaded = true; - info2.address = 0x87654321; - info2.load_time = 2222; - info2.size = 0xFEDCBA; - info2.timestamp = 222; - info2.age = 22; - info2.identifier[0] = 2; - info2.file = "nothing"; - info2.debug_file = "farewell"; - - global->RecordModuleInfo(info2); - std::vector modules2; - modules2 = global_analyzer.GetModules(global_analyzer.GetFirstProcess()); - ASSERT_EQ(2U, modules2.size()); - GlobalActivityTracker::ModuleInfo& stored2 = modules2[1]; - EXPECT_EQ(info2.is_loaded, stored2.is_loaded); - EXPECT_EQ(info2.address, stored2.address); - EXPECT_NE(info2.load_time, stored2.load_time); - EXPECT_EQ(info2.size, stored2.size); - EXPECT_EQ(info2.timestamp, stored2.timestamp); - EXPECT_EQ(info2.age, stored2.age); - EXPECT_EQ(info2.identifier[0], stored2.identifier[0]); - EXPECT_EQ(info2.file, stored2.file); - EXPECT_EQ(info2.debug_file, stored2.debug_file); -} - -TEST_F(ActivityAnalyzerTest, GlobalLogMessages) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - - PersistentMemoryAllocator* allocator = - GlobalActivityTracker::Get()->allocator(); - GlobalActivityAnalyzer analyzer(std::make_unique( - const_cast(allocator->data()), allocator->size(), 0, 0, "", true)); - - GlobalActivityTracker::Get()->RecordLogMessage("hello world"); - GlobalActivityTracker::Get()->RecordLogMessage("foo bar"); - - std::vector messages = analyzer.GetLogMessages(); - ASSERT_EQ(2U, messages.size()); - EXPECT_EQ("hello world", messages[0]); - EXPECT_EQ("foo bar", messages[1]); -} - -TEST_F(ActivityAnalyzerTest, GlobalMultiProcess) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 1001); - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - PersistentMemoryAllocator* allocator = global->allocator(); - EXPECT_EQ(1001, global->process_id()); - - int64_t process_id; - int64_t create_stamp; - ActivityUserData::GetOwningProcessId( - GlobalActivityTracker::Get()->process_data().GetBaseAddress(), - &process_id, &create_stamp); - ASSERT_EQ(1001, process_id); - - GlobalActivityTracker::Get()->process_data().SetInt("pid", - global->process_id()); - - GlobalActivityAnalyzer analyzer(std::make_unique( - const_cast(allocator->data()), allocator->size(), 0, 0, "", true)); - - AsOtherProcess(2002, [&global]() { - ASSERT_NE(global, GlobalActivityTracker::Get()); - EXPECT_EQ(2002, GlobalActivityTracker::Get()->process_id()); - - int64_t process_id; - int64_t create_stamp; - ActivityUserData::GetOwningProcessId( - GlobalActivityTracker::Get()->process_data().GetBaseAddress(), - &process_id, &create_stamp); - ASSERT_EQ(2002, process_id); - - GlobalActivityTracker::Get()->process_data().SetInt( - "pid", GlobalActivityTracker::Get()->process_id()); - }); - ASSERT_EQ(global, GlobalActivityTracker::Get()); - EXPECT_EQ(1001, GlobalActivityTracker::Get()->process_id()); - - const int64_t pid1 = analyzer.GetFirstProcess(); - ASSERT_EQ(1001, pid1); - const int64_t pid2 = analyzer.GetNextProcess(); - ASSERT_EQ(2002, pid2); - EXPECT_EQ(0, analyzer.GetNextProcess()); - - const ActivityUserData::Snapshot& pdata1 = - analyzer.GetProcessDataSnapshot(pid1); - const ActivityUserData::Snapshot& pdata2 = - analyzer.GetProcessDataSnapshot(pid2); - EXPECT_EQ(1001, pdata1.at("pid").GetInt()); - EXPECT_EQ(2002, pdata2.at("pid").GetInt()); -} - -} // namespace debug -} // namespace base diff --git a/debug/activity_tracker.cc b/debug/activity_tracker.cc deleted file mode 100644 index 24cfa5a47..000000000 --- a/debug/activity_tracker.cc +++ /dev/null @@ -1,1828 +0,0 @@ -// Copyright 2016 The Chromium 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/activity_tracker.h" - -#include -#include -#include - -#include "base/atomic_sequence_num.h" -#include "base/debug/stack_trace.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/files/memory_mapped_file.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/field_trial.h" -#include "base/metrics/histogram_macros.h" -#include "base/pending_task.h" -#include "base/pickle.h" -#include "base/process/process.h" -#include "base/process/process_handle.h" -#include "base/stl_util.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" - -namespace base { -namespace debug { - -namespace { - -// The minimum depth a stack should support. -const int kMinStackDepth = 2; - -// The amount of memory set aside for holding arbitrary user data (key/value -// pairs) globally or associated with ActivityData entries. -const size_t kUserDataSize = 1 << 10; // 1 KiB -const size_t kProcessDataSize = 4 << 10; // 4 KiB -const size_t kMaxUserDataNameLength = - static_cast(std::numeric_limits::max()); - -// A constant used to indicate that module information is changing. -const uint32_t kModuleInformationChanging = 0x80000000; - -// The key used to record process information. -const char kProcessPhaseDataKey[] = "process-phase"; - -// An atomically incrementing number, used to check for recreations of objects -// in the same memory space. -AtomicSequenceNumber g_next_id; - -// Gets the next non-zero identifier. It is only unique within a process. -uint32_t GetNextDataId() { - uint32_t id; - while ((id = g_next_id.GetNext()) == 0) - ; - return id; -} - -// Gets the current process-id, either from the GlobalActivityTracker if it -// exists (where the PID can be defined for testing) or from the system if -// there isn't such. -int64_t GetProcessId() { - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - if (global) - return global->process_id(); - return GetCurrentProcId(); -} - -// Finds and reuses a specific allocation or creates a new one. -PersistentMemoryAllocator::Reference AllocateFrom( - PersistentMemoryAllocator* allocator, - uint32_t from_type, - size_t size, - uint32_t to_type) { - PersistentMemoryAllocator::Iterator iter(allocator); - PersistentMemoryAllocator::Reference ref; - while ((ref = iter.GetNextOfType(from_type)) != 0) { - DCHECK_LE(size, allocator->GetAllocSize(ref)); - // This can fail if a another thread has just taken it. It is assumed that - // the memory is cleared during the "free" operation. - if (allocator->ChangeType(ref, to_type, from_type, /*clear=*/false)) - return ref; - } - - return allocator->Allocate(size, to_type); -} - -// Determines the previous aligned index. -size_t RoundDownToAlignment(size_t index, size_t alignment) { - return index & (0 - alignment); -} - -// Determines the next aligned index. -size_t RoundUpToAlignment(size_t index, size_t alignment) { - return (index + (alignment - 1)) & (0 - alignment); -} - -// Converts "tick" timing into wall time. -Time WallTimeFromTickTime(int64_t ticks_start, int64_t ticks, Time time_start) { - return time_start + TimeDelta::FromInternalValue(ticks - ticks_start); -} - -} // namespace - -union ThreadRef { - int64_t as_id; -#if defined(OS_WIN) - // On Windows, the handle itself is often a pseudo-handle with a common - // value meaning "this thread" and so the thread-id is used. The former - // can be converted to a thread-id with a system call. - PlatformThreadId as_tid; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // On Posix and Fuchsia, the handle is always a unique identifier so no - // conversion needs to be done. However, its value is officially opaque so - // there is no one correct way to convert it to a numerical identifier. - PlatformThreadHandle::Handle as_handle; -#endif -}; - -OwningProcess::OwningProcess() = default; -OwningProcess::~OwningProcess() = default; - -void OwningProcess::Release_Initialize(int64_t pid) { - uint32_t old_id = data_id.load(std::memory_order_acquire); - DCHECK_EQ(0U, old_id); - process_id = pid != 0 ? pid : GetProcessId(); - create_stamp = Time::Now().ToInternalValue(); - data_id.store(GetNextDataId(), std::memory_order_release); -} - -void OwningProcess::SetOwningProcessIdForTesting(int64_t pid, int64_t stamp) { - DCHECK_NE(0U, data_id); - process_id = pid; - create_stamp = stamp; -} - -// static -bool OwningProcess::GetOwningProcessId(const void* memory, - int64_t* out_id, - int64_t* out_stamp) { - const OwningProcess* info = reinterpret_cast(memory); - uint32_t id = info->data_id.load(std::memory_order_acquire); - if (id == 0) - return false; - - *out_id = info->process_id; - *out_stamp = info->create_stamp; - return id == info->data_id.load(std::memory_order_seq_cst); -} - -// It doesn't matter what is contained in this (though it will be all zeros) -// as only the address of it is important. -const ActivityData kNullActivityData = {}; - -ActivityData ActivityData::ForThread(const PlatformThreadHandle& handle) { - ThreadRef thread_ref; - thread_ref.as_id = 0; // Zero the union in case other is smaller. -#if defined(OS_WIN) - thread_ref.as_tid = ::GetThreadId(handle.platform_handle()); -#elif defined(OS_POSIX) - thread_ref.as_handle = handle.platform_handle(); -#endif - return ForThread(thread_ref.as_id); -} - -ActivityTrackerMemoryAllocator::ActivityTrackerMemoryAllocator( - PersistentMemoryAllocator* allocator, - uint32_t object_type, - uint32_t object_free_type, - size_t object_size, - size_t cache_size, - bool make_iterable) - : allocator_(allocator), - object_type_(object_type), - object_free_type_(object_free_type), - object_size_(object_size), - cache_size_(cache_size), - make_iterable_(make_iterable), - iterator_(allocator), - cache_values_(new Reference[cache_size]), - cache_used_(0) { - DCHECK(allocator); -} - -ActivityTrackerMemoryAllocator::~ActivityTrackerMemoryAllocator() = default; - -ActivityTrackerMemoryAllocator::Reference -ActivityTrackerMemoryAllocator::GetObjectReference() { - // First see if there is a cached value that can be returned. This is much - // faster than searching the memory system for free blocks. - while (cache_used_ > 0) { - Reference cached = cache_values_[--cache_used_]; - // Change the type of the cached object to the proper type and return it. - // If the type-change fails that means another thread has taken this from - // under us (via the search below) so ignore it and keep trying. Don't - // clear the memory because that was done when the type was made "free". - if (allocator_->ChangeType(cached, object_type_, object_free_type_, false)) - return cached; - } - - // Fetch the next "free" object from persistent memory. Rather than restart - // the iterator at the head each time and likely waste time going again - // through objects that aren't relevant, the iterator continues from where - // it last left off and is only reset when the end is reached. If the - // returned reference matches |last|, then it has wrapped without finding - // anything. - const Reference last = iterator_.GetLast(); - while (true) { - uint32_t type; - Reference found = iterator_.GetNext(&type); - if (found && type == object_free_type_) { - // Found a free object. Change it to the proper type and return it. If - // the type-change fails that means another thread has taken this from - // under us so ignore it and keep trying. - if (allocator_->ChangeType(found, object_type_, object_free_type_, false)) - return found; - } - if (found == last) { - // Wrapped. No desired object was found. - break; - } - if (!found) { - // Reached end; start over at the beginning. - iterator_.Reset(); - } - } - - // No free block was found so instead allocate a new one. - Reference allocated = allocator_->Allocate(object_size_, object_type_); - if (allocated && make_iterable_) - allocator_->MakeIterable(allocated); - return allocated; -} - -void ActivityTrackerMemoryAllocator::ReleaseObjectReference(Reference ref) { - // Mark object as free. - bool success = allocator_->ChangeType(ref, object_free_type_, object_type_, - /*clear=*/true); - DCHECK(success); - - // Add this reference to our "free" cache if there is space. If not, the type - // has still been changed to indicate that it is free so this (or another) - // thread can find it, albeit more slowly, using the iteration method above. - if (cache_used_ < cache_size_) - cache_values_[cache_used_++] = ref; -} - -// static -void Activity::FillFrom(Activity* activity, - const void* program_counter, - const void* origin, - Type type, - const ActivityData& data) { - activity->time_internal = base::TimeTicks::Now().ToInternalValue(); - activity->calling_address = reinterpret_cast(program_counter); - activity->origin_address = reinterpret_cast(origin); - activity->activity_type = type; - activity->data = data; - -#if (!defined(OS_NACL) && DCHECK_IS_ON()) || defined(ADDRESS_SANITIZER) - // Create a stacktrace from the current location and get the addresses for - // improved debuggability. - StackTrace stack_trace; - size_t stack_depth; - const void* const* stack_addrs = stack_trace.Addresses(&stack_depth); - // Copy the stack addresses, ignoring the first one (here). - size_t i; - for (i = 1; i < stack_depth && i < kActivityCallStackSize; ++i) { - activity->call_stack[i - 1] = reinterpret_cast(stack_addrs[i]); - } - activity->call_stack[i - 1] = 0; -#else - activity->call_stack[0] = 0; -#endif -} - -ActivityUserData::TypedValue::TypedValue() = default; -ActivityUserData::TypedValue::TypedValue(const TypedValue& other) = default; -ActivityUserData::TypedValue::~TypedValue() = default; - -StringPiece ActivityUserData::TypedValue::Get() const { - DCHECK_EQ(RAW_VALUE, type_); - return long_value_; -} - -StringPiece ActivityUserData::TypedValue::GetString() const { - DCHECK_EQ(STRING_VALUE, type_); - return long_value_; -} - -bool ActivityUserData::TypedValue::GetBool() const { - DCHECK_EQ(BOOL_VALUE, type_); - return short_value_ != 0; -} - -char ActivityUserData::TypedValue::GetChar() const { - DCHECK_EQ(CHAR_VALUE, type_); - return static_cast(short_value_); -} - -int64_t ActivityUserData::TypedValue::GetInt() const { - DCHECK_EQ(SIGNED_VALUE, type_); - return static_cast(short_value_); -} - -uint64_t ActivityUserData::TypedValue::GetUint() const { - DCHECK_EQ(UNSIGNED_VALUE, type_); - return static_cast(short_value_); -} - -StringPiece ActivityUserData::TypedValue::GetReference() const { - DCHECK_EQ(RAW_VALUE_REFERENCE, type_); - return ref_value_; -} - -StringPiece ActivityUserData::TypedValue::GetStringReference() const { - DCHECK_EQ(STRING_VALUE_REFERENCE, type_); - return ref_value_; -} - -// These are required because std::atomic is (currently) not a POD type and -// thus clang requires explicit out-of-line constructors and destructors even -// when they do nothing. -ActivityUserData::ValueInfo::ValueInfo() = default; -ActivityUserData::ValueInfo::ValueInfo(ValueInfo&&) = default; -ActivityUserData::ValueInfo::~ValueInfo() = default; -ActivityUserData::MemoryHeader::MemoryHeader() = default; -ActivityUserData::MemoryHeader::~MemoryHeader() = default; -ActivityUserData::FieldHeader::FieldHeader() = default; -ActivityUserData::FieldHeader::~FieldHeader() = default; - -ActivityUserData::ActivityUserData() : ActivityUserData(nullptr, 0, -1) {} - -ActivityUserData::ActivityUserData(void* memory, size_t size, int64_t pid) - : memory_(reinterpret_cast(memory)), - available_(RoundDownToAlignment(size, kMemoryAlignment)), - header_(reinterpret_cast(memory)), - orig_data_id(0), - orig_process_id(0), - orig_create_stamp(0) { - // It's possible that no user data is being stored. - if (!memory_) - return; - - static_assert(0 == sizeof(MemoryHeader) % kMemoryAlignment, "invalid header"); - DCHECK_LT(sizeof(MemoryHeader), available_); - if (header_->owner.data_id.load(std::memory_order_acquire) == 0) - header_->owner.Release_Initialize(pid); - memory_ += sizeof(MemoryHeader); - available_ -= sizeof(MemoryHeader); - - // Make a copy of identifying information for later comparison. - *const_cast(&orig_data_id) = - header_->owner.data_id.load(std::memory_order_acquire); - *const_cast(&orig_process_id) = header_->owner.process_id; - *const_cast(&orig_create_stamp) = header_->owner.create_stamp; - - // If there is already data present, load that. This allows the same class - // to be used for analysis through snapshots. - ImportExistingData(); -} - -ActivityUserData::~ActivityUserData() = default; - -bool ActivityUserData::CreateSnapshot(Snapshot* output_snapshot) const { - DCHECK(output_snapshot); - DCHECK(output_snapshot->empty()); - - // Find any new data that may have been added by an active instance of this - // class that is adding records. - ImportExistingData(); - - // Add all the values to the snapshot. - for (const auto& entry : values_) { - TypedValue value; - const size_t size = entry.second.size_ptr->load(std::memory_order_acquire); - value.type_ = entry.second.type; - DCHECK_GE(entry.second.extent, size); - - switch (entry.second.type) { - case RAW_VALUE: - case STRING_VALUE: - value.long_value_ = - std::string(reinterpret_cast(entry.second.memory), size); - break; - case RAW_VALUE_REFERENCE: - case STRING_VALUE_REFERENCE: { - ReferenceRecord* ref = - reinterpret_cast(entry.second.memory); - value.ref_value_ = StringPiece( - reinterpret_cast(static_cast(ref->address)), - static_cast(ref->size)); - } break; - case BOOL_VALUE: - case CHAR_VALUE: - value.short_value_ = *reinterpret_cast(entry.second.memory); - break; - case SIGNED_VALUE: - case UNSIGNED_VALUE: - value.short_value_ = *reinterpret_cast(entry.second.memory); - break; - case END_OF_VALUES: // Included for completeness purposes. - NOTREACHED(); - } - auto inserted = output_snapshot->insert( - std::make_pair(entry.second.name.as_string(), std::move(value))); - DCHECK(inserted.second); // True if inserted, false if existed. - } - - // Another import attempt will validate that the underlying memory has not - // been reused for another purpose. Entries added since the first import - // will be ignored here but will be returned if another snapshot is created. - ImportExistingData(); - if (!memory_) { - output_snapshot->clear(); - return false; - } - - // Successful snapshot. - return true; -} - -const void* ActivityUserData::GetBaseAddress() const { - // The |memory_| pointer advances as elements are written but the |header_| - // value is always at the start of the block so just return that. - return header_; -} - -void ActivityUserData::SetOwningProcessIdForTesting(int64_t pid, - int64_t stamp) { - if (!header_) - return; - header_->owner.SetOwningProcessIdForTesting(pid, stamp); -} - -// static -bool ActivityUserData::GetOwningProcessId(const void* memory, - int64_t* out_id, - int64_t* out_stamp) { - const MemoryHeader* header = reinterpret_cast(memory); - return OwningProcess::GetOwningProcessId(&header->owner, out_id, out_stamp); -} - -void ActivityUserData::Set(StringPiece name, - ValueType type, - const void* memory, - size_t size) { - DCHECK_GE(std::numeric_limits::max(), name.length()); - size = std::min(std::numeric_limits::max() - (kMemoryAlignment - 1), - size); - - // It's possible that no user data is being stored. - if (!memory_) - return; - - // The storage of a name is limited so use that limit during lookup. - if (name.length() > kMaxUserDataNameLength) - name.set(name.data(), kMaxUserDataNameLength); - - ValueInfo* info; - auto existing = values_.find(name); - if (existing != values_.end()) { - info = &existing->second; - } else { - // The name size is limited to what can be held in a single byte but - // because there are not alignment constraints on strings, it's set tight - // against the header. Its extent (the reserved space, even if it's not - // all used) is calculated so that, when pressed against the header, the - // following field will be aligned properly. - size_t name_size = name.length(); - size_t name_extent = - RoundUpToAlignment(sizeof(FieldHeader) + name_size, kMemoryAlignment) - - sizeof(FieldHeader); - size_t value_extent = RoundUpToAlignment(size, kMemoryAlignment); - - // The "base size" is the size of the header and (padded) string key. Stop - // now if there's not room enough for even this. - size_t base_size = sizeof(FieldHeader) + name_extent; - if (base_size > available_) - return; - - // The "full size" is the size for storing the entire value. - size_t full_size = std::min(base_size + value_extent, available_); - - // If the value is actually a single byte, see if it can be stuffed at the - // end of the name extent rather than wasting kMemoryAlignment bytes. - if (size == 1 && name_extent > name_size) { - full_size = base_size; - --name_extent; - --base_size; - } - - // Truncate the stored size to the amount of available memory. Stop now if - // there's not any room for even part of the value. - if (size != 0) { - size = std::min(full_size - base_size, size); - if (size == 0) - return; - } - - // Allocate a chunk of memory. - FieldHeader* header = reinterpret_cast(memory_); - memory_ += full_size; - available_ -= full_size; - - // Datafill the header and name records. Memory must be zeroed. The |type| - // is written last, atomically, to release all the other values. - DCHECK_EQ(END_OF_VALUES, header->type.load(std::memory_order_relaxed)); - DCHECK_EQ(0, header->value_size.load(std::memory_order_relaxed)); - header->name_size = static_cast(name_size); - header->record_size = full_size; - char* name_memory = reinterpret_cast(header) + sizeof(FieldHeader); - void* value_memory = - reinterpret_cast(header) + sizeof(FieldHeader) + name_extent; - memcpy(name_memory, name.data(), name_size); - header->type.store(type, std::memory_order_release); - - // Create an entry in |values_| so that this field can be found and changed - // later on without having to allocate new entries. - StringPiece persistent_name(name_memory, name_size); - auto inserted = - values_.insert(std::make_pair(persistent_name, ValueInfo())); - DCHECK(inserted.second); // True if inserted, false if existed. - info = &inserted.first->second; - info->name = persistent_name; - info->memory = value_memory; - info->size_ptr = &header->value_size; - info->extent = full_size - sizeof(FieldHeader) - name_extent; - info->type = type; - } - - // Copy the value data to storage. The |size| is written last, atomically, to - // release the copied data. Until then, a parallel reader will just ignore - // records with a zero size. - DCHECK_EQ(type, info->type); - size = std::min(size, info->extent); - info->size_ptr->store(0, std::memory_order_seq_cst); - memcpy(info->memory, memory, size); - info->size_ptr->store(size, std::memory_order_release); -} - -void ActivityUserData::SetReference(StringPiece name, - ValueType type, - const void* memory, - size_t size) { - ReferenceRecord rec; - rec.address = reinterpret_cast(memory); - rec.size = size; - Set(name, type, &rec, sizeof(rec)); -} - -void ActivityUserData::ImportExistingData() const { - // It's possible that no user data is being stored. - if (!memory_) - return; - - while (available_ > sizeof(FieldHeader)) { - FieldHeader* header = reinterpret_cast(memory_); - ValueType type = - static_cast(header->type.load(std::memory_order_acquire)); - if (type == END_OF_VALUES) - return; - if (header->record_size > available_) - return; - - size_t value_offset = RoundUpToAlignment( - sizeof(FieldHeader) + header->name_size, kMemoryAlignment); - if (header->record_size == value_offset && - header->value_size.load(std::memory_order_relaxed) == 1) { - value_offset -= 1; - } - if (value_offset + header->value_size > header->record_size) - return; - - ValueInfo info; - info.name = StringPiece(memory_ + sizeof(FieldHeader), header->name_size); - info.type = type; - info.memory = memory_ + value_offset; - info.size_ptr = &header->value_size; - info.extent = header->record_size - value_offset; - - StringPiece key(info.name); - values_.insert(std::make_pair(key, std::move(info))); - - memory_ += header->record_size; - available_ -= header->record_size; - } - - // Check if memory has been completely reused. - if (header_->owner.data_id.load(std::memory_order_acquire) != orig_data_id || - header_->owner.process_id != orig_process_id || - header_->owner.create_stamp != orig_create_stamp) { - memory_ = nullptr; - values_.clear(); - } -} - -// This information is kept for every thread that is tracked. It is filled -// the very first time the thread is seen. All fields must be of exact sizes -// so there is no issue moving between 32 and 64-bit builds. -struct ThreadActivityTracker::Header { - // Defined in .h for analyzer access. Increment this if structure changes! - static constexpr uint32_t kPersistentTypeId = - GlobalActivityTracker::kTypeIdActivityTracker; - - // Expected size for 32/64-bit check. - static constexpr size_t kExpectedInstanceSize = - OwningProcess::kExpectedInstanceSize + Activity::kExpectedInstanceSize + - 72; - - // This information uniquely identifies a process. - OwningProcess owner; - - // The thread-id (thread_ref.as_id) to which this data belongs. This number - // is not guaranteed to mean anything but combined with the process-id from - // OwningProcess is unique among all active trackers. - ThreadRef thread_ref; - - // The start-time and start-ticks when the data was created. Each activity - // record has a |time_internal| value that can be converted to a "wall time" - // with these two values. - int64_t start_time; - int64_t start_ticks; - - // The number of Activity slots (spaces that can hold an Activity) that - // immediately follow this structure in memory. - uint32_t stack_slots; - - // Some padding to keep everything 64-bit aligned. - uint32_t padding; - - // The current depth of the stack. This may be greater than the number of - // slots. If the depth exceeds the number of slots, the newest entries - // won't be recorded. - std::atomic current_depth; - - // A memory location used to indicate if changes have been made to the data - // that would invalidate an in-progress read of its contents. The active - // tracker will increment the value whenever something gets popped from the - // stack. A monitoring tracker can check the value before and after access - // to know, if it's still the same, that the contents didn't change while - // being copied. - std::atomic data_version; - - // The last "exception" activity. This can't be stored on the stack because - // that could get popped as things unwind. - Activity last_exception; - - // The name of the thread (up to a maximum length). Dynamic-length names - // are not practical since the memory has to come from the same persistent - // allocator that holds this structure and to which this object has no - // reference. - char thread_name[32]; -}; - -ThreadActivityTracker::Snapshot::Snapshot() = default; -ThreadActivityTracker::Snapshot::~Snapshot() = default; - -ThreadActivityTracker::ScopedActivity::ScopedActivity( - ThreadActivityTracker* tracker, - const void* program_counter, - const void* origin, - Activity::Type type, - const ActivityData& data) - : tracker_(tracker) { - if (tracker_) - activity_id_ = tracker_->PushActivity(program_counter, origin, type, data); -} - -ThreadActivityTracker::ScopedActivity::~ScopedActivity() { - if (tracker_) - tracker_->PopActivity(activity_id_); -} - -void ThreadActivityTracker::ScopedActivity::ChangeTypeAndData( - Activity::Type type, - const ActivityData& data) { - if (tracker_) - tracker_->ChangeActivity(activity_id_, type, data); -} - -ThreadActivityTracker::ThreadActivityTracker(void* base, size_t size) - : header_(static_cast(base)), - stack_(reinterpret_cast(reinterpret_cast(base) + - sizeof(Header))), -#if DCHECK_IS_ON() - thread_id_(PlatformThreadRef()), -#endif - stack_slots_( - static_cast((size - sizeof(Header)) / sizeof(Activity))) { - - // Verify the parameters but fail gracefully if they're not valid so that - // production code based on external inputs will not crash. IsValid() will - // return false in this case. - if (!base || - // Ensure there is enough space for the header and at least a few records. - size < sizeof(Header) + kMinStackDepth * sizeof(Activity) || - // Ensure that the |stack_slots_| calculation didn't overflow. - (size - sizeof(Header)) / sizeof(Activity) > - std::numeric_limits::max()) { - NOTREACHED(); - return; - } - - // Ensure that the thread reference doesn't exceed the size of the ID number. - // This won't compile at the global scope because Header is a private struct. - static_assert( - sizeof(header_->thread_ref) == sizeof(header_->thread_ref.as_id), - "PlatformThreadHandle::Handle is too big to hold in 64-bit ID"); - - // Ensure that the alignment of Activity.data is properly aligned to a - // 64-bit boundary so there are no interoperability-issues across cpu - // architectures. - static_assert(offsetof(Activity, data) % sizeof(uint64_t) == 0, - "ActivityData.data is not 64-bit aligned"); - - // Provided memory should either be completely initialized or all zeros. - if (header_->owner.data_id.load(std::memory_order_relaxed) == 0) { - // This is a new file. Double-check other fields and then initialize. - DCHECK_EQ(0, header_->owner.process_id); - DCHECK_EQ(0, header_->owner.create_stamp); - DCHECK_EQ(0, header_->thread_ref.as_id); - DCHECK_EQ(0, header_->start_time); - DCHECK_EQ(0, header_->start_ticks); - DCHECK_EQ(0U, header_->stack_slots); - DCHECK_EQ(0U, header_->current_depth.load(std::memory_order_relaxed)); - DCHECK_EQ(0U, header_->data_version.load(std::memory_order_relaxed)); - DCHECK_EQ(0, stack_[0].time_internal); - DCHECK_EQ(0U, stack_[0].origin_address); - DCHECK_EQ(0U, stack_[0].call_stack[0]); - DCHECK_EQ(0U, stack_[0].data.task.sequence_id); - -#if defined(OS_WIN) - header_->thread_ref.as_tid = PlatformThread::CurrentId(); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - header_->thread_ref.as_handle = - PlatformThread::CurrentHandle().platform_handle(); -#endif - - header_->start_time = base::Time::Now().ToInternalValue(); - header_->start_ticks = base::TimeTicks::Now().ToInternalValue(); - header_->stack_slots = stack_slots_; - strlcpy(header_->thread_name, PlatformThread::GetName(), - sizeof(header_->thread_name)); - - // This is done last so as to guarantee that everything above is "released" - // by the time this value gets written. - header_->owner.Release_Initialize(); - - valid_ = true; - DCHECK(IsValid()); - } else { - // This is a file with existing data. Perform basic consistency checks. - valid_ = true; - valid_ = IsValid(); - } -} - -ThreadActivityTracker::~ThreadActivityTracker() = default; - -ThreadActivityTracker::ActivityId ThreadActivityTracker::PushActivity( - const void* program_counter, - const void* origin, - Activity::Type type, - const ActivityData& data) { - // A thread-checker creates a lock to check the thread-id which means - // re-entry into this code if lock acquisitions are being tracked. - DCHECK(type == Activity::ACT_LOCK_ACQUIRE || CalledOnValidThread()); - - // Get the current depth of the stack. No access to other memory guarded - // by this variable is done here so a "relaxed" load is acceptable. - uint32_t depth = header_->current_depth.load(std::memory_order_relaxed); - - // Handle the case where the stack depth has exceeded the storage capacity. - // Extra entries will be lost leaving only the base of the stack. - if (depth >= stack_slots_) { - // Since no other threads modify the data, no compare/exchange is needed. - // Since no other memory is being modified, a "relaxed" store is acceptable. - header_->current_depth.store(depth + 1, std::memory_order_relaxed); - return depth; - } - - // Get a pointer to the next activity and load it. No atomicity is required - // here because the memory is known only to this thread. It will be made - // known to other threads once the depth is incremented. - Activity::FillFrom(&stack_[depth], program_counter, origin, type, data); - - // Save the incremented depth. Because this guards |activity| memory filled - // above that may be read by another thread once the recorded depth changes, - // a "release" store is required. - header_->current_depth.store(depth + 1, std::memory_order_release); - - // The current depth is used as the activity ID because it simply identifies - // an entry. Once an entry is pop'd, it's okay to reuse the ID. - return depth; -} - -void ThreadActivityTracker::ChangeActivity(ActivityId id, - Activity::Type type, - const ActivityData& data) { - DCHECK(CalledOnValidThread()); - DCHECK(type != Activity::ACT_NULL || &data != &kNullActivityData); - DCHECK_LT(id, header_->current_depth.load(std::memory_order_acquire)); - - // Update the information if it is being recorded (i.e. within slot limit). - if (id < stack_slots_) { - Activity* activity = &stack_[id]; - - if (type != Activity::ACT_NULL) { - DCHECK_EQ(activity->activity_type & Activity::ACT_CATEGORY_MASK, - type & Activity::ACT_CATEGORY_MASK); - activity->activity_type = type; - } - - if (&data != &kNullActivityData) - activity->data = data; - } -} - -void ThreadActivityTracker::PopActivity(ActivityId id) { - // Do an atomic decrement of the depth. No changes to stack entries guarded - // by this variable are done here so a "relaxed" operation is acceptable. - // |depth| will receive the value BEFORE it was modified which means the - // return value must also be decremented. The slot will be "free" after - // this call but since only a single thread can access this object, the - // data will remain valid until this method returns or calls outside. - uint32_t depth = - header_->current_depth.fetch_sub(1, std::memory_order_relaxed) - 1; - - // Validate that everything is running correctly. - DCHECK_EQ(id, depth); - - // A thread-checker creates a lock to check the thread-id which means - // re-entry into this code if lock acquisitions are being tracked. - DCHECK(stack_[depth].activity_type == Activity::ACT_LOCK_ACQUIRE || - CalledOnValidThread()); - - // The stack has shrunk meaning that some other thread trying to copy the - // contents for reporting purposes could get bad data. Increment the data - // version so that it con tell that things have changed. This needs to - // happen after the atomic |depth| operation above so a "release" store - // is required. - header_->data_version.fetch_add(1, std::memory_order_release); -} - -std::unique_ptr ThreadActivityTracker::GetUserData( - ActivityId id, - ActivityTrackerMemoryAllocator* allocator) { - // Don't allow user data for lock acquisition as recursion may occur. - if (stack_[id].activity_type == Activity::ACT_LOCK_ACQUIRE) { - NOTREACHED(); - return std::make_unique(); - } - - // User-data is only stored for activities actually held in the stack. - if (id >= stack_slots_) - return std::make_unique(); - - // Create and return a real UserData object. - return CreateUserDataForActivity(&stack_[id], allocator); -} - -bool ThreadActivityTracker::HasUserData(ActivityId id) { - // User-data is only stored for activities actually held in the stack. - return (id < stack_slots_ && stack_[id].user_data_ref); -} - -void ThreadActivityTracker::ReleaseUserData( - ActivityId id, - ActivityTrackerMemoryAllocator* allocator) { - // User-data is only stored for activities actually held in the stack. - if (id < stack_slots_ && stack_[id].user_data_ref) { - allocator->ReleaseObjectReference(stack_[id].user_data_ref); - stack_[id].user_data_ref = 0; - } -} - -void ThreadActivityTracker::RecordExceptionActivity(const void* program_counter, - const void* origin, - Activity::Type type, - const ActivityData& data) { - // A thread-checker creates a lock to check the thread-id which means - // re-entry into this code if lock acquisitions are being tracked. - DCHECK(CalledOnValidThread()); - - // Fill the reusable exception activity. - Activity::FillFrom(&header_->last_exception, program_counter, origin, type, - data); - - // The data has changed meaning that some other thread trying to copy the - // contents for reporting purposes could get bad data. - header_->data_version.fetch_add(1, std::memory_order_relaxed); -} - -bool ThreadActivityTracker::IsValid() const { - if (header_->owner.data_id.load(std::memory_order_acquire) == 0 || - header_->owner.process_id == 0 || header_->thread_ref.as_id == 0 || - header_->start_time == 0 || header_->start_ticks == 0 || - header_->stack_slots != stack_slots_ || - header_->thread_name[sizeof(header_->thread_name) - 1] != '\0') { - return false; - } - - return valid_; -} - -bool ThreadActivityTracker::CreateSnapshot(Snapshot* output_snapshot) const { - DCHECK(output_snapshot); - - // There is no "called on valid thread" check for this method as it can be - // called from other threads or even other processes. It is also the reason - // why atomic operations must be used in certain places above. - - // It's possible for the data to change while reading it in such a way that it - // invalidates the read. Make several attempts but don't try forever. - const int kMaxAttempts = 10; - uint32_t depth; - - // Stop here if the data isn't valid. - if (!IsValid()) - return false; - - // Allocate the maximum size for the stack so it doesn't have to be done - // during the time-sensitive snapshot operation. It is shrunk once the - // actual size is known. - output_snapshot->activity_stack.reserve(stack_slots_); - - for (int attempt = 0; attempt < kMaxAttempts; ++attempt) { - // Remember the data IDs to ensure nothing is replaced during the snapshot - // operation. Use "acquire" so that all the non-atomic fields of the - // structure are valid (at least at the current moment in time). - const uint32_t starting_id = - header_->owner.data_id.load(std::memory_order_acquire); - const int64_t starting_create_stamp = header_->owner.create_stamp; - const int64_t starting_process_id = header_->owner.process_id; - const int64_t starting_thread_id = header_->thread_ref.as_id; - - // Note the current |data_version| so it's possible to detect at the end - // that nothing has changed since copying the data began. A "cst" operation - // is required to ensure it occurs before everything else. Using "cst" - // memory ordering is relatively expensive but this is only done during - // analysis so doesn't directly affect the worker threads. - const uint32_t pre_version = - header_->data_version.load(std::memory_order_seq_cst); - - // Fetching the current depth also "acquires" the contents of the stack. - depth = header_->current_depth.load(std::memory_order_acquire); - uint32_t count = std::min(depth, stack_slots_); - output_snapshot->activity_stack.resize(count); - if (count > 0) { - // Copy the existing contents. Memcpy is used for speed. - memcpy(&output_snapshot->activity_stack[0], stack_, - count * sizeof(Activity)); - } - - // Capture the last exception. - memcpy(&output_snapshot->last_exception, &header_->last_exception, - sizeof(Activity)); - - // TODO(bcwhite): Snapshot other things here. - - // Retry if something changed during the copy. A "cst" operation ensures - // it must happen after all the above operations. - if (header_->data_version.load(std::memory_order_seq_cst) != pre_version) - continue; - - // Stack copied. Record it's full depth. - output_snapshot->activity_stack_depth = depth; - - // Get the general thread information. - output_snapshot->thread_name = - std::string(header_->thread_name, sizeof(header_->thread_name) - 1); - output_snapshot->create_stamp = header_->owner.create_stamp; - output_snapshot->thread_id = header_->thread_ref.as_id; - output_snapshot->process_id = header_->owner.process_id; - - // All characters of the thread-name buffer were copied so as to not break - // if the trailing NUL were missing. Now limit the length if the actual - // name is shorter. - output_snapshot->thread_name.resize( - strlen(output_snapshot->thread_name.c_str())); - - // If the data ID has changed then the tracker has exited and the memory - // reused by a new one. Try again. - if (header_->owner.data_id.load(std::memory_order_seq_cst) != starting_id || - output_snapshot->create_stamp != starting_create_stamp || - output_snapshot->process_id != starting_process_id || - output_snapshot->thread_id != starting_thread_id) { - continue; - } - - // Only successful if the data is still valid once everything is done since - // it's possible for the thread to end somewhere in the middle and all its - // values become garbage. - if (!IsValid()) - return false; - - // Change all the timestamps in the activities from "ticks" to "wall" time. - const Time start_time = Time::FromInternalValue(header_->start_time); - const int64_t start_ticks = header_->start_ticks; - for (Activity& activity : output_snapshot->activity_stack) { - activity.time_internal = - WallTimeFromTickTime(start_ticks, activity.time_internal, start_time) - .ToInternalValue(); - } - output_snapshot->last_exception.time_internal = - WallTimeFromTickTime(start_ticks, - output_snapshot->last_exception.time_internal, - start_time) - .ToInternalValue(); - - // Success! - return true; - } - - // Too many attempts. - return false; -} - -const void* ThreadActivityTracker::GetBaseAddress() { - return header_; -} - -uint32_t ThreadActivityTracker::GetDataVersionForTesting() { - return header_->data_version.load(std::memory_order_relaxed); -} - -void ThreadActivityTracker::SetOwningProcessIdForTesting(int64_t pid, - int64_t stamp) { - header_->owner.SetOwningProcessIdForTesting(pid, stamp); -} - -// static -bool ThreadActivityTracker::GetOwningProcessId(const void* memory, - int64_t* out_id, - int64_t* out_stamp) { - const Header* header = reinterpret_cast(memory); - return OwningProcess::GetOwningProcessId(&header->owner, out_id, out_stamp); -} - -// static -size_t ThreadActivityTracker::SizeForStackDepth(int stack_depth) { - return static_cast(stack_depth) * sizeof(Activity) + sizeof(Header); -} - -bool ThreadActivityTracker::CalledOnValidThread() { -#if DCHECK_IS_ON() - return thread_id_ == PlatformThreadRef(); -#else - return true; -#endif -} - -std::unique_ptr -ThreadActivityTracker::CreateUserDataForActivity( - Activity* activity, - ActivityTrackerMemoryAllocator* allocator) { - DCHECK_EQ(0U, activity->user_data_ref); - - PersistentMemoryAllocator::Reference ref = allocator->GetObjectReference(); - void* memory = allocator->GetAsArray(ref, kUserDataSize); - if (memory) { - std::unique_ptr user_data = - std::make_unique(memory, kUserDataSize); - activity->user_data_ref = ref; - activity->user_data_id = user_data->id(); - return user_data; - } - - // Return a dummy object that will still accept (but ignore) Set() calls. - return std::make_unique(); -} - -// The instantiation of the GlobalActivityTracker object. -// The object held here will obviously not be destructed at process exit -// but that's best since PersistentMemoryAllocator objects (that underlie -// GlobalActivityTracker objects) are explicitly forbidden from doing anything -// essential at exit anyway due to the fact that they depend on data managed -// elsewhere and which could be destructed first. An AtomicWord is used instead -// of std::atomic because the latter can create global ctors and dtors. -subtle::AtomicWord GlobalActivityTracker::g_tracker_ = 0; - -GlobalActivityTracker::ModuleInfo::ModuleInfo() = default; -GlobalActivityTracker::ModuleInfo::ModuleInfo(ModuleInfo&& rhs) = default; -GlobalActivityTracker::ModuleInfo::ModuleInfo(const ModuleInfo& rhs) = default; -GlobalActivityTracker::ModuleInfo::~ModuleInfo() = default; - -GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=( - ModuleInfo&& rhs) = default; -GlobalActivityTracker::ModuleInfo& GlobalActivityTracker::ModuleInfo::operator=( - const ModuleInfo& rhs) = default; - -GlobalActivityTracker::ModuleInfoRecord::ModuleInfoRecord() = default; -GlobalActivityTracker::ModuleInfoRecord::~ModuleInfoRecord() = default; - -bool GlobalActivityTracker::ModuleInfoRecord::DecodeTo( - GlobalActivityTracker::ModuleInfo* info, - size_t record_size) const { - // Get the current "changes" indicator, acquiring all the other values. - uint32_t current_changes = changes.load(std::memory_order_acquire); - - // Copy out the dynamic information. - info->is_loaded = loaded != 0; - info->address = static_cast(address); - info->load_time = load_time; - - // Check to make sure no information changed while being read. A "seq-cst" - // operation is expensive but is only done during analysis and it's the only - // way to ensure this occurs after all the accesses above. If changes did - // occur then return a "not loaded" result so that |size| and |address| - // aren't expected to be accurate. - if ((current_changes & kModuleInformationChanging) != 0 || - changes.load(std::memory_order_seq_cst) != current_changes) { - info->is_loaded = false; - } - - // Copy out the static information. These never change so don't have to be - // protected by the atomic |current_changes| operations. - info->size = static_cast(size); - info->timestamp = timestamp; - info->age = age; - memcpy(info->identifier, identifier, sizeof(info->identifier)); - - if (offsetof(ModuleInfoRecord, pickle) + pickle_size > record_size) - return false; - Pickle pickler(pickle, pickle_size); - PickleIterator iter(pickler); - return iter.ReadString(&info->file) && iter.ReadString(&info->debug_file); -} - -GlobalActivityTracker::ModuleInfoRecord* -GlobalActivityTracker::ModuleInfoRecord::CreateFrom( - const GlobalActivityTracker::ModuleInfo& info, - PersistentMemoryAllocator* allocator) { - Pickle pickler; - pickler.WriteString(info.file); - pickler.WriteString(info.debug_file); - size_t required_size = offsetof(ModuleInfoRecord, pickle) + pickler.size(); - ModuleInfoRecord* record = allocator->New(required_size); - if (!record) - return nullptr; - - // These fields never changes and are done before the record is made - // iterable so no thread protection is necessary. - record->size = info.size; - record->timestamp = info.timestamp; - record->age = info.age; - memcpy(record->identifier, info.identifier, sizeof(identifier)); - memcpy(record->pickle, pickler.data(), pickler.size()); - record->pickle_size = pickler.size(); - record->changes.store(0, std::memory_order_relaxed); - - // Initialize the owner info. - record->owner.Release_Initialize(); - - // Now set those fields that can change. - bool success = record->UpdateFrom(info); - DCHECK(success); - return record; -} - -bool GlobalActivityTracker::ModuleInfoRecord::UpdateFrom( - const GlobalActivityTracker::ModuleInfo& info) { - // Updates can occur after the record is made visible so make changes atomic. - // A "strong" exchange ensures no false failures. - uint32_t old_changes = changes.load(std::memory_order_relaxed); - uint32_t new_changes = old_changes | kModuleInformationChanging; - if ((old_changes & kModuleInformationChanging) != 0 || - !changes.compare_exchange_strong(old_changes, new_changes, - std::memory_order_acquire, - std::memory_order_acquire)) { - NOTREACHED() << "Multiple sources are updating module information."; - return false; - } - - loaded = info.is_loaded ? 1 : 0; - address = info.address; - load_time = Time::Now().ToInternalValue(); - - bool success = changes.compare_exchange_strong(new_changes, old_changes + 1, - std::memory_order_release, - std::memory_order_relaxed); - DCHECK(success); - return true; -} - -GlobalActivityTracker::ScopedThreadActivity::ScopedThreadActivity( - const void* program_counter, - const void* origin, - Activity::Type type, - const ActivityData& data, - bool lock_allowed) - : ThreadActivityTracker::ScopedActivity(GetOrCreateTracker(lock_allowed), - program_counter, - origin, - type, - data) {} - -GlobalActivityTracker::ScopedThreadActivity::~ScopedThreadActivity() { - if (tracker_ && tracker_->HasUserData(activity_id_)) { - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - AutoLock lock(global->user_data_allocator_lock_); - tracker_->ReleaseUserData(activity_id_, &global->user_data_allocator_); - } -} - -ActivityUserData& GlobalActivityTracker::ScopedThreadActivity::user_data() { - if (!user_data_) { - if (tracker_) { - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - AutoLock lock(global->user_data_allocator_lock_); - user_data_ = - tracker_->GetUserData(activity_id_, &global->user_data_allocator_); - } else { - user_data_ = std::make_unique(); - } - } - return *user_data_; -} - -GlobalActivityTracker::ThreadSafeUserData::ThreadSafeUserData(void* memory, - size_t size, - int64_t pid) - : ActivityUserData(memory, size, pid) {} - -GlobalActivityTracker::ThreadSafeUserData::~ThreadSafeUserData() = default; - -void GlobalActivityTracker::ThreadSafeUserData::Set(StringPiece name, - ValueType type, - const void* memory, - size_t size) { - AutoLock lock(data_lock_); - ActivityUserData::Set(name, type, memory, size); -} - -GlobalActivityTracker::ManagedActivityTracker::ManagedActivityTracker( - PersistentMemoryAllocator::Reference mem_reference, - void* base, - size_t size) - : ThreadActivityTracker(base, size), - mem_reference_(mem_reference), - mem_base_(base) {} - -GlobalActivityTracker::ManagedActivityTracker::~ManagedActivityTracker() { - // The global |g_tracker_| must point to the owner of this class since all - // objects of this type must be destructed before |g_tracker_| can be changed - // (something that only occurs in tests). - DCHECK(g_tracker_); - GlobalActivityTracker::Get()->ReturnTrackerMemory(this); -} - -void GlobalActivityTracker::CreateWithAllocator( - std::unique_ptr allocator, - int stack_depth, - int64_t process_id) { - // There's no need to do anything with the result. It is self-managing. - GlobalActivityTracker* global_tracker = - new GlobalActivityTracker(std::move(allocator), stack_depth, process_id); - // Create a tracker for this thread since it is known. - global_tracker->CreateTrackerForCurrentThread(); -} - -#if !defined(OS_NACL) -// static -bool GlobalActivityTracker::CreateWithFile(const FilePath& file_path, - size_t size, - uint64_t id, - StringPiece name, - int stack_depth) { - DCHECK(!file_path.empty()); - DCHECK_GE(static_cast(std::numeric_limits::max()), size); - - // Create and map the file into memory and make it globally available. - std::unique_ptr mapped_file(new MemoryMappedFile()); - bool success = mapped_file->Initialize( - File(file_path, File::FLAG_CREATE_ALWAYS | File::FLAG_READ | - File::FLAG_WRITE | File::FLAG_SHARE_DELETE), - {0, size}, MemoryMappedFile::READ_WRITE_EXTEND); - if (!success) - return false; - if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mapped_file, false)) - return false; - CreateWithAllocator(std::make_unique( - std::move(mapped_file), size, id, name, false), - stack_depth, 0); - return true; -} -#endif // !defined(OS_NACL) - -// static -bool GlobalActivityTracker::CreateWithLocalMemory(size_t size, - uint64_t id, - StringPiece name, - int stack_depth, - int64_t process_id) { - CreateWithAllocator( - std::make_unique(size, id, name), - stack_depth, process_id); - return true; -} - -// static -bool GlobalActivityTracker::CreateWithSharedMemory( - std::unique_ptr shm, - uint64_t id, - StringPiece name, - int stack_depth) { - if (shm->mapped_size() == 0 || - !SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) { - return false; - } - CreateWithAllocator(std::make_unique( - std::move(shm), id, name, false), - stack_depth, 0); - return true; -} - -// static -bool GlobalActivityTracker::CreateWithSharedMemoryHandle( - const SharedMemoryHandle& handle, - size_t size, - uint64_t id, - StringPiece name, - int stack_depth) { - std::unique_ptr shm( - new SharedMemory(handle, /*readonly=*/false)); - if (!shm->Map(size)) - return false; - return CreateWithSharedMemory(std::move(shm), id, name, stack_depth); -} - -// static -void GlobalActivityTracker::SetForTesting( - std::unique_ptr tracker) { - CHECK(!subtle::NoBarrier_Load(&g_tracker_)); - subtle::Release_Store(&g_tracker_, - reinterpret_cast(tracker.release())); -} - -// static -std::unique_ptr -GlobalActivityTracker::ReleaseForTesting() { - GlobalActivityTracker* tracker = Get(); - if (!tracker) - return nullptr; - - // Thread trackers assume that the global tracker is present for some - // operations so ensure that there aren't any. - tracker->ReleaseTrackerForCurrentThreadForTesting(); - DCHECK_EQ(0, tracker->thread_tracker_count_.load(std::memory_order_relaxed)); - - subtle::Release_Store(&g_tracker_, 0); - return WrapUnique(tracker); -} - -ThreadActivityTracker* GlobalActivityTracker::CreateTrackerForCurrentThread() { - DCHECK(!this_thread_tracker_.Get()); - - PersistentMemoryAllocator::Reference mem_reference; - - { - base::AutoLock autolock(thread_tracker_allocator_lock_); - mem_reference = thread_tracker_allocator_.GetObjectReference(); - } - - if (!mem_reference) { - // Failure. This shouldn't happen. But be graceful if it does, probably - // because the underlying allocator wasn't given enough memory to satisfy - // to all possible requests. - NOTREACHED(); - // Report the thread-count at which the allocator was full so that the - // failure can be seen and underlying memory resized appropriately. - UMA_HISTOGRAM_COUNTS_1000( - "ActivityTracker.ThreadTrackers.MemLimitTrackerCount", - thread_tracker_count_.load(std::memory_order_relaxed)); - // Return null, just as if tracking wasn't enabled. - return nullptr; - } - - // Convert the memory block found above into an actual memory address. - // Doing the conversion as a Header object enacts the 32/64-bit size - // consistency checks which would not otherwise be done. Unfortunately, - // some older compilers and MSVC don't have standard-conforming definitions - // of std::atomic which cause it not to be plain-old-data. Don't check on - // those platforms assuming that the checks on other platforms will be - // sufficient. - // TODO(bcwhite): Review this after major compiler releases. - DCHECK(mem_reference); - void* mem_base; - mem_base = - allocator_->GetAsObject(mem_reference); - - DCHECK(mem_base); - DCHECK_LE(stack_memory_size_, allocator_->GetAllocSize(mem_reference)); - - // Create a tracker with the acquired memory and set it as the tracker - // for this particular thread in thread-local-storage. - ManagedActivityTracker* tracker = - new ManagedActivityTracker(mem_reference, mem_base, stack_memory_size_); - DCHECK(tracker->IsValid()); - this_thread_tracker_.Set(tracker); - int old_count = thread_tracker_count_.fetch_add(1, std::memory_order_relaxed); - - UMA_HISTOGRAM_EXACT_LINEAR("ActivityTracker.ThreadTrackers.Count", - old_count + 1, static_cast(kMaxThreadCount)); - return tracker; -} - -void GlobalActivityTracker::ReleaseTrackerForCurrentThreadForTesting() { - ThreadActivityTracker* tracker = - reinterpret_cast(this_thread_tracker_.Get()); - if (tracker) { - this_thread_tracker_.Set(nullptr); - delete tracker; - } -} - -void GlobalActivityTracker::SetBackgroundTaskRunner( - const scoped_refptr& runner) { - AutoLock lock(global_tracker_lock_); - background_task_runner_ = runner; -} - -void GlobalActivityTracker::SetProcessExitCallback( - ProcessExitCallback callback) { - AutoLock lock(global_tracker_lock_); - process_exit_callback_ = callback; -} - -void GlobalActivityTracker::RecordProcessLaunch( - ProcessId process_id, - const FilePath::StringType& cmd) { - const int64_t pid = process_id; - DCHECK_NE(GetProcessId(), pid); - DCHECK_NE(0, pid); - - base::AutoLock lock(global_tracker_lock_); - if (base::ContainsKey(known_processes_, pid)) { - // TODO(bcwhite): Measure this in UMA. - NOTREACHED() << "Process #" << process_id - << " was previously recorded as \"launched\"" - << " with no corresponding exit.\n" - << known_processes_[pid]; - known_processes_.erase(pid); - } - -#if defined(OS_WIN) - known_processes_.insert(std::make_pair(pid, UTF16ToUTF8(cmd))); -#else - known_processes_.insert(std::make_pair(pid, cmd)); -#endif -} - -void GlobalActivityTracker::RecordProcessLaunch( - ProcessId process_id, - const FilePath::StringType& exe, - const FilePath::StringType& args) { - if (exe.find(FILE_PATH_LITERAL(" "))) { - RecordProcessLaunch(process_id, - FilePath::StringType(FILE_PATH_LITERAL("\"")) + exe + - FILE_PATH_LITERAL("\" ") + args); - } else { - RecordProcessLaunch(process_id, exe + FILE_PATH_LITERAL(' ') + args); - } -} - -void GlobalActivityTracker::RecordProcessExit(ProcessId process_id, - int exit_code) { - const int64_t pid = process_id; - DCHECK_NE(GetProcessId(), pid); - DCHECK_NE(0, pid); - - scoped_refptr task_runner; - std::string command_line; - { - base::AutoLock lock(global_tracker_lock_); - task_runner = background_task_runner_; - auto found = known_processes_.find(pid); - if (found != known_processes_.end()) { - command_line = std::move(found->second); - known_processes_.erase(found); - } else { - DLOG(ERROR) << "Recording exit of unknown process #" << process_id; - } - } - - // Use the current time to differentiate the process that just exited - // from any that might be created in the future with the same ID. - int64_t now_stamp = Time::Now().ToInternalValue(); - - // The persistent allocator is thread-safe so run the iteration and - // adjustments on a worker thread if one was provided. - if (task_runner && !task_runner->RunsTasksInCurrentSequence()) { - task_runner->PostTask( - FROM_HERE, - BindOnce(&GlobalActivityTracker::CleanupAfterProcess, Unretained(this), - pid, now_stamp, exit_code, std::move(command_line))); - return; - } - - CleanupAfterProcess(pid, now_stamp, exit_code, std::move(command_line)); -} - -void GlobalActivityTracker::SetProcessPhase(ProcessPhase phase) { - process_data().SetInt(kProcessPhaseDataKey, phase); -} - -void GlobalActivityTracker::CleanupAfterProcess(int64_t process_id, - int64_t exit_stamp, - int exit_code, - std::string&& command_line) { - // The process may not have exited cleanly so its necessary to go through - // all the data structures it may have allocated in the persistent memory - // segment and mark them as "released". This will allow them to be reused - // later on. - - PersistentMemoryAllocator::Iterator iter(allocator_.get()); - PersistentMemoryAllocator::Reference ref; - - ProcessExitCallback process_exit_callback; - { - AutoLock lock(global_tracker_lock_); - process_exit_callback = process_exit_callback_; - } - if (process_exit_callback) { - // Find the processes user-data record so the process phase can be passed - // to the callback. - ActivityUserData::Snapshot process_data_snapshot; - while ((ref = iter.GetNextOfType(kTypeIdProcessDataRecord)) != 0) { - const void* memory = allocator_->GetAsArray( - ref, kTypeIdProcessDataRecord, PersistentMemoryAllocator::kSizeAny); - if (!memory) - continue; - int64_t found_id; - int64_t create_stamp; - if (ActivityUserData::GetOwningProcessId(memory, &found_id, - &create_stamp)) { - if (found_id == process_id && create_stamp < exit_stamp) { - const ActivityUserData process_data(const_cast(memory), - allocator_->GetAllocSize(ref)); - process_data.CreateSnapshot(&process_data_snapshot); - break; // No need to look for any others. - } - } - } - iter.Reset(); // So it starts anew when used below. - - // Record the process's phase at exit so callback doesn't need to go - // searching based on a private key value. - ProcessPhase exit_phase = PROCESS_PHASE_UNKNOWN; - auto phase = process_data_snapshot.find(kProcessPhaseDataKey); - if (phase != process_data_snapshot.end()) - exit_phase = static_cast(phase->second.GetInt()); - - // Perform the callback. - process_exit_callback.Run(process_id, exit_stamp, exit_code, exit_phase, - std::move(command_line), - std::move(process_data_snapshot)); - } - - // Find all allocations associated with the exited process and free them. - uint32_t type; - while ((ref = iter.GetNext(&type)) != 0) { - switch (type) { - case kTypeIdActivityTracker: - case kTypeIdUserDataRecord: - case kTypeIdProcessDataRecord: - case ModuleInfoRecord::kPersistentTypeId: { - const void* memory = allocator_->GetAsArray( - ref, type, PersistentMemoryAllocator::kSizeAny); - if (!memory) - continue; - int64_t found_id; - int64_t create_stamp; - - // By convention, the OwningProcess structure is always the first - // field of the structure so there's no need to handle all the - // cases separately. - if (OwningProcess::GetOwningProcessId(memory, &found_id, - &create_stamp)) { - // Only change the type to be "free" if the process ID matches and - // the creation time is before the exit time (so PID re-use doesn't - // cause the erasure of something that is in-use). Memory is cleared - // here, rather than when it's needed, so as to limit the impact at - // that critical time. - if (found_id == process_id && create_stamp < exit_stamp) - allocator_->ChangeType(ref, ~type, type, /*clear=*/true); - } - } break; - } - } -} - -void GlobalActivityTracker::RecordLogMessage(StringPiece message) { - // Allocate at least one extra byte so the string is NUL terminated. All - // memory returned by the allocator is guaranteed to be zeroed. - PersistentMemoryAllocator::Reference ref = - allocator_->Allocate(message.size() + 1, kTypeIdGlobalLogMessage); - char* memory = allocator_->GetAsArray(ref, kTypeIdGlobalLogMessage, - message.size() + 1); - if (memory) { - memcpy(memory, message.data(), message.size()); - allocator_->MakeIterable(ref); - } -} - -void GlobalActivityTracker::RecordModuleInfo(const ModuleInfo& info) { - AutoLock lock(modules_lock_); - auto found = modules_.find(info.file); - if (found != modules_.end()) { - ModuleInfoRecord* record = found->second; - DCHECK(record); - - // Update the basic state of module information that has been already - // recorded. It is assumed that the string information (identifier, - // version, etc.) remain unchanged which means that there's no need - // to create a new record to accommodate a possibly longer length. - record->UpdateFrom(info); - return; - } - - ModuleInfoRecord* record = - ModuleInfoRecord::CreateFrom(info, allocator_.get()); - if (!record) - return; - allocator_->MakeIterable(record); - modules_.emplace(info.file, record); -} - -void GlobalActivityTracker::RecordFieldTrial(const std::string& trial_name, - StringPiece group_name) { - const std::string key = std::string("FieldTrial.") + trial_name; - process_data_.SetString(key, group_name); -} - -void GlobalActivityTracker::RecordException(const void* pc, - const void* origin, - uint32_t code) { - RecordExceptionImpl(pc, origin, code); -} - -void GlobalActivityTracker::MarkDeleted() { - allocator_->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED); -} - -GlobalActivityTracker::GlobalActivityTracker( - std::unique_ptr allocator, - int stack_depth, - int64_t process_id) - : allocator_(std::move(allocator)), - stack_memory_size_(ThreadActivityTracker::SizeForStackDepth(stack_depth)), - process_id_(process_id == 0 ? GetCurrentProcId() : process_id), - this_thread_tracker_(&OnTLSDestroy), - thread_tracker_count_(0), - thread_tracker_allocator_(allocator_.get(), - kTypeIdActivityTracker, - kTypeIdActivityTrackerFree, - stack_memory_size_, - kCachedThreadMemories, - /*make_iterable=*/true), - user_data_allocator_(allocator_.get(), - kTypeIdUserDataRecord, - kTypeIdUserDataRecordFree, - kUserDataSize, - kCachedUserDataMemories, - /*make_iterable=*/true), - process_data_(allocator_->GetAsArray( - AllocateFrom(allocator_.get(), - kTypeIdProcessDataRecordFree, - kProcessDataSize, - kTypeIdProcessDataRecord), - kTypeIdProcessDataRecord, - kProcessDataSize), - kProcessDataSize, - process_id_) { - DCHECK_NE(0, process_id_); - - // Ensure that there is no other global object and then make this one such. - DCHECK(!g_tracker_); - subtle::Release_Store(&g_tracker_, reinterpret_cast(this)); - - // The data records must be iterable in order to be found by an analyzer. - allocator_->MakeIterable(allocator_->GetAsReference( - process_data_.GetBaseAddress(), kTypeIdProcessDataRecord)); - - // Note that this process has launched. - SetProcessPhase(PROCESS_LAUNCHED); - - // Fetch and record all activated field trials. - FieldTrial::ActiveGroups active_groups; - FieldTrialList::GetActiveFieldTrialGroups(&active_groups); - for (auto& group : active_groups) - RecordFieldTrial(group.trial_name, group.group_name); -} - -GlobalActivityTracker::~GlobalActivityTracker() { - DCHECK(Get() == nullptr || Get() == this); - DCHECK_EQ(0, thread_tracker_count_.load(std::memory_order_relaxed)); - subtle::Release_Store(&g_tracker_, 0); -} - -void GlobalActivityTracker::ReturnTrackerMemory( - ManagedActivityTracker* tracker) { - PersistentMemoryAllocator::Reference mem_reference = tracker->mem_reference_; - void* mem_base = tracker->mem_base_; - DCHECK(mem_reference); - DCHECK(mem_base); - - // Remove the destructed tracker from the set of known ones. - DCHECK_LE(1, thread_tracker_count_.load(std::memory_order_relaxed)); - thread_tracker_count_.fetch_sub(1, std::memory_order_relaxed); - - // Release this memory for re-use at a later time. - base::AutoLock autolock(thread_tracker_allocator_lock_); - thread_tracker_allocator_.ReleaseObjectReference(mem_reference); -} - -void GlobalActivityTracker::RecordExceptionImpl(const void* pc, - const void* origin, - uint32_t code) { - // Get an existing tracker for this thread. It's not possible to create - // one at this point because such would involve memory allocations and - // other potentially complex operations that can cause failures if done - // within an exception handler. In most cases various operations will - // have already created the tracker so this shouldn't generally be a - // problem. - ThreadActivityTracker* tracker = GetTrackerForCurrentThread(); - if (!tracker) - return; - - tracker->RecordExceptionActivity(pc, origin, Activity::ACT_EXCEPTION, - ActivityData::ForException(code)); -} - -// static -void GlobalActivityTracker::OnTLSDestroy(void* value) { - delete reinterpret_cast(value); -} - -ScopedActivity::ScopedActivity(const void* program_counter, - uint8_t action, - uint32_t id, - int32_t info) - : GlobalActivityTracker::ScopedThreadActivity( - program_counter, - nullptr, - static_cast(Activity::ACT_GENERIC | action), - ActivityData::ForGeneric(id, info), - /*lock_allowed=*/true), - id_(id) { - // The action must not affect the category bits of the activity type. - DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK); -} - -void ScopedActivity::ChangeAction(uint8_t action) { - DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK); - ChangeTypeAndData(static_cast(Activity::ACT_GENERIC | action), - kNullActivityData); -} - -void ScopedActivity::ChangeInfo(int32_t info) { - ChangeTypeAndData(Activity::ACT_NULL, ActivityData::ForGeneric(id_, info)); -} - -void ScopedActivity::ChangeActionAndInfo(uint8_t action, int32_t info) { - DCHECK_EQ(0, action & Activity::ACT_CATEGORY_MASK); - ChangeTypeAndData(static_cast(Activity::ACT_GENERIC | action), - ActivityData::ForGeneric(id_, info)); -} - -ScopedTaskRunActivity::ScopedTaskRunActivity( - const void* program_counter, - const base::PendingTask& task) - : GlobalActivityTracker::ScopedThreadActivity( - program_counter, - task.posted_from.program_counter(), - Activity::ACT_TASK_RUN, - ActivityData::ForTask(task.sequence_num), - /*lock_allowed=*/true) {} - -ScopedLockAcquireActivity::ScopedLockAcquireActivity( - const void* program_counter, - const base::internal::LockImpl* lock) - : GlobalActivityTracker::ScopedThreadActivity( - program_counter, - nullptr, - Activity::ACT_LOCK_ACQUIRE, - ActivityData::ForLock(lock), - /*lock_allowed=*/false) {} - -ScopedEventWaitActivity::ScopedEventWaitActivity( - const void* program_counter, - const base::WaitableEvent* event) - : GlobalActivityTracker::ScopedThreadActivity( - program_counter, - nullptr, - Activity::ACT_EVENT_WAIT, - ActivityData::ForEvent(event), - /*lock_allowed=*/true) {} - -ScopedThreadJoinActivity::ScopedThreadJoinActivity( - const void* program_counter, - const base::PlatformThreadHandle* thread) - : GlobalActivityTracker::ScopedThreadActivity( - program_counter, - nullptr, - Activity::ACT_THREAD_JOIN, - ActivityData::ForThread(*thread), - /*lock_allowed=*/true) {} - -#if !defined(OS_NACL) && !defined(OS_IOS) -ScopedProcessWaitActivity::ScopedProcessWaitActivity( - const void* program_counter, - const base::Process* process) - : GlobalActivityTracker::ScopedThreadActivity( - program_counter, - nullptr, - Activity::ACT_PROCESS_WAIT, - ActivityData::ForProcess(process->Pid()), - /*lock_allowed=*/true) {} -#endif - -} // namespace debug -} // namespace base diff --git a/debug/activity_tracker.h b/debug/activity_tracker.h deleted file mode 100644 index bfd9f9d45..000000000 --- a/debug/activity_tracker.h +++ /dev/null @@ -1,1360 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Activity tracking provides a low-overhead method of collecting information -// about the state of the application for analysis both while it is running -// and after it has terminated unexpectedly. Its primary purpose is to help -// locate reasons the browser becomes unresponsive by providing insight into -// what all the various threads and processes are (or were) doing. - -#ifndef BASE_DEBUG_ACTIVITY_TRACKER_H_ -#define BASE_DEBUG_ACTIVITY_TRACKER_H_ - -// std::atomic is undesired due to performance issues when used as global -// variables. There are no such instances here. This module uses the -// PersistentMemoryAllocator which also uses std::atomic and is written -// by the same author. -#include -#include -#include -#include -#include - -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/gtest_prod_util.h" -#include "base/location.h" -#include "base/memory/shared_memory.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/process/process_handle.h" -#include "base/strings/string_piece.h" -#include "base/strings/utf_string_conversions.h" -#include "base/task_runner.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread_local_storage.h" - -namespace base { - -struct PendingTask; - -class FilePath; -class Lock; -class PlatformThreadHandle; -class Process; -class WaitableEvent; - -namespace debug { - -class ThreadActivityTracker; - - -enum : int { - // The maximum number of call-stack addresses stored per activity. This - // cannot be changed without also changing the version number of the - // structure. See kTypeIdActivityTracker in GlobalActivityTracker. - kActivityCallStackSize = 10, -}; - -// A class for keeping all information needed to verify that a structure is -// associated with a given process. -struct OwningProcess { - OwningProcess(); - ~OwningProcess(); - - // Initializes structure with the current process id and the current time. - // These can uniquely identify a process. A unique non-zero data_id will be - // set making it possible to tell using atomic reads if the data has changed. - void Release_Initialize(int64_t pid = 0); - - // Explicitly sets the process ID. - void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp); - - // Gets the associated process ID, in native form, and the creation timestamp - // from memory without loading the entire structure for analysis. This will - // return false if no valid process ID is available. - static bool GetOwningProcessId(const void* memory, - int64_t* out_id, - int64_t* out_stamp); - - // SHA1(base::debug::OwningProcess): Increment this if structure changes! - static constexpr uint32_t kPersistentTypeId = 0xB1179672 + 1; - - // Expected size for 32/64-bit check by PersistentMemoryAllocator. - static constexpr size_t kExpectedInstanceSize = 24; - - std::atomic data_id; - uint32_t padding; - int64_t process_id; - int64_t create_stamp; -}; - -// The data associated with an activity is dependent upon the activity type. -// This union defines all of the various fields. All fields must be explicitly -// sized types to ensure no interoperability problems between 32-bit and -// 64-bit systems. -union ActivityData { - // Expected size for 32/64-bit check. - // TODO(bcwhite): VC2015 doesn't allow statics in unions. Fix when it does. - // static constexpr size_t kExpectedInstanceSize = 8; - - // Generic activities don't have any defined structure. - struct { - uint32_t id; // An arbitrary identifier used for association. - int32_t info; // An arbitrary value used for information purposes. - } generic; - struct { - uint64_t sequence_id; // The sequence identifier of the posted task. - } task; - struct { - uint64_t lock_address; // The memory address of the lock object. - } lock; - struct { - uint64_t event_address; // The memory address of the event object. - } event; - struct { - int64_t thread_id; // A unique identifier for a thread within a process. - } thread; - struct { - int64_t process_id; // A unique identifier for a process. - } process; - struct { - uint32_t code; // An "exception code" number. - } exception; - - // These methods create an ActivityData object from the appropriate - // parameters. Objects of this type should always be created this way to - // ensure that no fields remain unpopulated should the set of recorded - // fields change. They're defined inline where practical because they - // reduce to loading a small local structure with a few values, roughly - // the same as loading all those values into parameters. - - static ActivityData ForGeneric(uint32_t id, int32_t info) { - ActivityData data; - data.generic.id = id; - data.generic.info = info; - return data; - } - - static ActivityData ForTask(uint64_t sequence) { - ActivityData data; - data.task.sequence_id = sequence; - return data; - } - - static ActivityData ForLock(const void* lock) { - ActivityData data; - data.lock.lock_address = reinterpret_cast(lock); - return data; - } - - static ActivityData ForEvent(const void* event) { - ActivityData data; - data.event.event_address = reinterpret_cast(event); - return data; - } - - static ActivityData ForThread(const PlatformThreadHandle& handle); - static ActivityData ForThread(const int64_t id) { - ActivityData data; - data.thread.thread_id = id; - return data; - } - - static ActivityData ForProcess(const int64_t id) { - ActivityData data; - data.process.process_id = id; - return data; - } - - static ActivityData ForException(const uint32_t code) { - ActivityData data; - data.exception.code = code; - return data; - } -}; - -// A "null" activity-data that can be passed to indicate "do not change". -extern const ActivityData kNullActivityData; - - -// A helper class that is used for managing memory allocations within a -// persistent memory allocator. Instances of this class are NOT thread-safe. -// Use from a single thread or protect access with a lock. -class BASE_EXPORT ActivityTrackerMemoryAllocator { - public: - using Reference = PersistentMemoryAllocator::Reference; - - // Creates a instance for allocating objects of a fixed |object_type|, a - // corresponding |object_free| type, and the |object_size|. An internal - // cache of the last |cache_size| released references will be kept for - // quick future fetches. If |make_iterable| then allocated objects will - // be marked "iterable" in the allocator. - ActivityTrackerMemoryAllocator(PersistentMemoryAllocator* allocator, - uint32_t object_type, - uint32_t object_free_type, - size_t object_size, - size_t cache_size, - bool make_iterable); - ~ActivityTrackerMemoryAllocator(); - - // Gets a reference to an object of the configured type. This can return - // a null reference if it was not possible to allocate the memory. - Reference GetObjectReference(); - - // Returns an object to the "free" pool. - void ReleaseObjectReference(Reference ref); - - // Helper function to access an object allocated using this instance. - template - T* GetAsObject(Reference ref) { - return allocator_->GetAsObject(ref); - } - - // Similar to GetAsObject() but converts references to arrays of objects. - template - T* GetAsArray(Reference ref, size_t count) { - return allocator_->GetAsArray(ref, object_type_, count); - } - - // The current "used size" of the internal cache, visible for testing. - size_t cache_used() const { return cache_used_; } - - private: - PersistentMemoryAllocator* const allocator_; - const uint32_t object_type_; - const uint32_t object_free_type_; - const size_t object_size_; - const size_t cache_size_; - const bool make_iterable_; - - // An iterator for going through persistent memory looking for free'd objects. - PersistentMemoryAllocator::Iterator iterator_; - - // The cache of released object memories. - std::unique_ptr cache_values_; - size_t cache_used_; - - DISALLOW_COPY_AND_ASSIGN(ActivityTrackerMemoryAllocator); -}; - - -// This structure is the full contents recorded for every activity pushed -// onto the stack. The |activity_type| indicates what is actually stored in -// the |data| field. All fields must be explicitly sized types to ensure no -// interoperability problems between 32-bit and 64-bit systems. -struct Activity { - // SHA1(base::debug::Activity): Increment this if structure changes! - static constexpr uint32_t kPersistentTypeId = 0x99425159 + 1; - // Expected size for 32/64-bit check. Update this if structure changes! - static constexpr size_t kExpectedInstanceSize = - 48 + 8 * kActivityCallStackSize; - - // The type of an activity on the stack. Activities are broken into - // categories with the category ID taking the top 4 bits and the lower - // bits representing an action within that category. This combination - // makes it easy to "switch" based on the type during analysis. - enum Type : uint8_t { - // This "null" constant is used to indicate "do not change" in calls. - ACT_NULL = 0, - - // Task activities involve callbacks posted to a thread or thread-pool - // using the PostTask() method or any of its friends. - ACT_TASK = 1 << 4, - ACT_TASK_RUN = ACT_TASK, - - // Lock activities involve the acquisition of "mutex" locks. - ACT_LOCK = 2 << 4, - ACT_LOCK_ACQUIRE = ACT_LOCK, - ACT_LOCK_RELEASE, - - // Event activities involve operations on a WaitableEvent. - ACT_EVENT = 3 << 4, - ACT_EVENT_WAIT = ACT_EVENT, - ACT_EVENT_SIGNAL, - - // Thread activities involve the life management of threads. - ACT_THREAD = 4 << 4, - ACT_THREAD_START = ACT_THREAD, - ACT_THREAD_JOIN, - - // Process activities involve the life management of processes. - ACT_PROCESS = 5 << 4, - ACT_PROCESS_START = ACT_PROCESS, - ACT_PROCESS_WAIT, - - // Exception activities indicate the occurence of something unexpected. - ACT_EXCEPTION = 14 << 4, - - // Generic activities are user defined and can be anything. - ACT_GENERIC = 15 << 4, - - // These constants can be used to separate the category and action from - // a combined activity type. - ACT_CATEGORY_MASK = 0xF << 4, - ACT_ACTION_MASK = 0xF - }; - - // Internal representation of time. During collection, this is in "ticks" - // but when returned in a snapshot, it is "wall time". - int64_t time_internal; - - // The address that pushed the activity onto the stack as a raw number. - uint64_t calling_address; - - // The address that is the origin of the activity if it not obvious from - // the call stack. This is useful for things like tasks that are posted - // from a completely different thread though most activities will leave - // it null. - uint64_t origin_address; - - // Array of program-counters that make up the top of the call stack. - // Despite the fixed size, this list is always null-terminated. Entries - // after the terminator have no meaning and may or may not also be null. - // The list will be completely empty if call-stack collection is not - // enabled. - uint64_t call_stack[kActivityCallStackSize]; - - // Reference to arbitrary user data within the persistent memory segment - // and a unique identifier for it. - uint32_t user_data_ref; - uint32_t user_data_id; - - // The (enumerated) type of the activity. This defines what fields of the - // |data| record are valid. - uint8_t activity_type; - - // Padding to ensure that the next member begins on a 64-bit boundary - // even on 32-bit builds which ensures inter-operability between CPU - // architectures. New fields can be taken from this space. - uint8_t padding[7]; - - // Information specific to the |activity_type|. - ActivityData data; - - static void FillFrom(Activity* activity, - const void* program_counter, - const void* origin, - Type type, - const ActivityData& data); -}; - -// This class manages arbitrary user data that can be associated with activities -// done by a thread by supporting key/value pairs of any type. This can provide -// additional information during debugging. It is also used to store arbitrary -// global data. All updates must be done from the same thread though other -// threads can read it concurrently if they create new objects using the same -// memory. -class BASE_EXPORT ActivityUserData { - public: - // List of known value type. REFERENCE types must immediately follow the non- - // external types. - enum ValueType : uint8_t { - END_OF_VALUES = 0, - RAW_VALUE, - RAW_VALUE_REFERENCE, - STRING_VALUE, - STRING_VALUE_REFERENCE, - CHAR_VALUE, - BOOL_VALUE, - SIGNED_VALUE, - UNSIGNED_VALUE, - }; - - class BASE_EXPORT TypedValue { - public: - TypedValue(); - TypedValue(const TypedValue& other); - ~TypedValue(); - - ValueType type() const { return type_; } - - // These methods return the extracted value in the correct format. - StringPiece Get() const; - StringPiece GetString() const; - bool GetBool() const; - char GetChar() const; - int64_t GetInt() const; - uint64_t GetUint() const; - - // These methods return references to process memory as originally provided - // to corresponding Set calls. USE WITH CAUTION! There is no guarantee that - // the referenced memory is assessible or useful. It's possible that: - // - the memory was free'd and reallocated for a different purpose - // - the memory has been released back to the OS - // - the memory belongs to a different process's address space - // Dereferencing the returned StringPiece when the memory is not accessible - // will cause the program to SEGV! - StringPiece GetReference() const; - StringPiece GetStringReference() const; - - private: - friend class ActivityUserData; - - ValueType type_ = END_OF_VALUES; - uint64_t short_value_; // Used to hold copy of numbers, etc. - std::string long_value_; // Used to hold copy of raw/string data. - StringPiece ref_value_; // Used to hold reference to external data. - }; - - using Snapshot = std::map; - - // Initialize the object either as a "sink" that just accepts and discards - // data or an active one that writes to a given (zeroed) memory block. - ActivityUserData(); - ActivityUserData(void* memory, size_t size, int64_t pid = 0); - virtual ~ActivityUserData(); - - // Gets the unique ID number for this user data. If this changes then the - // contents have been overwritten by another thread. The return value is - // always non-zero unless it's actually just a data "sink". - uint32_t id() const { - return header_ ? header_->owner.data_id.load(std::memory_order_relaxed) : 0; - } - - // Writes a |value| (as part of a key/value pair) that will be included with - // the activity in any reports. The same |name| can be written multiple times - // with each successive call overwriting the previously stored |value|. For - // raw and string values, the maximum size of successive writes is limited by - // the first call. The length of "name" is limited to 255 characters. - // - // This information is stored on a "best effort" basis. It may be dropped if - // the memory buffer is full or the associated activity is beyond the maximum - // recording depth. - void Set(StringPiece name, const void* memory, size_t size) { - Set(name, RAW_VALUE, memory, size); - } - void SetString(StringPiece name, StringPiece value) { - Set(name, STRING_VALUE, value.data(), value.length()); - } - void SetString(StringPiece name, StringPiece16 value) { - SetString(name, UTF16ToUTF8(value)); - } - void SetBool(StringPiece name, bool value) { - char cvalue = value ? 1 : 0; - Set(name, BOOL_VALUE, &cvalue, sizeof(cvalue)); - } - void SetChar(StringPiece name, char value) { - Set(name, CHAR_VALUE, &value, sizeof(value)); - } - void SetInt(StringPiece name, int64_t value) { - Set(name, SIGNED_VALUE, &value, sizeof(value)); - } - void SetUint(StringPiece name, uint64_t value) { - Set(name, UNSIGNED_VALUE, &value, sizeof(value)); - } - - // These function as above but don't actually copy the data into the - // persistent memory. They store unaltered pointers along with a size. These - // can be used in conjuction with a memory dump to find certain large pieces - // of information. - void SetReference(StringPiece name, const void* memory, size_t size) { - SetReference(name, RAW_VALUE_REFERENCE, memory, size); - } - void SetStringReference(StringPiece name, StringPiece value) { - SetReference(name, STRING_VALUE_REFERENCE, value.data(), value.length()); - } - - // Creates a snapshot of the key/value pairs contained within. The returned - // data will be fixed, independent of whatever changes afterward. There is - // some protection against concurrent modification. This will return false - // if the data is invalid or if a complete overwrite of the contents is - // detected. - bool CreateSnapshot(Snapshot* output_snapshot) const; - - // Gets the base memory address used for storing data. - const void* GetBaseAddress() const; - - // Explicitly sets the process ID. - void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp); - - // Gets the associated process ID, in native form, and the creation timestamp - // from tracker memory without loading the entire structure for analysis. This - // will return false if no valid process ID is available. - static bool GetOwningProcessId(const void* memory, - int64_t* out_id, - int64_t* out_stamp); - - protected: - virtual void Set(StringPiece name, - ValueType type, - const void* memory, - size_t size); - - private: - FRIEND_TEST_ALL_PREFIXES(ActivityTrackerTest, UserDataTest); - - enum : size_t { kMemoryAlignment = sizeof(uint64_t) }; - - // A structure that defines the structure header in memory. - struct MemoryHeader { - MemoryHeader(); - ~MemoryHeader(); - - OwningProcess owner; // Information about the creating process. - }; - - // Header to a key/value record held in persistent memory. - struct FieldHeader { - FieldHeader(); - ~FieldHeader(); - - std::atomic type; // Encoded ValueType - uint8_t name_size; // Length of "name" key. - std::atomic value_size; // Actual size of of the stored value. - uint16_t record_size; // Total storage of name, value, header. - }; - - // A structure used to reference data held outside of persistent memory. - struct ReferenceRecord { - uint64_t address; - uint64_t size; - }; - - // This record is used to hold known value is a map so that they can be - // found and overwritten later. - struct ValueInfo { - ValueInfo(); - ValueInfo(ValueInfo&&); - ~ValueInfo(); - - StringPiece name; // The "key" of the record. - ValueType type; // The type of the value. - void* memory; // Where the "value" is held. - std::atomic* size_ptr; // Address of the actual size of value. - size_t extent; // The total storage of the value, - }; // typically rounded up for alignment. - - void SetReference(StringPiece name, - ValueType type, - const void* memory, - size_t size); - - // Loads any data already in the memory segment. This allows for accessing - // records created previously. If this detects that the underlying data has - // gone away (cleared by another thread/process), it will invalidate all the - // data in this object and turn it into simple "sink" with no values to - // return. - void ImportExistingData() const; - - // A map of all the values within the memory block, keyed by name for quick - // updates of the values. This is "mutable" because it changes on "const" - // objects even when the actual data values can't change. - mutable std::map values_; - - // Information about the memory block in which new data can be stored. These - // are "mutable" because they change even on "const" objects that are just - // skipping already set values. - mutable char* memory_; - mutable size_t available_; - - // A pointer to the memory header for this instance. - MemoryHeader* const header_; - - // These hold values used when initially creating the object. They are - // compared against current header values to check for outside changes. - const uint32_t orig_data_id; - const int64_t orig_process_id; - const int64_t orig_create_stamp; - - DISALLOW_COPY_AND_ASSIGN(ActivityUserData); -}; - -// This class manages tracking a stack of activities for a single thread in -// a persistent manner, implementing a bounded-size stack in a fixed-size -// memory allocation. In order to support an operational mode where another -// thread is analyzing this data in real-time, atomic operations are used -// where necessary to guarantee a consistent view from the outside. -// -// This class is not generally used directly but instead managed by the -// GlobalActivityTracker instance and updated using Scoped*Activity local -// objects. -class BASE_EXPORT ThreadActivityTracker { - public: - using ActivityId = uint32_t; - - // This structure contains all the common information about the thread so - // it doesn't have to be repeated in every entry on the stack. It is defined - // and used completely within the .cc file. - struct Header; - - // This structure holds a copy of all the internal data at the moment the - // "snapshot" operation is done. It is disconnected from the live tracker - // so that continued operation of the thread will not cause changes here. - struct BASE_EXPORT Snapshot { - // Explicit constructor/destructor are needed because of complex types - // with non-trivial default constructors and destructors. - Snapshot(); - ~Snapshot(); - - // The name of the thread as set when it was created. The name may be - // truncated due to internal length limitations. - std::string thread_name; - - // The timestamp at which this process was created. - int64_t create_stamp; - - // The process and thread IDs. These values have no meaning other than - // they uniquely identify a running process and a running thread within - // that process. Thread-IDs can be re-used across different processes - // and both can be re-used after the process/thread exits. - int64_t process_id = 0; - int64_t thread_id = 0; - - // The current stack of activities that are underway for this thread. It - // is limited in its maximum size with later entries being left off. - std::vector activity_stack; - - // The current total depth of the activity stack, including those later - // entries not recorded in the |activity_stack| vector. - uint32_t activity_stack_depth = 0; - - // The last recorded "exception" activity. - Activity last_exception; - }; - - // This is the base class for having the compiler manage an activity on the - // tracker's stack. It does nothing but call methods on the passed |tracker| - // if it is not null, making it safe (and cheap) to create these objects - // even if activity tracking is not enabled. - class BASE_EXPORT ScopedActivity { - public: - ScopedActivity(ThreadActivityTracker* tracker, - const void* program_counter, - const void* origin, - Activity::Type type, - const ActivityData& data); - ~ScopedActivity(); - - // Changes some basic metadata about the activity. - void ChangeTypeAndData(Activity::Type type, const ActivityData& data); - - protected: - // The thread tracker to which this object reports. It can be null if - // activity tracking is not (yet) enabled. - ThreadActivityTracker* const tracker_; - - // An identifier that indicates a specific activity on the stack. - ActivityId activity_id_; - - private: - DISALLOW_COPY_AND_ASSIGN(ScopedActivity); - }; - - // A ThreadActivityTracker runs on top of memory that is managed externally. - // It must be large enough for the internal header and a few Activity - // blocks. See SizeForStackDepth(). - ThreadActivityTracker(void* base, size_t size); - virtual ~ThreadActivityTracker(); - - // Indicates that an activity has started from a given |origin| address in - // the code, though it can be null if the creator's address is not known. - // The |type| and |data| describe the activity. |program_counter| should be - // the result of GetProgramCounter() where push is called. Returned is an - // ID that can be used to adjust the pushed activity. - ActivityId PushActivity(const void* program_counter, - const void* origin, - Activity::Type type, - const ActivityData& data); - - // An inlined version of the above that gets the program counter where it - // is called. - ALWAYS_INLINE - ActivityId PushActivity(const void* origin, - Activity::Type type, - const ActivityData& data) { - return PushActivity(GetProgramCounter(), origin, type, data); - } - - // Changes the activity |type| and |data| of the top-most entry on the stack. - // This is useful if the information has changed and it is desireable to - // track that change without creating a new stack entry. If the type is - // ACT_NULL or the data is kNullActivityData then that value will remain - // unchanged. The type, if changed, must remain in the same category. - // Changing both is not atomic so a snapshot operation could occur between - // the update of |type| and |data| or between update of |data| fields. - void ChangeActivity(ActivityId id, - Activity::Type type, - const ActivityData& data); - - // Indicates that an activity has completed. - void PopActivity(ActivityId id); - - // Sets the user-data information for an activity. - std::unique_ptr GetUserData( - ActivityId id, - ActivityTrackerMemoryAllocator* allocator); - - // Returns if there is true use-data associated with a given ActivityId since - // it's possible than any returned object is just a sink. - bool HasUserData(ActivityId id); - - // Release the user-data information for an activity. - void ReleaseUserData(ActivityId id, - ActivityTrackerMemoryAllocator* allocator); - - // Save an exception. |origin| is the location of the exception. - void RecordExceptionActivity(const void* program_counter, - const void* origin, - Activity::Type type, - const ActivityData& data); - - // Returns whether the current data is valid or not. It is not valid if - // corruption has been detected in the header or other data structures. - bool IsValid() const; - - // Gets a copy of the tracker contents for analysis. Returns false if a - // snapshot was not possible, perhaps because the data is not valid; the - // contents of |output_snapshot| are undefined in that case. The current - // implementation does not support concurrent snapshot operations. - bool CreateSnapshot(Snapshot* output_snapshot) const; - - // Gets the base memory address used for storing data. - const void* GetBaseAddress(); - - // Access the "data version" value so tests can determine if an activity - // was pushed and popped in a single call. - uint32_t GetDataVersionForTesting(); - - // Explicitly sets the process ID. - void SetOwningProcessIdForTesting(int64_t pid, int64_t stamp); - - // Gets the associated process ID, in native form, and the creation timestamp - // from tracker memory without loading the entire structure for analysis. This - // will return false if no valid process ID is available. - static bool GetOwningProcessId(const void* memory, - int64_t* out_id, - int64_t* out_stamp); - - // Calculates the memory size required for a given stack depth, including - // the internal header structure for the stack. - static size_t SizeForStackDepth(int stack_depth); - - private: - friend class ActivityTrackerTest; - - bool CalledOnValidThread(); - - std::unique_ptr CreateUserDataForActivity( - Activity* activity, - ActivityTrackerMemoryAllocator* allocator); - - Header* const header_; // Pointer to the Header structure. - Activity* const stack_; // The stack of activities. - -#if DCHECK_IS_ON() - // The ActivityTracker is thread bound, and will be invoked across all the - // sequences that run on the thread. A ThreadChecker does not work here, as it - // asserts on running in the same sequence each time. - const PlatformThreadRef thread_id_; // The thread this instance is bound to. -#endif - const uint32_t stack_slots_; // The total number of stack slots. - - bool valid_ = false; // Tracks whether the data is valid or not. - - DISALLOW_COPY_AND_ASSIGN(ThreadActivityTracker); -}; - - -// The global tracker manages all the individual thread trackers. Memory for -// the thread trackers is taken from a PersistentMemoryAllocator which allows -// for the data to be analyzed by a parallel process or even post-mortem. -class BASE_EXPORT GlobalActivityTracker { - public: - // Type identifiers used when storing in persistent memory so they can be - // identified during extraction; the first 4 bytes of the SHA1 of the name - // is used as a unique integer. A "version number" is added to the base - // so that, if the structure of that object changes, stored older versions - // will be safely ignored. These are public so that an external process - // can recognize records of this type within an allocator. - enum : uint32_t { - kTypeIdActivityTracker = 0x5D7381AF + 4, // SHA1(ActivityTracker) v4 - kTypeIdUserDataRecord = 0x615EDDD7 + 3, // SHA1(UserDataRecord) v3 - kTypeIdGlobalLogMessage = 0x4CF434F9 + 1, // SHA1(GlobalLogMessage) v1 - kTypeIdProcessDataRecord = kTypeIdUserDataRecord + 0x100, - - kTypeIdActivityTrackerFree = ~kTypeIdActivityTracker, - kTypeIdUserDataRecordFree = ~kTypeIdUserDataRecord, - kTypeIdProcessDataRecordFree = ~kTypeIdProcessDataRecord, - }; - - // An enumeration of common process life stages. All entries are given an - // explicit number so they are known and remain constant; this allows for - // cross-version analysis either locally or on a server. - enum ProcessPhase : int { - // The phases are generic and may have meaning to the tracker. - PROCESS_PHASE_UNKNOWN = 0, - PROCESS_LAUNCHED = 1, - PROCESS_LAUNCH_FAILED = 2, - PROCESS_EXITED_CLEANLY = 10, - PROCESS_EXITED_WITH_CODE = 11, - - // Add here whatever is useful for analysis. - PROCESS_SHUTDOWN_STARTED = 100, - PROCESS_MAIN_LOOP_STARTED = 101, - }; - - // A callback made when a process exits to allow immediate analysis of its - // data. Note that the system may reuse the |process_id| so when fetching - // records it's important to ensure that what is returned was created before - // the |exit_stamp|. Movement of |process_data| information is allowed. - using ProcessExitCallback = - Callback; - - // This structure contains information about a loaded module, as shown to - // users of the tracker. - struct BASE_EXPORT ModuleInfo { - ModuleInfo(); - ModuleInfo(ModuleInfo&& rhs); - ModuleInfo(const ModuleInfo& rhs); - ~ModuleInfo(); - - ModuleInfo& operator=(ModuleInfo&& rhs); - ModuleInfo& operator=(const ModuleInfo& rhs); - - // Information about where and when the module was loaded/unloaded. - bool is_loaded = false; // Was the last operation a load or unload? - uintptr_t address = 0; // Address of the last load operation. - int64_t load_time = 0; // Time of last change; set automatically. - - // Information about the module itself. These never change no matter how - // many times a module may be loaded and unloaded. - size_t size = 0; // The size of the loaded module. - uint32_t timestamp = 0; // Opaque "timestamp" for the module. - uint32_t age = 0; // Opaque "age" for the module. - uint8_t identifier[16]; // Opaque identifier (GUID, etc.) for the module. - std::string file; // The full path to the file. (UTF-8) - std::string debug_file; // The full path to the debug file. - }; - - // This is a thin wrapper around the thread-tracker's ScopedActivity that - // allows thread-safe access to data values. It is safe to use even if - // activity tracking is not enabled. - class BASE_EXPORT ScopedThreadActivity - : public ThreadActivityTracker::ScopedActivity { - public: - ScopedThreadActivity(const void* program_counter, - const void* origin, - Activity::Type type, - const ActivityData& data, - bool lock_allowed); - ~ScopedThreadActivity(); - - // Returns an object for manipulating user data. - ActivityUserData& user_data(); - - private: - // Gets (or creates) a tracker for the current thread. If locking is not - // allowed (because a lock is being tracked which would cause recursion) - // then the attempt to create one if none found will be skipped. Once - // the tracker for this thread has been created for other reasons, locks - // will be tracked. The thread-tracker uses locks. - static ThreadActivityTracker* GetOrCreateTracker(bool lock_allowed) { - GlobalActivityTracker* global_tracker = Get(); - if (!global_tracker) - return nullptr; - if (lock_allowed) - return global_tracker->GetOrCreateTrackerForCurrentThread(); - else - return global_tracker->GetTrackerForCurrentThread(); - } - - // An object that manages additional user data, created only upon request. - std::unique_ptr user_data_; - - DISALLOW_COPY_AND_ASSIGN(ScopedThreadActivity); - }; - - ~GlobalActivityTracker(); - - // Creates a global tracker using a given persistent-memory |allocator| and - // providing the given |stack_depth| to each thread tracker it manages. The - // created object is activated so tracking will begin immediately upon return. - // The |process_id| can be zero to get it from the OS but is taken for testing - // purposes. - static void CreateWithAllocator( - std::unique_ptr allocator, - int stack_depth, - int64_t process_id); - -#if !defined(OS_NACL) - // Like above but internally creates an allocator around a disk file with - // the specified |size| at the given |file_path|. Any existing file will be - // overwritten. The |id| and |name| are arbitrary and stored in the allocator - // for reference by whatever process reads it. Returns true if successful. - static bool CreateWithFile(const FilePath& file_path, - size_t size, - uint64_t id, - StringPiece name, - int stack_depth); -#endif // !defined(OS_NACL) - - // Like above but internally creates an allocator using local heap memory of - // the specified size. This is used primarily for unit tests. The |process_id| - // can be zero to get it from the OS but is taken for testing purposes. - static bool CreateWithLocalMemory(size_t size, - uint64_t id, - StringPiece name, - int stack_depth, - int64_t process_id); - - // Like above but internally creates an allocator using a shared-memory - // segment. The segment must already be mapped into the local memory space. - static bool CreateWithSharedMemory(std::unique_ptr shm, - uint64_t id, - StringPiece name, - int stack_depth); - - // Like above but takes a handle to an existing shared memory segment and - // maps it before creating the tracker. - static bool CreateWithSharedMemoryHandle(const SharedMemoryHandle& handle, - size_t size, - uint64_t id, - StringPiece name, - int stack_depth); - - // Gets the global activity-tracker or null if none exists. - static GlobalActivityTracker* Get() { - return reinterpret_cast( - subtle::Acquire_Load(&g_tracker_)); - } - - // Sets the global activity-tracker for testing purposes. - static void SetForTesting(std::unique_ptr tracker); - - // This access to the persistent allocator is only for testing; it extracts - // the global tracker completely. All tracked threads must exit before - // calling this. Tracking for the current thread will be automatically - // stopped. - static std::unique_ptr ReleaseForTesting(); - - // Convenience method for determining if a global tracker is active. - static bool IsEnabled() { return Get() != nullptr; } - - // Gets the persistent-memory-allocator in which data is stored. Callers - // can store additional records here to pass more information to the - // analysis process. - PersistentMemoryAllocator* allocator() { return allocator_.get(); } - - // Gets the thread's activity-tracker if it exists. This is inline for - // performance reasons and it uses thread-local-storage (TLS) so that there - // is no significant lookup time required to find the one for the calling - // thread. Ownership remains with the global tracker. - ThreadActivityTracker* GetTrackerForCurrentThread() { - return reinterpret_cast(this_thread_tracker_.Get()); - } - - // Gets the thread's activity-tracker or creates one if none exists. This - // is inline for performance reasons. Ownership remains with the global - // tracker. - ThreadActivityTracker* GetOrCreateTrackerForCurrentThread() { - ThreadActivityTracker* tracker = GetTrackerForCurrentThread(); - if (tracker) - return tracker; - return CreateTrackerForCurrentThread(); - } - - // Creates an activity-tracker for the current thread. - ThreadActivityTracker* CreateTrackerForCurrentThread(); - - // Releases the activity-tracker for the current thread (for testing only). - void ReleaseTrackerForCurrentThreadForTesting(); - - // Sets a task-runner that can be used for background work. - void SetBackgroundTaskRunner(const scoped_refptr& runner); - - // Sets an optional callback to be called when a process exits. - void SetProcessExitCallback(ProcessExitCallback callback); - - // Manages process lifetimes. These are called by the process that launched - // and reaped the subprocess, not the subprocess itself. If it is expensive - // to generate the parameters, Get() the global tracker and call these - // conditionally rather than using the static versions. - void RecordProcessLaunch(ProcessId process_id, - const FilePath::StringType& cmd); - void RecordProcessLaunch(ProcessId process_id, - const FilePath::StringType& exe, - const FilePath::StringType& args); - void RecordProcessExit(ProcessId process_id, int exit_code); - static void RecordProcessLaunchIfEnabled(ProcessId process_id, - const FilePath::StringType& cmd) { - GlobalActivityTracker* tracker = Get(); - if (tracker) - tracker->RecordProcessLaunch(process_id, cmd); - } - static void RecordProcessLaunchIfEnabled(ProcessId process_id, - const FilePath::StringType& exe, - const FilePath::StringType& args) { - GlobalActivityTracker* tracker = Get(); - if (tracker) - tracker->RecordProcessLaunch(process_id, exe, args); - } - static void RecordProcessExitIfEnabled(ProcessId process_id, int exit_code) { - GlobalActivityTracker* tracker = Get(); - if (tracker) - tracker->RecordProcessExit(process_id, exit_code); - } - - // Sets the "phase" of the current process, useful for knowing what it was - // doing when it last reported. - void SetProcessPhase(ProcessPhase phase); - static void SetProcessPhaseIfEnabled(ProcessPhase phase) { - GlobalActivityTracker* tracker = Get(); - if (tracker) - tracker->SetProcessPhase(phase); - } - - // Records a log message. The current implementation does NOT recycle these - // only store critical messages such as FATAL ones. - void RecordLogMessage(StringPiece message); - static void RecordLogMessageIfEnabled(StringPiece message) { - GlobalActivityTracker* tracker = Get(); - if (tracker) - tracker->RecordLogMessage(message); - } - - // Records a module load/unload event. This is safe to call multiple times - // even with the same information. - void RecordModuleInfo(const ModuleInfo& info); - static void RecordModuleInfoIfEnabled(const ModuleInfo& info) { - GlobalActivityTracker* tracker = Get(); - if (tracker) - tracker->RecordModuleInfo(info); - } - - // Record field trial information. This call is thread-safe. In addition to - // this, construction of a GlobalActivityTracker will cause all existing - // active field trials to be fetched and recorded. - void RecordFieldTrial(const std::string& trial_name, StringPiece group_name); - static void RecordFieldTrialIfEnabled(const std::string& trial_name, - StringPiece group_name) { - GlobalActivityTracker* tracker = Get(); - if (tracker) - tracker->RecordFieldTrial(trial_name, group_name); - } - - // Record exception information for the current thread. - ALWAYS_INLINE - void RecordException(const void* origin, uint32_t code) { - return RecordExceptionImpl(GetProgramCounter(), origin, code); - } - void RecordException(const void* pc, const void* origin, uint32_t code); - - // Marks the tracked data as deleted. - void MarkDeleted(); - - // Gets the process ID used for tracking. This is typically the same as what - // the OS thinks is the current process but can be overridden for testing. - int64_t process_id() { return process_id_; } - - // Accesses the process data record for storing arbitrary key/value pairs. - // Updates to this are thread-safe. - ActivityUserData& process_data() { return process_data_; } - - private: - friend class GlobalActivityAnalyzer; - friend class ScopedThreadActivity; - friend class ActivityTrackerTest; - - enum : int { - // The maximum number of threads that can be tracked within a process. If - // more than this number run concurrently, tracking of new ones may cease. - kMaxThreadCount = 100, - kCachedThreadMemories = 10, - kCachedUserDataMemories = 10, - }; - - // A wrapper around ActivityUserData that is thread-safe and thus can be used - // in the global scope without the requirement of being called from only one - // thread. - class ThreadSafeUserData : public ActivityUserData { - public: - ThreadSafeUserData(void* memory, size_t size, int64_t pid = 0); - ~ThreadSafeUserData() override; - - private: - void Set(StringPiece name, - ValueType type, - const void* memory, - size_t size) override; - - Lock data_lock_; - - DISALLOW_COPY_AND_ASSIGN(ThreadSafeUserData); - }; - - // State of a module as stored in persistent memory. This supports a single - // loading of a module only. If modules are loaded multiple times at - // different addresses, only the last will be recorded and an unload will - // not revert to the information of any other addresses. - struct BASE_EXPORT ModuleInfoRecord { - // SHA1(ModuleInfoRecord): Increment this if structure changes! - static constexpr uint32_t kPersistentTypeId = 0x05DB5F41 + 1; - - // Expected size for 32/64-bit check by PersistentMemoryAllocator. - static constexpr size_t kExpectedInstanceSize = - OwningProcess::kExpectedInstanceSize + 56; - - // The atomic unfortunately makes this a "complex" class on some compilers - // and thus requires an out-of-line constructor & destructor even though - // they do nothing. - ModuleInfoRecord(); - ~ModuleInfoRecord(); - - OwningProcess owner; // The process that created this record. - uint64_t address; // The base address of the module. - uint64_t load_time; // Time of last load/unload. - uint64_t size; // The size of the module in bytes. - uint32_t timestamp; // Opaque timestamp of the module. - uint32_t age; // Opaque "age" associated with the module. - uint8_t identifier[16]; // Opaque identifier for the module. - std::atomic changes; // Number load/unload actions. - uint16_t pickle_size; // The size of the following pickle. - uint8_t loaded; // Flag if module is loaded or not. - char pickle[1]; // Other strings; may allocate larger. - - // Decodes/encodes storage structure from more generic info structure. - bool DecodeTo(GlobalActivityTracker::ModuleInfo* info, - size_t record_size) const; - static ModuleInfoRecord* CreateFrom( - const GlobalActivityTracker::ModuleInfo& info, - PersistentMemoryAllocator* allocator); - - // Updates the core information without changing the encoded strings. This - // is useful when a known module changes state (i.e. new load or unload). - bool UpdateFrom(const GlobalActivityTracker::ModuleInfo& info); - - private: - DISALLOW_COPY_AND_ASSIGN(ModuleInfoRecord); - }; - - // A thin wrapper around the main thread-tracker that keeps additional - // information that the global tracker needs to handle joined threads. - class ManagedActivityTracker : public ThreadActivityTracker { - public: - ManagedActivityTracker(PersistentMemoryAllocator::Reference mem_reference, - void* base, - size_t size); - ~ManagedActivityTracker() override; - - // The reference into persistent memory from which the thread-tracker's - // memory was created. - const PersistentMemoryAllocator::Reference mem_reference_; - - // The physical address used for the thread-tracker's memory. - void* const mem_base_; - - private: - DISALLOW_COPY_AND_ASSIGN(ManagedActivityTracker); - }; - - // Creates a global tracker using a given persistent-memory |allocator| and - // providing the given |stack_depth| to each thread tracker it manages. The - // created object is activated so tracking has already started upon return. - // The |process_id| can be zero to get it from the OS but is taken for testing - // purposes. - GlobalActivityTracker(std::unique_ptr allocator, - int stack_depth, - int64_t process_id); - - // Returns the memory used by an activity-tracker managed by this class. - // It is called during the destruction of a ManagedActivityTracker object. - void ReturnTrackerMemory(ManagedActivityTracker* tracker); - - // Records exception information. - void RecordExceptionImpl(const void* pc, const void* origin, uint32_t code); - - // Releases the activity-tracker associcated with thread. It is called - // automatically when a thread is joined and thus there is nothing more to - // be tracked. |value| is a pointer to a ManagedActivityTracker. - static void OnTLSDestroy(void* value); - - // Does process-exit work. This can be run on any thread. - void CleanupAfterProcess(int64_t process_id, - int64_t exit_stamp, - int exit_code, - std::string&& command_line); - - // The persistent-memory allocator from which the memory for all trackers - // is taken. - std::unique_ptr allocator_; - - // The size (in bytes) of memory required by a ThreadActivityTracker to - // provide the stack-depth requested during construction. - const size_t stack_memory_size_; - - // The process-id of the current process. This is kept as a member variable, - // defined during initialization, for testing purposes. - const int64_t process_id_; - - // The activity tracker for the currently executing thread. - ThreadLocalStorage::Slot this_thread_tracker_; - - // The number of thread trackers currently active. - std::atomic thread_tracker_count_; - - // A caching memory allocator for thread-tracker objects. - ActivityTrackerMemoryAllocator thread_tracker_allocator_; - Lock thread_tracker_allocator_lock_; - - // A caching memory allocator for user data attached to activity data. - ActivityTrackerMemoryAllocator user_data_allocator_; - Lock user_data_allocator_lock_; - - // An object for holding arbitrary key value pairs with thread-safe access. - ThreadSafeUserData process_data_; - - // A map of global module information, keyed by module path. - std::map modules_; - Lock modules_lock_; - - // The active global activity tracker. - static subtle::AtomicWord g_tracker_; - - // A lock that is used to protect access to the following fields. - Lock global_tracker_lock_; - - // The collection of processes being tracked and their command-lines. - std::map known_processes_; - - // A task-runner that can be used for doing background processing. - scoped_refptr background_task_runner_; - - // A callback performed when a subprocess exits, including its exit-code - // and the phase it was in when that occurred. This will be called via - // the |background_task_runner_| if one is set or whatever thread reaped - // the process otherwise. - ProcessExitCallback process_exit_callback_; - - DISALLOW_COPY_AND_ASSIGN(GlobalActivityTracker); -}; - - -// Record entry in to and out of an arbitrary block of code. -class BASE_EXPORT ScopedActivity - : public GlobalActivityTracker::ScopedThreadActivity { - public: - // Track activity at the specified FROM_HERE location for an arbitrary - // 4-bit |action|, an arbitrary 32-bit |id|, and 32-bits of arbitrary - // |info|. None of these values affect operation; they're all purely - // for association and analysis. To have unique identifiers across a - // diverse code-base, create the number by taking the first 8 characters - // of the hash of the activity being tracked. - // - // For example: - // Tracking method: void MayNeverExit(uint32_t foo) {...} - // echo -n "MayNeverExit" | sha1sum => e44873ccab21e2b71270da24aa1... - // - // void MayNeverExit(int32_t foo) { - // base::debug::ScopedActivity track_me(0, 0xE44873CC, foo); - // ... - // } - ALWAYS_INLINE - ScopedActivity(uint8_t action, uint32_t id, int32_t info) - : ScopedActivity(GetProgramCounter(), action, id, info) {} - ScopedActivity() : ScopedActivity(0, 0, 0) {} - - // Changes the |action| and/or |info| of this activity on the stack. This - // is useful for tracking progress through a function, updating the action - // to indicate "milestones" in the block (max 16 milestones: 0-15) or the - // info to reflect other changes. Changing both is not atomic so a snapshot - // operation could occur between the update of |action| and |info|. - void ChangeAction(uint8_t action); - void ChangeInfo(int32_t info); - void ChangeActionAndInfo(uint8_t action, int32_t info); - - private: - // Constructs the object using a passed-in program-counter. - ScopedActivity(const void* program_counter, - uint8_t action, - uint32_t id, - int32_t info); - - // A copy of the ID code so it doesn't have to be passed by the caller when - // changing the |info| field. - uint32_t id_; - - DISALLOW_COPY_AND_ASSIGN(ScopedActivity); -}; - - -// These "scoped" classes provide easy tracking of various blocking actions. - -class BASE_EXPORT ScopedTaskRunActivity - : public GlobalActivityTracker::ScopedThreadActivity { - public: - ALWAYS_INLINE - explicit ScopedTaskRunActivity(const PendingTask& task) - : ScopedTaskRunActivity(GetProgramCounter(), task) {} - - private: - ScopedTaskRunActivity(const void* program_counter, const PendingTask& task); - DISALLOW_COPY_AND_ASSIGN(ScopedTaskRunActivity); -}; - -class BASE_EXPORT ScopedLockAcquireActivity - : public GlobalActivityTracker::ScopedThreadActivity { - public: - ALWAYS_INLINE - explicit ScopedLockAcquireActivity(const base::internal::LockImpl* lock) - : ScopedLockAcquireActivity(GetProgramCounter(), lock) {} - - private: - ScopedLockAcquireActivity(const void* program_counter, - const base::internal::LockImpl* lock); - DISALLOW_COPY_AND_ASSIGN(ScopedLockAcquireActivity); -}; - -class BASE_EXPORT ScopedEventWaitActivity - : public GlobalActivityTracker::ScopedThreadActivity { - public: - ALWAYS_INLINE - explicit ScopedEventWaitActivity(const WaitableEvent* event) - : ScopedEventWaitActivity(GetProgramCounter(), event) {} - - private: - ScopedEventWaitActivity(const void* program_counter, - const WaitableEvent* event); - DISALLOW_COPY_AND_ASSIGN(ScopedEventWaitActivity); -}; - -class BASE_EXPORT ScopedThreadJoinActivity - : public GlobalActivityTracker::ScopedThreadActivity { - public: - ALWAYS_INLINE - explicit ScopedThreadJoinActivity(const PlatformThreadHandle* thread) - : ScopedThreadJoinActivity(GetProgramCounter(), thread) {} - - private: - ScopedThreadJoinActivity(const void* program_counter, - const PlatformThreadHandle* thread); - DISALLOW_COPY_AND_ASSIGN(ScopedThreadJoinActivity); -}; - -// Some systems don't have base::Process -#if !defined(OS_NACL) && !defined(OS_IOS) -class BASE_EXPORT ScopedProcessWaitActivity - : public GlobalActivityTracker::ScopedThreadActivity { - public: - ALWAYS_INLINE - explicit ScopedProcessWaitActivity(const Process* process) - : ScopedProcessWaitActivity(GetProgramCounter(), process) {} - - private: - ScopedProcessWaitActivity(const void* program_counter, - const Process* process); - DISALLOW_COPY_AND_ASSIGN(ScopedProcessWaitActivity); -}; -#endif - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_ACTIVITY_TRACKER_H_ diff --git a/debug/activity_tracker_unittest.cc b/debug/activity_tracker_unittest.cc deleted file mode 100644 index e2b61a920..000000000 --- a/debug/activity_tracker_unittest.cc +++ /dev/null @@ -1,585 +0,0 @@ -// Copyright 2016 The Chromium 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/activity_tracker.h" - -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/memory_mapped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/ptr_util.h" -#include "base/pending_task.h" -#include "base/rand_util.h" -#include "base/synchronization/condition_variable.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/spin_wait.h" -#include "base/threading/platform_thread.h" -#include "base/threading/simple_thread.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace debug { - -namespace { - -class TestActivityTracker : public ThreadActivityTracker { - public: - TestActivityTracker(std::unique_ptr memory, size_t mem_size) - : ThreadActivityTracker(memset(memory.get(), 0, mem_size), mem_size), - mem_segment_(std::move(memory)) {} - - ~TestActivityTracker() override = default; - - private: - std::unique_ptr mem_segment_; -}; - -} // namespace - - -class ActivityTrackerTest : public testing::Test { - public: - const int kMemorySize = 1 << 20; // 1MiB - const int kStackSize = 1 << 10; // 1KiB - - using ActivityId = ThreadActivityTracker::ActivityId; - - ActivityTrackerTest() = default; - - ~ActivityTrackerTest() override { - GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); - if (global_tracker) { - global_tracker->ReleaseTrackerForCurrentThreadForTesting(); - delete global_tracker; - } - } - - std::unique_ptr CreateActivityTracker() { - std::unique_ptr memory(new char[kStackSize]); - return std::make_unique(std::move(memory), kStackSize); - } - - size_t GetGlobalActiveTrackerCount() { - GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); - if (!global_tracker) - return 0; - return global_tracker->thread_tracker_count_.load( - std::memory_order_relaxed); - } - - size_t GetGlobalInactiveTrackerCount() { - GlobalActivityTracker* global_tracker = GlobalActivityTracker::Get(); - if (!global_tracker) - return 0; - AutoLock autolock(global_tracker->thread_tracker_allocator_lock_); - return global_tracker->thread_tracker_allocator_.cache_used(); - } - - size_t GetGlobalUserDataMemoryCacheUsed() { - return GlobalActivityTracker::Get()->user_data_allocator_.cache_used(); - } - - void HandleProcessExit(int64_t id, - int64_t stamp, - int code, - GlobalActivityTracker::ProcessPhase phase, - std::string&& command, - ActivityUserData::Snapshot&& data) { - exit_id_ = id; - exit_stamp_ = stamp; - exit_code_ = code; - exit_phase_ = phase; - exit_command_ = std::move(command); - exit_data_ = std::move(data); - } - - int64_t exit_id_ = 0; - int64_t exit_stamp_; - int exit_code_; - GlobalActivityTracker::ProcessPhase exit_phase_; - std::string exit_command_; - ActivityUserData::Snapshot exit_data_; -}; - -TEST_F(ActivityTrackerTest, UserDataTest) { - char buffer[256]; - memset(buffer, 0, sizeof(buffer)); - ActivityUserData data(buffer, sizeof(buffer)); - size_t space = sizeof(buffer) - sizeof(ActivityUserData::MemoryHeader); - ASSERT_EQ(space, data.available_); - - data.SetInt("foo", 1); - space -= 24; - ASSERT_EQ(space, data.available_); - - data.SetUint("b", 1U); // Small names fit beside header in a word. - space -= 16; - ASSERT_EQ(space, data.available_); - - data.Set("c", buffer, 10); - space -= 24; - ASSERT_EQ(space, data.available_); - - data.SetString("dear john", "it's been fun"); - space -= 32; - ASSERT_EQ(space, data.available_); - - data.Set("c", buffer, 20); - ASSERT_EQ(space, data.available_); - - data.SetString("dear john", "but we're done together"); - ASSERT_EQ(space, data.available_); - - data.SetString("dear john", "bye"); - ASSERT_EQ(space, data.available_); - - data.SetChar("d", 'x'); - space -= 8; - ASSERT_EQ(space, data.available_); - - data.SetBool("ee", true); - space -= 16; - ASSERT_EQ(space, data.available_); - - data.SetString("f", ""); - space -= 8; - ASSERT_EQ(space, data.available_); -} - -TEST_F(ActivityTrackerTest, PushPopTest) { - std::unique_ptr tracker = CreateActivityTracker(); - ThreadActivityTracker::Snapshot snapshot; - - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(0U, snapshot.activity_stack_depth); - ASSERT_EQ(0U, snapshot.activity_stack.size()); - - char origin1; - ActivityId id1 = tracker->PushActivity(&origin1, Activity::ACT_TASK, - ActivityData::ForTask(11)); - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(1U, snapshot.activity_stack_depth); - ASSERT_EQ(1U, snapshot.activity_stack.size()); - EXPECT_NE(0, snapshot.activity_stack[0].time_internal); - EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type); - EXPECT_EQ(reinterpret_cast(&origin1), - snapshot.activity_stack[0].origin_address); - EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id); - - char origin2; - char lock2; - ActivityId id2 = tracker->PushActivity(&origin2, Activity::ACT_LOCK, - ActivityData::ForLock(&lock2)); - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(2U, snapshot.activity_stack_depth); - ASSERT_EQ(2U, snapshot.activity_stack.size()); - EXPECT_LE(snapshot.activity_stack[0].time_internal, - snapshot.activity_stack[1].time_internal); - EXPECT_EQ(Activity::ACT_LOCK, snapshot.activity_stack[1].activity_type); - EXPECT_EQ(reinterpret_cast(&origin2), - snapshot.activity_stack[1].origin_address); - EXPECT_EQ(reinterpret_cast(&lock2), - snapshot.activity_stack[1].data.lock.lock_address); - - tracker->PopActivity(id2); - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(1U, snapshot.activity_stack_depth); - ASSERT_EQ(1U, snapshot.activity_stack.size()); - EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type); - EXPECT_EQ(reinterpret_cast(&origin1), - snapshot.activity_stack[0].origin_address); - EXPECT_EQ(11U, snapshot.activity_stack[0].data.task.sequence_id); - - tracker->PopActivity(id1); - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(0U, snapshot.activity_stack_depth); - ASSERT_EQ(0U, snapshot.activity_stack.size()); -} - -TEST_F(ActivityTrackerTest, ScopedTaskTest) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - - ThreadActivityTracker* tracker = - GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); - ThreadActivityTracker::Snapshot snapshot; - ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed()); - - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(0U, snapshot.activity_stack_depth); - ASSERT_EQ(0U, snapshot.activity_stack.size()); - - { - PendingTask task1(FROM_HERE, DoNothing()); - ScopedTaskRunActivity activity1(task1); - ActivityUserData& user_data1 = activity1.user_data(); - (void)user_data1; // Tell compiler it's been used. - - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(1U, snapshot.activity_stack_depth); - ASSERT_EQ(1U, snapshot.activity_stack.size()); - EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type); - - { - PendingTask task2(FROM_HERE, DoNothing()); - ScopedTaskRunActivity activity2(task2); - ActivityUserData& user_data2 = activity2.user_data(); - (void)user_data2; // Tell compiler it's been used. - - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(2U, snapshot.activity_stack_depth); - ASSERT_EQ(2U, snapshot.activity_stack.size()); - EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[1].activity_type); - } - - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(1U, snapshot.activity_stack_depth); - ASSERT_EQ(1U, snapshot.activity_stack.size()); - EXPECT_EQ(Activity::ACT_TASK, snapshot.activity_stack[0].activity_type); - } - - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(0U, snapshot.activity_stack_depth); - ASSERT_EQ(0U, snapshot.activity_stack.size()); - ASSERT_EQ(2U, GetGlobalUserDataMemoryCacheUsed()); -} - -namespace { - -class SimpleLockThread : public SimpleThread { - public: - SimpleLockThread(const std::string& name, Lock* lock) - : SimpleThread(name, Options()), - lock_(lock), - data_changed_(false), - is_running_(false) {} - - ~SimpleLockThread() override = default; - - void Run() override { - ThreadActivityTracker* tracker = - GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); - uint32_t pre_version = tracker->GetDataVersionForTesting(); - - is_running_.store(true, std::memory_order_relaxed); - lock_->Acquire(); - data_changed_ = tracker->GetDataVersionForTesting() != pre_version; - lock_->Release(); - is_running_.store(false, std::memory_order_relaxed); - } - - bool IsRunning() { return is_running_.load(std::memory_order_relaxed); } - - bool WasDataChanged() { return data_changed_; }; - - private: - Lock* lock_; - bool data_changed_; - std::atomic is_running_; - - DISALLOW_COPY_AND_ASSIGN(SimpleLockThread); -}; - -} // namespace - -TEST_F(ActivityTrackerTest, LockTest) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - - ThreadActivityTracker* tracker = - GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); - ThreadActivityTracker::Snapshot snapshot; - ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed()); - - Lock lock; - uint32_t pre_version = tracker->GetDataVersionForTesting(); - - // Check no activity when only "trying" a lock. - EXPECT_TRUE(lock.Try()); - EXPECT_EQ(pre_version, tracker->GetDataVersionForTesting()); - lock.Release(); - EXPECT_EQ(pre_version, tracker->GetDataVersionForTesting()); - - // Check no activity when acquiring a free lock. - SimpleLockThread t1("locker1", &lock); - t1.Start(); - t1.Join(); - EXPECT_FALSE(t1.WasDataChanged()); - - // Check that activity is recorded when acquring a busy lock. - SimpleLockThread t2("locker2", &lock); - lock.Acquire(); - t2.Start(); - while (!t2.IsRunning()) - PlatformThread::Sleep(TimeDelta::FromMilliseconds(10)); - // t2 can't join until the lock is released but have to give time for t2 to - // actually block on the lock before releasing it or the results will not - // be correct. - PlatformThread::Sleep(TimeDelta::FromMilliseconds(200)); - lock.Release(); - // Now the results will be valid. - t2.Join(); - EXPECT_TRUE(t2.WasDataChanged()); -} - -TEST_F(ActivityTrackerTest, ExceptionTest) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - - ThreadActivityTracker* tracker = - GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); - ThreadActivityTracker::Snapshot snapshot; - ASSERT_EQ(0U, GetGlobalUserDataMemoryCacheUsed()); - - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - ASSERT_EQ(0U, snapshot.last_exception.activity_type); - - char origin; - global->RecordException(&origin, 42); - - ASSERT_TRUE(tracker->CreateSnapshot(&snapshot)); - EXPECT_EQ(Activity::ACT_EXCEPTION, snapshot.last_exception.activity_type); - EXPECT_EQ(reinterpret_cast(&origin), - snapshot.last_exception.origin_address); - EXPECT_EQ(42U, snapshot.last_exception.data.exception.code); -} - -TEST_F(ActivityTrackerTest, CreateWithFileTest) { - const char temp_name[] = "CreateWithFileTest"; - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name); - const size_t temp_size = 64 << 10; // 64 KiB - - // Create a global tracker on a new file. - ASSERT_FALSE(PathExists(temp_file)); - GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "foo", 3); - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - EXPECT_EQ(std::string("foo"), global->allocator()->Name()); - global->ReleaseTrackerForCurrentThreadForTesting(); - delete global; - - // Create a global tracker over an existing file, replacing it. If the - // replacement doesn't work, the name will remain as it was first created. - ASSERT_TRUE(PathExists(temp_file)); - GlobalActivityTracker::CreateWithFile(temp_file, temp_size, 0, "bar", 3); - global = GlobalActivityTracker::Get(); - EXPECT_EQ(std::string("bar"), global->allocator()->Name()); - global->ReleaseTrackerForCurrentThreadForTesting(); - delete global; -} - - -// GlobalActivityTracker tests below. - -TEST_F(ActivityTrackerTest, BasicTest) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - - // Ensure the data repositories have backing store, indicated by non-zero ID. - EXPECT_NE(0U, global->process_data().id()); -} - -namespace { - -class SimpleActivityThread : public SimpleThread { - public: - SimpleActivityThread(const std::string& name, - const void* origin, - Activity::Type activity, - const ActivityData& data) - : SimpleThread(name, Options()), - origin_(origin), - activity_(activity), - data_(data), - ready_(false), - exit_(false), - exit_condition_(&lock_) {} - - ~SimpleActivityThread() override = default; - - void Run() override { - ThreadActivityTracker::ActivityId id = - GlobalActivityTracker::Get() - ->GetOrCreateTrackerForCurrentThread() - ->PushActivity(origin_, activity_, data_); - - { - AutoLock auto_lock(lock_); - ready_.store(true, std::memory_order_release); - while (!exit_.load(std::memory_order_relaxed)) - exit_condition_.Wait(); - } - - GlobalActivityTracker::Get()->GetTrackerForCurrentThread()->PopActivity(id); - } - - void Exit() { - AutoLock auto_lock(lock_); - exit_.store(true, std::memory_order_relaxed); - exit_condition_.Signal(); - } - - void WaitReady() { - SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(ready_.load(std::memory_order_acquire)); - } - - private: - const void* origin_; - Activity::Type activity_; - ActivityData data_; - - std::atomic ready_; - std::atomic exit_; - Lock lock_; - ConditionVariable exit_condition_; - - DISALLOW_COPY_AND_ASSIGN(SimpleActivityThread); -}; - -} // namespace - -TEST_F(ActivityTrackerTest, ThreadDeathTest) { - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - GlobalActivityTracker::Get()->GetOrCreateTrackerForCurrentThread(); - const size_t starting_active = GetGlobalActiveTrackerCount(); - const size_t starting_inactive = GetGlobalInactiveTrackerCount(); - - SimpleActivityThread t1("t1", nullptr, Activity::ACT_TASK, - ActivityData::ForTask(11)); - t1.Start(); - t1.WaitReady(); - EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount()); - EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount()); - - t1.Exit(); - t1.Join(); - EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount()); - EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); - - // Start another thread and ensure it re-uses the existing memory. - - SimpleActivityThread t2("t2", nullptr, Activity::ACT_TASK, - ActivityData::ForTask(22)); - t2.Start(); - t2.WaitReady(); - EXPECT_EQ(starting_active + 1, GetGlobalActiveTrackerCount()); - EXPECT_EQ(starting_inactive, GetGlobalInactiveTrackerCount()); - - t2.Exit(); - t2.Join(); - EXPECT_EQ(starting_active, GetGlobalActiveTrackerCount()); - EXPECT_EQ(starting_inactive + 1, GetGlobalInactiveTrackerCount()); -} - -TEST_F(ActivityTrackerTest, ProcessDeathTest) { - // This doesn't actually create and destroy a process. Instead, it uses for- - // testing interfaces to simulate data created by other processes. - const int64_t other_process_id = GetCurrentProcId() + 1; - - GlobalActivityTracker::CreateWithLocalMemory(kMemorySize, 0, "", 3, 0); - GlobalActivityTracker* global = GlobalActivityTracker::Get(); - ThreadActivityTracker* thread = global->GetOrCreateTrackerForCurrentThread(); - - // Get callbacks for process exit. - global->SetProcessExitCallback( - Bind(&ActivityTrackerTest::HandleProcessExit, Unretained(this))); - - // Pretend than another process has started. - global->RecordProcessLaunch(other_process_id, FILE_PATH_LITERAL("foo --bar")); - - // Do some activities. - PendingTask task(FROM_HERE, DoNothing()); - ScopedTaskRunActivity activity(task); - ActivityUserData& user_data = activity.user_data(); - ASSERT_NE(0U, user_data.id()); - - // Get the memory-allocator references to that data. - PersistentMemoryAllocator::Reference proc_data_ref = - global->allocator()->GetAsReference( - global->process_data().GetBaseAddress(), - GlobalActivityTracker::kTypeIdProcessDataRecord); - ASSERT_TRUE(proc_data_ref); - PersistentMemoryAllocator::Reference tracker_ref = - global->allocator()->GetAsReference( - thread->GetBaseAddress(), - GlobalActivityTracker::kTypeIdActivityTracker); - ASSERT_TRUE(tracker_ref); - PersistentMemoryAllocator::Reference user_data_ref = - global->allocator()->GetAsReference( - user_data.GetBaseAddress(), - GlobalActivityTracker::kTypeIdUserDataRecord); - ASSERT_TRUE(user_data_ref); - - // Make a copy of the thread-tracker state so it can be restored later. - const size_t tracker_size = global->allocator()->GetAllocSize(tracker_ref); - std::unique_ptr tracker_copy(new char[tracker_size]); - memcpy(tracker_copy.get(), thread->GetBaseAddress(), tracker_size); - - // Change the objects to appear to be owned by another process. Use a "past" - // time so that exit-time is always later than create-time. - const int64_t past_stamp = Time::Now().ToInternalValue() - 1; - int64_t owning_id; - int64_t stamp; - ASSERT_TRUE(ActivityUserData::GetOwningProcessId( - global->process_data().GetBaseAddress(), &owning_id, &stamp)); - EXPECT_NE(other_process_id, owning_id); - ASSERT_TRUE(ThreadActivityTracker::GetOwningProcessId( - thread->GetBaseAddress(), &owning_id, &stamp)); - EXPECT_NE(other_process_id, owning_id); - ASSERT_TRUE(ActivityUserData::GetOwningProcessId(user_data.GetBaseAddress(), - &owning_id, &stamp)); - EXPECT_NE(other_process_id, owning_id); - global->process_data().SetOwningProcessIdForTesting(other_process_id, - past_stamp); - thread->SetOwningProcessIdForTesting(other_process_id, past_stamp); - user_data.SetOwningProcessIdForTesting(other_process_id, past_stamp); - ASSERT_TRUE(ActivityUserData::GetOwningProcessId( - global->process_data().GetBaseAddress(), &owning_id, &stamp)); - EXPECT_EQ(other_process_id, owning_id); - ASSERT_TRUE(ThreadActivityTracker::GetOwningProcessId( - thread->GetBaseAddress(), &owning_id, &stamp)); - EXPECT_EQ(other_process_id, owning_id); - ASSERT_TRUE(ActivityUserData::GetOwningProcessId(user_data.GetBaseAddress(), - &owning_id, &stamp)); - EXPECT_EQ(other_process_id, owning_id); - - // Check that process exit will perform callback and free the allocations. - ASSERT_EQ(0, exit_id_); - ASSERT_EQ(GlobalActivityTracker::kTypeIdProcessDataRecord, - global->allocator()->GetType(proc_data_ref)); - ASSERT_EQ(GlobalActivityTracker::kTypeIdActivityTracker, - global->allocator()->GetType(tracker_ref)); - ASSERT_EQ(GlobalActivityTracker::kTypeIdUserDataRecord, - global->allocator()->GetType(user_data_ref)); - global->RecordProcessExit(other_process_id, 0); - EXPECT_EQ(other_process_id, exit_id_); - EXPECT_EQ("foo --bar", exit_command_); - EXPECT_EQ(GlobalActivityTracker::kTypeIdProcessDataRecordFree, - global->allocator()->GetType(proc_data_ref)); - EXPECT_EQ(GlobalActivityTracker::kTypeIdActivityTrackerFree, - global->allocator()->GetType(tracker_ref)); - EXPECT_EQ(GlobalActivityTracker::kTypeIdUserDataRecordFree, - global->allocator()->GetType(user_data_ref)); - - // Restore memory contents and types so things don't crash when doing real - // process clean-up. - memcpy(const_cast(thread->GetBaseAddress()), tracker_copy.get(), - tracker_size); - global->allocator()->ChangeType( - proc_data_ref, GlobalActivityTracker::kTypeIdProcessDataRecord, - GlobalActivityTracker::kTypeIdUserDataRecordFree, false); - global->allocator()->ChangeType( - tracker_ref, GlobalActivityTracker::kTypeIdActivityTracker, - GlobalActivityTracker::kTypeIdActivityTrackerFree, false); - global->allocator()->ChangeType( - user_data_ref, GlobalActivityTracker::kTypeIdUserDataRecord, - GlobalActivityTracker::kTypeIdUserDataRecordFree, false); -} - -} // namespace debug -} // namespace base diff --git a/debug/alias.cc b/debug/alias.cc deleted file mode 100644 index ebc8b5a4c..000000000 --- a/debug/alias.cc +++ /dev/null @@ -1,27 +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/alias.h" -#include "build/build_config.h" - -namespace base { -namespace debug { - -#if defined(COMPILER_MSVC) -#pragma optimize("", off) -#elif defined(__clang__) -#pragma clang optimize off -#endif - -void Alias(const void* var) { -} - -#if defined(COMPILER_MSVC) -#pragma optimize("", on) -#elif defined(__clang__) -#pragma clang optimize on -#endif - -} // namespace debug -} // namespace base diff --git a/debug/alias.h b/debug/alias.h deleted file mode 100644 index 128fdaa05..000000000 --- a/debug/alias.h +++ /dev/null @@ -1,43 +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_DEBUG_ALIAS_H_ -#define BASE_DEBUG_ALIAS_H_ - -#include "base/base_export.h" -#include "base/strings/string_util.h" - -namespace base { -namespace debug { - -// Make the optimizer think that var is aliased. This is to prevent it from -// optimizing out local variables that would not otherwise be live at the point -// of a potential crash. -// base::debug::Alias should only be used for local variables, not globals, -// object members, or function return values - these must be copied to locals if -// you want to ensure they are recorded in crash dumps. -// Note that if the local variable is a pointer then its value will be retained -// but the memory that it points to will probably not be saved in the crash -// dump - by default only stack memory is saved. Therefore the aliasing -// technique is usually only worthwhile with non-pointer variables. If you have -// a pointer to an object and you want to retain the object's state you need to -// copy the object or its fields to local variables. Example usage: -// int last_error = err_; -// base::debug::Alias(&last_error); -// DEBUG_ALIAS_FOR_CSTR(name_copy, p->name, 16); -// CHECK(false); -void BASE_EXPORT Alias(const void* var); - -} // namespace debug -} // namespace base - -// Convenience macro that copies the null-terminated string from |c_str| into a -// stack-allocated char array named |var_name| that holds up to |char_count| -// characters and should be preserved in memory dumps. -#define DEBUG_ALIAS_FOR_CSTR(var_name, c_str, char_count) \ - char var_name[char_count]; \ - ::base::strlcpy(var_name, (c_str), arraysize(var_name)); \ - ::base::debug::Alias(var_name); - -#endif // BASE_DEBUG_ALIAS_H_ diff --git a/debug/alias_unittest.cc b/debug/alias_unittest.cc deleted file mode 100644 index 66682f1f0..000000000 --- a/debug/alias_unittest.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018 The Chromium 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 -#include - -#include "base/debug/alias.h" -#include "testing/gtest/include/gtest/gtest.h" - -TEST(DebugAlias, Test) { - std::unique_ptr input = - std::make_unique("string contents"); - - // Verify the contents get copied + the new local variable has the right type. - DEBUG_ALIAS_FOR_CSTR(copy1, input->c_str(), 100 /* > input->size() */); - static_assert(sizeof(copy1) == 100, - "Verification that copy1 has expected size"); - EXPECT_STREQ("string contents", copy1); - - // Verify that the copy is properly null-terminated even when it is smaller - // than the input string. - DEBUG_ALIAS_FOR_CSTR(copy2, input->c_str(), 3 /* < input->size() */); - static_assert(sizeof(copy2) == 3, - "Verification that copy2 has expected size"); - EXPECT_STREQ("st", copy2); - EXPECT_EQ('\0', copy2[2]); -} diff --git a/debug/asan_invalid_access.cc b/debug/asan_invalid_access.cc deleted file mode 100644 index 07c19db9c..000000000 --- a/debug/asan_invalid_access.cc +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/debug/asan_invalid_access.h" - -#include - -#include - -#include "base/debug/alias.h" -#include "base/logging.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#endif - -namespace base { -namespace debug { - -namespace { - -#if defined(OS_WIN) && defined(ADDRESS_SANITIZER) -// 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; - LONG* array = new LONG[kArraySize]; - - // Explicitly call out to a kernel32 function to perform the memory access. - // This way the underflow won't be detected but the corruption will (as the - // allocator will still be hooked). - auto InterlockedIncrementFn = - reinterpret_cast( - GetProcAddress(GetModuleHandle(L"kernel32"), "InterlockedIncrement")); - CHECK(InterlockedIncrementFn); - - LONG volatile dummy = InterlockedIncrementFn(array - 1); - base::debug::Alias(const_cast(&dummy)); - - if (induce_crash) - CHECK(false); - delete[] array; -} -#endif // OS_WIN && ADDRESS_SANITIZER - -} // namespace - -#if defined(ADDRESS_SANITIZER) -// NOTE(sebmarchand): We intentionally perform some invalid heap access here in -// order to trigger an AddressSanitizer (ASan) error report. - -static const size_t kArraySize = 5; - -void AsanHeapOverflow() { - // Declares the array as volatile to make sure it doesn't get optimized away. - std::unique_ptr array( - const_cast(new int[kArraySize])); - int dummy = array[kArraySize]; - base::debug::Alias(&dummy); -} - -void AsanHeapUnderflow() { - // Declares the array as volatile to make sure it doesn't get optimized away. - std::unique_ptr array( - const_cast(new int[kArraySize])); - // We need to store the underflow address in a temporary variable as trying to - // access array[-1] will trigger a warning C4245: "conversion from 'int' to - // 'size_t', signed/unsigned mismatch". - volatile int* underflow_address = &array[0] - 1; - int dummy = *underflow_address; - base::debug::Alias(&dummy); -} - -void AsanHeapUseAfterFree() { - // Declares the array as volatile to make sure it doesn't get optimized away. - std::unique_ptr array( - const_cast(new int[kArraySize])); - volatile int* dangling = array.get(); - array.reset(); - int dummy = dangling[kArraySize / 2]; - base::debug::Alias(&dummy); -} - -#if defined(OS_WIN) -void AsanCorruptHeapBlock() { - CorruptMemoryBlock(false); -} - -void AsanCorruptHeap() { - CorruptMemoryBlock(true); -} -#endif // OS_WIN -#endif // ADDRESS_SANITIZER - -} // namespace debug -} // namespace base diff --git a/debug/asan_invalid_access.h b/debug/asan_invalid_access.h deleted file mode 100644 index dc9a7ee64..000000000 --- a/debug/asan_invalid_access.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// 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" -#include "build/build_config.h" - -namespace base { -namespace debug { - -#if defined(ADDRESS_SANITIZER) - -// 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(); - -// The "corrupt-block" and "corrupt-heap" classes of bugs is specific to -// Windows. -#if defined(OS_WIN) -// 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 // OS_WIN -#endif // ADDRESS_SANITIZER - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_ASAN_INVALID_ACCESS_H_ diff --git a/debug/close_handle_hook_win.cc b/debug/close_handle_hook_win.cc deleted file mode 100644 index 1f1f43213..000000000 --- a/debug/close_handle_hook_win.cc +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/debug/close_handle_hook_win.h" - -#include -#include -#include - -#include -#include -#include - -#include "base/macros.h" -#include "base/win/iat_patch_function.h" -#include "base/win/pe_image.h" -#include "base/win/scoped_handle.h" -#include "build/build_config.h" - -namespace { - -typedef BOOL (WINAPI* CloseHandleType) (HANDLE handle); - -typedef BOOL (WINAPI* DuplicateHandleType)(HANDLE source_process, - HANDLE source_handle, - HANDLE target_process, - HANDLE* target_handle, - DWORD desired_access, - BOOL inherit_handle, - DWORD options); - -CloseHandleType g_close_function = NULL; -DuplicateHandleType g_duplicate_function = NULL; - -// The entry point for CloseHandle interception. This function notifies the -// verifier about the handle that is being closed, and calls the original -// function. -BOOL WINAPI CloseHandleHook(HANDLE handle) { - base::win::OnHandleBeingClosed(handle); - return g_close_function(handle); -} - -BOOL WINAPI DuplicateHandleHook(HANDLE source_process, - HANDLE source_handle, - HANDLE target_process, - HANDLE* target_handle, - DWORD desired_access, - BOOL inherit_handle, - DWORD options) { - if ((options & DUPLICATE_CLOSE_SOURCE) && - (GetProcessId(source_process) == ::GetCurrentProcessId())) { - base::win::OnHandleBeingClosed(source_handle); - } - - return g_duplicate_function(source_process, source_handle, target_process, - target_handle, desired_access, inherit_handle, - options); -} - -} // namespace - -namespace base { -namespace debug { - -namespace { - -// Provides a simple way to temporarily change the protection of a memory page. -class AutoProtectMemory { - public: - AutoProtectMemory() - : changed_(false), address_(NULL), bytes_(0), old_protect_(0) {} - - ~AutoProtectMemory() { - RevertProtection(); - } - - // Grants write access to a given memory range. - bool ChangeProtection(void* address, size_t bytes); - - // Restores the original page protection. - void RevertProtection(); - - private: - bool changed_; - void* address_; - size_t bytes_; - DWORD old_protect_; - - DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory); -}; - -bool AutoProtectMemory::ChangeProtection(void* address, size_t bytes) { - DCHECK(!changed_); - DCHECK(address); - - // Change the page protection so that we can write. - MEMORY_BASIC_INFORMATION memory_info; - if (!VirtualQuery(address, &memory_info, sizeof(memory_info))) - return false; - - DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ | - PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) & - memory_info.Protect; - - DWORD protect = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; - if (!VirtualProtect(address, bytes, protect, &old_protect_)) - return false; - - changed_ = true; - address_ = address; - bytes_ = bytes; - return true; -} - -void AutoProtectMemory::RevertProtection() { - if (!changed_) - return; - - DCHECK(address_); - DCHECK(bytes_); - - VirtualProtect(address_, bytes_, old_protect_, &old_protect_); - changed_ = false; - address_ = NULL; - bytes_ = 0; - old_protect_ = 0; -} - -// Performs an EAT interception. -void EATPatch(HMODULE module, const char* function_name, - void* new_function, void** old_function) { - if (!module) - return; - - base::win::PEImage pe(module); - if (!pe.VerifyMagic()) - return; - - DWORD* eat_entry = pe.GetExportEntry(function_name); - if (!eat_entry) - return; - - if (!(*old_function)) - *old_function = pe.RVAToAddr(*eat_entry); - - AutoProtectMemory memory; - if (!memory.ChangeProtection(eat_entry, sizeof(DWORD))) - return; - - // Perform the patch. -#pragma warning(push) -#pragma warning(disable : 4311 4302) - // These casts generate truncation warnings because they are 32 bit specific. - *eat_entry = reinterpret_cast(new_function) - - reinterpret_cast(module); -#pragma warning(pop) -} - -// Performs an IAT interception. -base::win::IATPatchFunction* IATPatch(HMODULE module, const char* function_name, - void* new_function, void** old_function) { - if (!module) - return NULL; - - base::win::IATPatchFunction* patch = new base::win::IATPatchFunction; - __try { - // There is no guarantee that |module| is still loaded at this point. - if (patch->PatchFromModule(module, "kernel32.dll", function_name, - new_function)) { - delete patch; - return NULL; - } - } __except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION || - GetExceptionCode() == EXCEPTION_GUARD_PAGE || - GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR) ? - EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { - // Leak the patch. - return NULL; - } - - if (!(*old_function)) { - // Things are probably messed up if each intercepted function points to - // a different place, but we need only one function to call. - *old_function = patch->original_function(); - } - return patch; -} - -// Keeps track of all the hooks needed to intercept functions which could -// possibly close handles. -class HandleHooks { - public: - HandleHooks() {} - ~HandleHooks() {} - - void AddIATPatch(HMODULE module); - void AddEATPatch(); - - private: - std::vector hooks_; - DISALLOW_COPY_AND_ASSIGN(HandleHooks); -}; - -void HandleHooks::AddIATPatch(HMODULE module) { - if (!module) - return; - - base::win::IATPatchFunction* patch = NULL; - patch = - IATPatch(module, "CloseHandle", reinterpret_cast(&CloseHandleHook), - reinterpret_cast(&g_close_function)); - if (!patch) - return; - hooks_.push_back(patch); - - patch = IATPatch(module, "DuplicateHandle", - reinterpret_cast(&DuplicateHandleHook), - reinterpret_cast(&g_duplicate_function)); - if (!patch) - return; - hooks_.push_back(patch); -} - -void HandleHooks::AddEATPatch() { - // An attempt to restore the entry on the table at destruction is not safe. - EATPatch(GetModuleHandleA("kernel32.dll"), "CloseHandle", - reinterpret_cast(&CloseHandleHook), - reinterpret_cast(&g_close_function)); - EATPatch(GetModuleHandleA("kernel32.dll"), "DuplicateHandle", - reinterpret_cast(&DuplicateHandleHook), - reinterpret_cast(&g_duplicate_function)); -} - -void PatchLoadedModules(HandleHooks* hooks) { - const DWORD kSize = 256; - DWORD returned; - std::unique_ptr modules(new HMODULE[kSize]); - if (!EnumProcessModules(GetCurrentProcess(), modules.get(), - kSize * sizeof(HMODULE), &returned)) { - return; - } - returned /= sizeof(HMODULE); - returned = std::min(kSize, returned); - - for (DWORD current = 0; current < returned; current++) { - hooks->AddIATPatch(modules[current]); - } -} - -} // namespace - -void InstallHandleHooks() { - static HandleHooks* hooks = new HandleHooks(); - - // Performing EAT interception first is safer in the presence of other - // threads attempting to call CloseHandle. - hooks->AddEATPatch(); - PatchLoadedModules(hooks); -} - -} // namespace debug -} // namespace base diff --git a/debug/close_handle_hook_win.h b/debug/close_handle_hook_win.h deleted file mode 100644 index c775d75cb..000000000 --- a/debug/close_handle_hook_win.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_ -#define BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_ - -#include "base/base_export.h" - -namespace base { -namespace debug { - -// Installs the hooks required to debug use of improper handles. -BASE_EXPORT void InstallHandleHooks(); - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_CLOSE_HANDLE_HOOK_WIN_H_ diff --git a/debug/crash_logging.cc b/debug/crash_logging.cc deleted file mode 100644 index 1dabb6b96..000000000 --- a/debug/crash_logging.cc +++ /dev/null @@ -1,54 +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/debug/crash_logging.h" - -namespace base { -namespace debug { - -namespace { - -CrashKeyImplementation* g_crash_key_impl = nullptr; - -} // namespace - -CrashKeyString* AllocateCrashKeyString(const char name[], - CrashKeySize value_length) { - if (!g_crash_key_impl) - return nullptr; - - return g_crash_key_impl->Allocate(name, value_length); -} - -void SetCrashKeyString(CrashKeyString* crash_key, base::StringPiece value) { - if (!g_crash_key_impl || !crash_key) - return; - - g_crash_key_impl->Set(crash_key, value); -} - -void ClearCrashKeyString(CrashKeyString* crash_key) { - if (!g_crash_key_impl || !crash_key) - return; - - g_crash_key_impl->Clear(crash_key); -} - -ScopedCrashKeyString::ScopedCrashKeyString(CrashKeyString* crash_key, - base::StringPiece value) - : crash_key_(crash_key) { - SetCrashKeyString(crash_key_, value); -} - -ScopedCrashKeyString::~ScopedCrashKeyString() { - ClearCrashKeyString(crash_key_); -} - -void SetCrashKeyImplementation(std::unique_ptr impl) { - delete g_crash_key_impl; - g_crash_key_impl = impl.release(); -} - -} // namespace debug -} // namespace base diff --git a/debug/crash_logging.h b/debug/crash_logging.h deleted file mode 100644 index 9c6cd758d..000000000 --- a/debug/crash_logging.h +++ /dev/null @@ -1,104 +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_DEBUG_CRASH_LOGGING_H_ -#define BASE_DEBUG_CRASH_LOGGING_H_ - -#include - -#include - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" - -namespace base { -namespace debug { - -// A crash key is an annotation that is carried along with a crash report, to -// provide additional debugging information beyond a stack trace. Crash keys -// have a name and a string value. -// -// The preferred API is //components/crash/core/common:crash_key, however not -// all clients can hold a direct dependency on that target. The API provided -// in this file indirects the dependency. -// -// Example usage: -// static CrashKeyString* crash_key = -// AllocateCrashKeyString("name", CrashKeySize::Size32); -// SetCrashKeyString(crash_key, "value"); -// ClearCrashKeyString(crash_key); - -// The maximum length for a crash key's value must be one of the following -// pre-determined values. -enum class CrashKeySize { - Size32 = 32, - Size64 = 64, - Size256 = 256, -}; - -struct CrashKeyString; - -// Allocates a new crash key with the specified |name| with storage for a -// value up to length |size|. This will return null if the crash key system is -// not initialized. -BASE_EXPORT CrashKeyString* AllocateCrashKeyString(const char name[], - CrashKeySize size); - -// Stores |value| into the specified |crash_key|. The |crash_key| may be null -// if AllocateCrashKeyString() returned null. If |value| is longer than the -// size with which the key was allocated, it will be truncated. -BASE_EXPORT void SetCrashKeyString(CrashKeyString* crash_key, - base::StringPiece value); - -// Clears any value that was stored in |crash_key|. The |crash_key| may be -// null. -BASE_EXPORT void ClearCrashKeyString(CrashKeyString* crash_key); - -// A scoper that sets the specified key to value for the lifetime of the -// object, and clears it on destruction. -class BASE_EXPORT ScopedCrashKeyString { - public: - ScopedCrashKeyString(CrashKeyString* crash_key, base::StringPiece value); - ~ScopedCrashKeyString(); - - private: - CrashKeyString* const crash_key_; - - DISALLOW_COPY_AND_ASSIGN(ScopedCrashKeyString); -}; - -//////////////////////////////////////////////////////////////////////////////// -// The following declarations are used to initialize the crash key system -// in //base by providing implementations for the above functions. - -// The virtual interface that provides the implementation for the crash key -// API. This is implemented by a higher-layer component, and the instance is -// set using the function below. -class CrashKeyImplementation { - public: - virtual ~CrashKeyImplementation() = default; - - virtual CrashKeyString* Allocate(const char name[], CrashKeySize size) = 0; - virtual void Set(CrashKeyString* crash_key, base::StringPiece value) = 0; - virtual void Clear(CrashKeyString* crash_key) = 0; -}; - -// Initializes the crash key system in base by replacing the existing -// implementation, if it exists, with |impl|. The |impl| is copied into base. -BASE_EXPORT void SetCrashKeyImplementation( - std::unique_ptr impl); - -// The base structure for a crash key, storing the allocation metadata. -struct CrashKeyString { - constexpr CrashKeyString(const char name[], CrashKeySize size) - : name(name), size(size) {} - const char* const name; - const CrashKeySize size; -}; - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_CRASH_LOGGING_H_ diff --git a/debug/crash_logging_unittest.cc b/debug/crash_logging_unittest.cc deleted file mode 100644 index c10d36e36..000000000 --- a/debug/crash_logging_unittest.cc +++ /dev/null @@ -1,17 +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/debug/crash_logging.h" - -#include "testing/gtest/include/gtest/gtest.h" - -TEST(CrashLoggingTest, UninitializedCrashKeyStringSupport) { - auto* crash_key = base::debug::AllocateCrashKeyString( - "test", base::debug::CrashKeySize::Size32); - EXPECT_FALSE(crash_key); - - base::debug::SetCrashKeyString(crash_key, "value"); - - base::debug::ClearCrashKeyString(crash_key); -} diff --git a/debug/debugger.cc b/debug/debugger.cc deleted file mode 100644 index 1ccee1c2c..000000000 --- a/debug/debugger.cc +++ /dev/null @@ -1,42 +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/debugger.h" -#include "base/logging.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" - -namespace base { -namespace debug { - -static bool is_debug_ui_suppressed = false; - -bool WaitForDebugger(int wait_seconds, bool silent) { -#if defined(OS_ANDROID) - // The pid from which we know which process to attach to are not output by - // android ddms, so we have to print it out explicitly. - DLOG(INFO) << "DebugUtil::WaitForDebugger(pid=" << static_cast(getpid()) - << ")"; -#endif - for (int i = 0; i < wait_seconds * 10; ++i) { - if (BeingDebugged()) { - if (!silent) - BreakDebugger(); - return true; - } - PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); - } - return false; -} - -void SetSuppressDebugUI(bool suppress) { - is_debug_ui_suppressed = suppress; -} - -bool IsDebugUISuppressed() { - return is_debug_ui_suppressed; -} - -} // namespace debug -} // namespace base diff --git a/debug/debugger.h b/debug/debugger.h deleted file mode 100644 index 8680e281e..000000000 --- a/debug/debugger.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a cross platform interface for helper functions related to -// debuggers. You should use this to test if you're running under a debugger, -// and if you would like to yield (breakpoint) into the debugger. - -#ifndef BASE_DEBUG_DEBUGGER_H_ -#define BASE_DEBUG_DEBUGGER_H_ - -#include "base/base_export.h" - -namespace base { -namespace debug { - -// 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); - -// Returns true if the given process is being run under a debugger. -// -// On OS X, the underlying mechanism doesn't work when the sandbox is enabled. -// To get around this, this function caches its value. -// -// WARNING: Because of this, on OS X, a call MUST be made to this function -// BEFORE the sandbox is enabled. -BASE_EXPORT bool BeingDebugged(); - -// Break into the debugger, assumes a debugger is present. -BASE_EXPORT void BreakDebugger(); - -// Used in test code, this controls whether showing dialogs and breaking into -// the debugger is suppressed for debug errors, even in debug mode (normally -// release mode doesn't do this stuff -- this is controlled separately). -// Normally UI is not suppressed. This is normally used when running automated -// tests where we want a crash rather than a dialog or a debugger. -BASE_EXPORT void SetSuppressDebugUI(bool suppress); -BASE_EXPORT bool IsDebugUISuppressed(); - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_DEBUGGER_H_ diff --git a/debug/debugger_posix.cc b/debug/debugger_posix.cc deleted file mode 100644 index b62bf013b..000000000 --- a/debug/debugger_posix.cc +++ /dev/null @@ -1,272 +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/debug/debugger.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "base/macros.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" -#include "build/build_config.h" - -#if defined(__GLIBCXX__) -#include -#endif - -#if defined(OS_MACOSX) -#include -#endif - -#if defined(OS_MACOSX) || defined(OS_BSD) -#include -#endif - -#if defined(OS_FREEBSD) -#include -#endif - -#include - -#include "base/debug/alias.h" -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "base/strings/string_piece.h" - -#if defined(USE_SYMBOLIZE) -#include "base/third_party/symbolize/symbolize.h" -#endif - -#if defined(OS_ANDROID) -#include "base/threading/platform_thread.h" -#endif - -namespace base { -namespace debug { - -#if defined(OS_MACOSX) || defined(OS_BSD) - -// Based on Apple's recommended method as described in -// http://developer.apple.com/qa/qa2004/qa1361.html -bool BeingDebugged() { - // 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. - // - // While some code used below may be async-signal unsafe, note how - // the result is cached (see |is_set| and |being_debugged| static variables - // right below). If this code is properly warmed-up early - // in the start-up process, it should be safe to use later. - - // If the process is sandboxed then we can't use the sysctl, so cache the - // value. - static bool is_set = false; - static bool being_debugged = false; - - if (is_set) - return being_debugged; - - // Initialize mib, which tells sysctl what info we want. In this case, - // we're looking for information about a specific process ID. - int mib[] = { - CTL_KERN, - KERN_PROC, - KERN_PROC_PID, - getpid() -#if defined(OS_OPENBSD) - , sizeof(struct kinfo_proc), - 0 -#endif - }; - - // Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE. The source and - // binary interfaces may change. - struct kinfo_proc info; - size_t info_size = sizeof(info); - -#if defined(OS_OPENBSD) - if (sysctl(mib, arraysize(mib), NULL, &info_size, NULL, 0) < 0) - return -1; - - mib[5] = (info_size / sizeof(struct kinfo_proc)); -#endif - - int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0); - DCHECK_EQ(sysctl_result, 0); - if (sysctl_result != 0) { - is_set = true; - being_debugged = false; - return being_debugged; - } - - // This process is being debugged if the P_TRACED flag is set. - is_set = true; -#if defined(OS_FREEBSD) - being_debugged = (info.ki_flag & P_TRACED) != 0; -#elif defined(OS_BSD) - being_debugged = (info.p_flag & P_TRACED) != 0; -#else - being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0; -#endif - return being_debugged; -} - -#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) - -// We can look in /proc/self/status for TracerPid. We are likely used in crash -// handling, so we are careful not to use the heap or have side effects. -// Another option that is common is to try to ptrace yourself, but then we -// can't detach without forking(), and that's not so great. -// static -bool BeingDebugged() { - // 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. - - int status_fd = open("/proc/self/status", O_RDONLY); - if (status_fd == -1) - return false; - - // We assume our line will be in the first 1024 characters and that we can - // read this much all at once. In practice this will generally be true. - // This simplifies and speeds up things considerably. - char buf[1024]; - - ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf))); - if (IGNORE_EINTR(close(status_fd)) < 0) - return false; - - if (num_read <= 0) - return false; - - StringPiece status(buf, num_read); - StringPiece tracer("TracerPid:\t"); - - StringPiece::size_type pid_index = status.find(tracer); - if (pid_index == StringPiece::npos) - return false; - - // Our pid is 0 without a debugger, assume this for any pid starting with 0. - pid_index += tracer.size(); - return pid_index < status.size() && status[pid_index] != '0'; -} - -#elif defined(OS_FUCHSIA) - -bool BeingDebugged() { - // TODO(fuchsia): No gdb/gdbserver in the SDK yet. - return false; -} - -#else - -bool BeingDebugged() { - NOTIMPLEMENTED(); - return false; -} - -#endif - -// We want to break into the debugger in Debug mode, and cause a crash dump in -// Release mode. Breakpad behaves as follows: -// -// +-------+-----------------+-----------------+ -// | OS | Dump on SIGTRAP | Dump on SIGABRT | -// +-------+-----------------+-----------------+ -// | Linux | N | Y | -// | Mac | Y | N | -// +-------+-----------------+-----------------+ -// -// Thus we do the following: -// Linux: Debug mode if a debugger is attached, send SIGTRAP; otherwise send -// SIGABRT -// Mac: Always send SIGTRAP. - -#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) -#define DEBUG_BREAK_ASM() asm("int3") -#endif - -#if defined(NDEBUG) && !defined(OS_MACOSX) && !defined(OS_ANDROID) -#define DEBUG_BREAK() abort() -#elif defined(OS_NACL) -// The NaCl verifier doesn't let use use int3. For now, we call abort(). We -// should ask for advice from some NaCl experts about the optimum thing here. -// http://code.google.com/p/nativeclient/issues/detail?id=645 -#define DEBUG_BREAK() abort() -#elif !defined(OS_MACOSX) -// Though Android has a "helpful" process called debuggerd to catch native -// signals on the general assumption that they are fatal errors. If no debugger -// is attached, we call abort since Breakpad needs SIGABRT to create a dump. -// When debugger is attached, for ARM platform the bkpt instruction appears -// to cause SIGBUS which is trapped by debuggerd, and we've had great -// difficulty continuing in a debugger once we stop from SIG triggered by native -// code, use GDB to set |go| to 1 to resume execution; for X86 platform, use -// "int3" to setup breakpiont and raise SIGTRAP. -// -// On other POSIX architectures, except Mac OS X, we use the same logic to -// ensure that breakpad creates a dump on crashes while it is still possible to -// use a debugger. -namespace { -void DebugBreak() { - if (!BeingDebugged()) { - abort(); - } else { -#if defined(DEBUG_BREAK_ASM) - DEBUG_BREAK_ASM(); -#else - volatile int go = 0; - while (!go) { - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); - } -#endif - } -} -} // namespace -#define DEBUG_BREAK() DebugBreak() -#elif defined(DEBUG_BREAK_ASM) -#define DEBUG_BREAK() DEBUG_BREAK_ASM() -#else -#error "Don't know how to debug break on this architecture/OS" -#endif - -void BreakDebugger() { - // 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. - - // Linker's ICF feature may merge this function with other functions with the - // same definition (e.g. any function whose sole job is to call abort()) and - // it may confuse the crash report processing system. http://crbug.com/508489 - static int static_variable_to_make_this_function_unique = 0; - base::debug::Alias(&static_variable_to_make_this_function_unique); - - DEBUG_BREAK(); -#if defined(OS_ANDROID) && !defined(OFFICIAL_BUILD) - // For Android development we always build release (debug builds are - // unmanageably large), so the unofficial build is used for debugging. It is - // helpful to be able to insert BreakDebugger() statements in the source, - // attach the debugger, inspect the state of the program and then resume it by - // setting the 'go' variable above. -#elif defined(NDEBUG) - // Terminate the program after signaling the debug break. - _exit(1); -#endif -} - -} // namespace debug -} // namespace base diff --git a/debug/debugger_unittest.cc b/debug/debugger_unittest.cc deleted file mode 100644 index 0a5a0390e..000000000 --- a/debug/debugger_unittest.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/debug/debugger.h" - -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) -void CrashWithBreakDebugger() { - base::debug::SetSuppressDebugUI(false); - base::debug::BreakDebugger(); - -#if defined(OS_WIN) - // This should not be executed. - _exit(125); -#endif -} -#endif // defined(GTEST_HAS_DEATH_TEST) - -} // namespace - -// Death tests misbehave on Android. -#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) - -TEST(Debugger, CrashAtBreakpoint) { - EXPECT_DEATH(CrashWithBreakDebugger(), ""); -} - -#if defined(OS_WIN) -TEST(Debugger, DoesntExecuteBeyondBreakpoint) { - EXPECT_EXIT(CrashWithBreakDebugger(), - ::testing::ExitedWithCode(0x80000003), ""); -} -#endif // defined(OS_WIN) - -#else // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) -TEST(Debugger, NoTest) { -} -#endif // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) diff --git a/debug/debugger_win.cc b/debug/debugger_win.cc deleted file mode 100644 index a1d86e445..000000000 --- a/debug/debugger_win.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/debug/debugger.h" - -#include -#include - -namespace base { -namespace debug { - -bool BeingDebugged() { - return ::IsDebuggerPresent() != 0; -} - -void BreakDebugger() { - if (IsDebugUISuppressed()) - _exit(1); - - __debugbreak(); -} - -} // namespace debug -} // namespace base diff --git a/debug/dump_without_crashing.cc b/debug/dump_without_crashing.cc deleted file mode 100644 index 1ab8c9cc4..000000000 --- a/debug/dump_without_crashing.cc +++ /dev/null @@ -1,41 +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/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_)() = nullptr; - -} // namespace - -namespace base { - -namespace debug { - -bool DumpWithoutCrashing() { - if (dump_without_crashing_function_) { - (*dump_without_crashing_function_)(); - return true; - } - return false; -} - -void SetDumpWithoutCrashingFunction(void (CDECL *function)()) { -#if !defined(COMPONENT_BUILD) - // In component builds, the same base is shared between modules - // so might be initialized several times. However in non- - // component builds this should never happen. - DCHECK(!dump_without_crashing_function_); -#endif - dump_without_crashing_function_ = function; -} - -} // namespace debug - -} // namespace base diff --git a/debug/dump_without_crashing.h b/debug/dump_without_crashing.h deleted file mode 100644 index 913f6c428..000000000 --- a/debug/dump_without_crashing.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. - -#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. -// Windows: -// This must be done for each instance of base (i.e. module) and is normally -// chrome_elf!DumpProcessWithoutCrash. See example code in chrome_main.cc that -// does this for chrome.dll and chrome_child.dll. Note: Crashpad sets this up -// for main chrome.exe as part of calling crash_reporter::InitializeCrashpad. -// Mac/Linux: -// Crashpad does this as part of crash_reporter::InitializeCrashpad. -// Returns false if called before SetDumpWithoutCrashingFunction. -BASE_EXPORT bool 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/debug/elf_reader_linux.cc b/debug/elf_reader_linux.cc deleted file mode 100644 index cdf8193ff..000000000 --- a/debug/elf_reader_linux.cc +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2018 The Chromium 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/elf_reader_linux.h" - -#include -#include - -#include - -#include "base/bits.h" -#include "base/containers/span.h" -#include "base/sha1.h" -#include "base/strings/stringprintf.h" - -namespace base { -namespace debug { - -namespace { - -#if __SIZEOF_POINTER__ == 4 -using Ehdr = Elf32_Ehdr; -using Dyn = Elf32_Dyn; -using Half = Elf32_Half; -using Nhdr = Elf32_Nhdr; -using Phdr = Elf32_Phdr; -using Word = Elf32_Word; -#else -using Ehdr = Elf64_Ehdr; -using Dyn = Elf64_Dyn; -using Half = Elf64_Half; -using Nhdr = Elf64_Nhdr; -using Phdr = Elf64_Phdr; -using Word = Elf64_Word; -#endif - -using ElfSegment = span; - -Optional ElfSegmentBuildIDNoteAsString(const ElfSegment& segment) { - const void* section_end = segment.data() + segment.size_bytes(); - const Nhdr* note_header = reinterpret_cast(segment.data()); - while (note_header < section_end) { - if (note_header->n_type == NT_GNU_BUILD_ID) - break; - note_header = reinterpret_cast( - reinterpret_cast(note_header) + sizeof(Nhdr) + - bits::Align(note_header->n_namesz, 4) + - bits::Align(note_header->n_descsz, 4)); - } - - if (note_header >= section_end || note_header->n_descsz != kSHA1Length) - return nullopt; - - const uint8_t* guid = reinterpret_cast(note_header) + - sizeof(Nhdr) + bits::Align(note_header->n_namesz, 4); - - uint32_t dword = htonl(*reinterpret_cast(guid)); - uint16_t word1 = htons(*reinterpret_cast(guid + 4)); - uint16_t word2 = htons(*reinterpret_cast(guid + 6)); - std::string identifier; - identifier.reserve(kSHA1Length * 2); // as hex string - SStringPrintf(&identifier, "%08X%04X%04X", dword, word1, word2); - for (size_t i = 8; i < note_header->n_descsz; ++i) - StringAppendF(&identifier, "%02X", guid[i]); - - return identifier; -} - -std::vector FindElfSegments(const void* elf_mapped_base, - uint32_t segment_type) { - const char* elf_base = reinterpret_cast(elf_mapped_base); - if (strncmp(elf_base, ELFMAG, SELFMAG) != 0) - return std::vector(); - - const Ehdr* elf_header = reinterpret_cast(elf_base); - const Phdr* phdrs = - reinterpret_cast(elf_base + elf_header->e_phoff); - std::vector segments; - for (Half i = 0; i < elf_header->e_phnum; ++i) { - if (phdrs[i].p_type == segment_type) - segments.push_back({elf_base + phdrs[i].p_offset, phdrs[i].p_filesz}); - } - return segments; -} - -} // namespace - -Optional ReadElfBuildId(const void* elf_base) { - // Elf program headers can have multiple PT_NOTE arrays. - std::vector segs = FindElfSegments(elf_base, PT_NOTE); - if (segs.empty()) - return nullopt; - Optional id; - for (const ElfSegment& seg : segs) { - id = ElfSegmentBuildIDNoteAsString(seg); - if (id) - return id; - } - - return nullopt; -} - -Optional ReadElfLibraryName(const void* elf_base) { - std::vector segs = FindElfSegments(elf_base, PT_DYNAMIC); - if (segs.empty()) - return nullopt; - DCHECK_EQ(1u, segs.size()); - - const ElfSegment& dynamic_seg = segs.front(); - const Dyn* dynamic_start = reinterpret_cast(dynamic_seg.data()); - const Dyn* dynamic_end = reinterpret_cast( - dynamic_seg.data() + dynamic_seg.size_bytes()); - Optional soname; - Word soname_strtab_offset = 0; - const char* strtab_addr = 0; - for (const Dyn* dynamic_iter = dynamic_start; dynamic_iter < dynamic_end; - ++dynamic_iter) { - if (dynamic_iter->d_tag == DT_STRTAB) { - strtab_addr = - dynamic_iter->d_un.d_ptr + reinterpret_cast(elf_base); - } else if (dynamic_iter->d_tag == DT_SONAME) { - soname_strtab_offset = dynamic_iter->d_un.d_val; - } - } - if (soname_strtab_offset && strtab_addr) - return std::string(strtab_addr + soname_strtab_offset); - return nullopt; -} - -} // namespace debug -} // namespace base diff --git a/debug/elf_reader_linux.h b/debug/elf_reader_linux.h deleted file mode 100644 index 4086dfbe5..000000000 --- a/debug/elf_reader_linux.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018 The Chromium 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_ELF_READER_LINUX_H_ -#define BASE_DEBUG_ELF_READER_LINUX_H_ - -#include - -#include "base/base_export.h" -#include "base/optional.h" - -namespace base { -namespace debug { - -// Returns the ELF section .note.gnu.build-id from the ELF file mapped at -// |elf_base|, if present. The caller must ensure that the file is fully mapped -// in memory. -Optional BASE_EXPORT ReadElfBuildId(const void* elf_base); - -// Returns the library name from the ELF file mapped at |elf_base|, if present. -// The caller must ensure that the file is fully mapped in memory. -Optional BASE_EXPORT ReadElfLibraryName(const void* elf_base); - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_ELF_READER_LINUX_H_ diff --git a/debug/elf_reader_linux_unittest.cc b/debug/elf_reader_linux_unittest.cc deleted file mode 100644 index 5510418d0..000000000 --- a/debug/elf_reader_linux_unittest.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2018 The Chromium 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/elf_reader_linux.h" - -#include - -#include "base/files/memory_mapped_file.h" -#include "base/strings/string_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -extern char __executable_start; - -namespace base { -namespace debug { - -// The linker flag --build-id is passed only on official builds. Clang does not -// enable it by default and we do not have build id section in non-official -// builds. -#if defined(OFFICIAL_BUILD) -TEST(ElfReaderTest, ReadElfBuildId) { - Optional build_id = ReadElfBuildId(&__executable_start); - ASSERT_TRUE(build_id); - const size_t kGuidBytes = 20; - EXPECT_EQ(2 * kGuidBytes, build_id.value().size()); - for (char c : *build_id) { - EXPECT_TRUE(IsHexDigit(c)); - EXPECT_FALSE(IsAsciiLower(c)); - } -} -#endif - -TEST(ElfReaderTest, ReadElfLibraryName) { -#if defined(OS_ANDROID) - // On Android the library loader memory maps the full so file. - const char kLibraryName[] = "lib_base_unittests__library"; - const void* addr = &__executable_start; -#else - // On Linux the executable does not contain soname and is not mapped till - // dynamic segment. So, use malloc wrapper so file on which the test already - // depends on. - const char kLibraryName[] = MALLOC_WRAPPER_LIB; - // Find any symbol in the loaded file. - void* handle = dlopen(kLibraryName, RTLD_NOW | RTLD_LOCAL); - const void* init_addr = dlsym(handle, "_init"); - // Use this symbol to get full path to the loaded library. - Dl_info info; - int res = dladdr(init_addr, &info); - ASSERT_NE(0, res); - std::string filename(info.dli_fname); - EXPECT_FALSE(filename.empty()); - EXPECT_NE(std::string::npos, filename.find(kLibraryName)); - - // Memory map the so file and use it to test reading so name. - MemoryMappedFile file; - file.Initialize(FilePath(filename)); - const void* addr = file.data(); -#endif - - auto name = ReadElfLibraryName(addr); - ASSERT_TRUE(name); - EXPECT_NE(std::string::npos, name->find(kLibraryName)) - << "Library name " << *name << " doesn't contain expected " - << kLibraryName; -} - -} // namespace debug -} // namespace base diff --git a/debug/gdi_debug_util_win.cc b/debug/gdi_debug_util_win.cc deleted file mode 100644 index bf9827c57..000000000 --- a/debug/gdi_debug_util_win.cc +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -#include "base/debug/gdi_debug_util_win.h" - -#include -#include - -#include -#include -#include - -#include "base/debug/alias.h" -#include "base/logging.h" -#include "base/win/scoped_handle.h" -#include "base/win/win_util.h" - -namespace { - -void CollectChildGDIUsageAndDie(DWORD parent_pid) { - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - CHECK_NE(INVALID_HANDLE_VALUE, snapshot); - - int total_process_count = 0; - base::debug::Alias(&total_process_count); - int total_peak_gdi_count = 0; - base::debug::Alias(&total_peak_gdi_count); - int total_gdi_count = 0; - base::debug::Alias(&total_gdi_count); - int total_user_count = 0; - base::debug::Alias(&total_user_count); - - 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); - CHECK(Process32First(snapshot, &proc_entry)); - - do { - base::win::ScopedHandle process( - OpenProcess(PROCESS_QUERY_INFORMATION, - FALSE, - proc_entry.th32ProcessID)); - if (!process.IsValid()) - continue; - - int num_gdi_handles = GetGuiResources(process.Get(), GR_GDIOBJECTS); - int num_user_handles = GetGuiResources(process.Get(), GR_USEROBJECTS); - - // Compute sum and peak counts for all processes. - ++total_process_count; - total_user_count += num_user_handles; - total_gdi_count += num_gdi_handles; - total_peak_gdi_count = std::max(total_peak_gdi_count, num_gdi_handles); - - if (parent_pid != proc_entry.th32ParentProcessID) - continue; - - // Compute sum and peak counts for child processes. - ++child_count; - sum_user_count += num_user_handles; - sum_gdi_count += num_gdi_handles; - peak_gdi_count = std::max(peak_gdi_count, num_gdi_handles); - - } while (Process32Next(snapshot, &proc_entry)); - - CloseHandle(snapshot); - CHECK(false); -} - -} // namespace - -namespace base { -namespace debug { - -void CollectGDIUsageAndDie(BITMAPINFOHEADER* header, HANDLE shared_section) { - // Make sure parameters are saved in the minidump. - DWORD last_error = GetLastError(); - bool is_gdi_available = base::win::IsUser32AndGdi32Available(); - - LONG width = header ? header->biWidth : 0; - LONG height = header ? header->biHeight : 0; - - base::debug::Alias(&last_error); - base::debug::Alias(&is_gdi_available); - base::debug::Alias(&width); - base::debug::Alias(&height); - base::debug::Alias(&shared_section); - - DWORD num_user_handles = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS); - - DWORD 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; - CHECK_LE(num_gdi_handles, kLotsOfHandles); - - PROCESS_MEMORY_COUNTERS_EX pmc; - pmc.cb = sizeof(pmc); - CHECK(GetProcessMemoryInfo(GetCurrentProcess(), - reinterpret_cast(&pmc), - sizeof(pmc))); - const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB - CHECK_LE(pmc.PagefileUsage, kLotsOfMemory); - CHECK_LE(pmc.PrivateUsage, kLotsOfMemory); - - void* small_data = nullptr; - base::debug::Alias(&small_data); - - if (std::abs(height) * 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( - nullptr, reinterpret_cast(&header), - 0, &small_data, shared_section, 0); - CHECK(small_bitmap != nullptr); - DeleteObject(small_bitmap); - } - // Maybe the child processes are the ones leaking GDI or USER resouces. - CollectChildGDIUsageAndDie(GetCurrentProcessId()); -} - -} // namespace debug -} // namespace base diff --git a/debug/gdi_debug_util_win.h b/debug/gdi_debug_util_win.h deleted file mode 100644 index 3383a4d52..000000000 --- a/debug/gdi_debug_util_win.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_ -#define BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_ - -#include - -#include "base/base_export.h" - -namespace base { -namespace debug { - -// Crashes the process, using base::debug::Alias to leave valuable debugging -// information in the crash dump. Pass values for |header| and |shared_section| -// in the event of a bitmap allocation failure, to gather information about -// those as well. -void BASE_EXPORT CollectGDIUsageAndDie(BITMAPINFOHEADER* header = nullptr, - HANDLE shared_section = nullptr); - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_ diff --git a/debug/leak_annotations.h b/debug/leak_annotations.h deleted file mode 100644 index dc502461d..000000000 --- a/debug/leak_annotations.h +++ /dev/null @@ -1,46 +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_DEBUG_LEAK_ANNOTATIONS_H_ -#define BASE_DEBUG_LEAK_ANNOTATIONS_H_ - -#include "base/macros.h" -#include "build/build_config.h" - -// This file defines macros which can be used to annotate intentional memory -// leaks. Support for annotations is implemented in LeakSanitizer. Annotated -// objects will be treated as a source of live pointers, i.e. any heap objects -// reachable by following pointers from an annotated object will not be -// reported as leaks. -// -// ANNOTATE_SCOPED_MEMORY_LEAK: all allocations made in the current scope -// will be annotated as leaks. -// ANNOTATE_LEAKING_OBJECT_PTR(X): the heap object referenced by pointer X will -// be annotated as a leak. - -#if defined(LEAK_SANITIZER) && !defined(OS_NACL) - -#include - -class ScopedLeakSanitizerDisabler { - public: - ScopedLeakSanitizerDisabler() { __lsan_disable(); } - ~ScopedLeakSanitizerDisabler() { __lsan_enable(); } - private: - DISALLOW_COPY_AND_ASSIGN(ScopedLeakSanitizerDisabler); -}; - -#define ANNOTATE_SCOPED_MEMORY_LEAK \ - ScopedLeakSanitizerDisabler leak_sanitizer_disabler; static_cast(0) - -#define ANNOTATE_LEAKING_OBJECT_PTR(X) __lsan_ignore_object(X); - -#else - -#define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0) -#define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)0) - -#endif - -#endif // BASE_DEBUG_LEAK_ANNOTATIONS_H_ diff --git a/debug/leak_tracker.h b/debug/leak_tracker.h deleted file mode 100644 index 7ddd5b62d..000000000 --- a/debug/leak_tracker.h +++ /dev/null @@ -1,142 +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_DEBUG_LEAK_TRACKER_H_ -#define BASE_DEBUG_LEAK_TRACKER_H_ - -#include - -#include "build/build_config.h" - -// Only enable leak tracking in non-uClibc debug builds. -#if !defined(NDEBUG) && !defined(__UCLIBC__) -#define ENABLE_LEAK_TRACKER -#endif - -#ifdef ENABLE_LEAK_TRACKER -#include "base/containers/linked_list.h" -#include "base/debug/stack_trace.h" -#include "base/logging.h" -#endif // ENABLE_LEAK_TRACKER - -// LeakTracker is a helper to verify that all instances of a class -// have been destroyed. -// -// It is particularly useful for classes that are bound to a single thread -- -// before destroying that thread, one can check that there are no remaining -// instances of that class. -// -// For example, to enable leak tracking for class net::URLRequest, start by -// adding a member variable of type LeakTracker. -// -// class URLRequest { -// ... -// private: -// base::LeakTracker leak_tracker_; -// }; -// -// -// Next, when we believe all instances of net::URLRequest have been deleted: -// -// LeakTracker::CheckForLeaks(); -// -// Should the check fail (because there are live instances of net::URLRequest), -// then the allocation callstack for each leaked instances is dumped to -// the error log. -// -// If ENABLE_LEAK_TRACKER is not defined, then the check has no effect. - -namespace base { -namespace debug { - -#ifndef ENABLE_LEAK_TRACKER - -// If leak tracking is disabled, do nothing. -template -class LeakTracker { - public: - // This destructor suppresses warnings about instances of this class not being - // used. - ~LeakTracker() {} - static void CheckForLeaks() {} - static int NumLiveInstances() { return -1; } -}; - -#else - -// If leak tracking is enabled we track where the object was allocated from. - -template -class LeakTracker : public LinkNode > { - public: - LeakTracker() { - instances()->Append(this); - } - - ~LeakTracker() { - this->RemoveFromList(); - } - - static void CheckForLeaks() { - // Walk the allocation list and print each entry it contains. - size_t count = 0; - - // Copy the first 3 leak allocation callstacks onto the stack. - // This way if we hit the CHECK() in a release build, the leak - // information will be available in mini-dump. - const size_t kMaxStackTracesToCopyOntoStack = 3; - StackTrace stacktraces[kMaxStackTracesToCopyOntoStack]; - - for (LinkNode >* node = instances()->head(); - node != instances()->end(); - node = node->next()) { - StackTrace& allocation_stack = node->value()->allocation_stack_; - - if (count < kMaxStackTracesToCopyOntoStack) - stacktraces[count] = allocation_stack; - - ++count; - if (LOG_IS_ON(ERROR)) { - LOG_STREAM(ERROR) << "Leaked " << node << " which was allocated by:"; - allocation_stack.OutputToStream(&LOG_STREAM(ERROR)); - } - } - - CHECK_EQ(0u, count); - - // Hack to keep |stacktraces| and |count| alive (so compiler - // doesn't optimize it out, and it will appear in mini-dumps). - if (count == 0x1234) { - for (size_t i = 0; i < kMaxStackTracesToCopyOntoStack; ++i) - stacktraces[i].Print(); - } - } - - static int NumLiveInstances() { - // Walk the allocation list and count how many entries it has. - int count = 0; - for (LinkNode >* node = instances()->head(); - node != instances()->end(); - node = node->next()) { - ++count; - } - return count; - } - - private: - // Each specialization of LeakTracker gets its own static storage. - static LinkedList >* instances() { - static LinkedList > list; - return &list; - } - - StackTrace allocation_stack_; -}; - -#endif // ENABLE_LEAK_TRACKER - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_LEAK_TRACKER_H_ diff --git a/debug/leak_tracker_unittest.cc b/debug/leak_tracker_unittest.cc deleted file mode 100644 index b9ecdcf3c..000000000 --- a/debug/leak_tracker_unittest.cc +++ /dev/null @@ -1,115 +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/leak_tracker.h" - -#include - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace debug { - -namespace { - -class ClassA { - private: - LeakTracker leak_tracker_; -}; - -class ClassB { - private: - LeakTracker leak_tracker_; -}; - -#ifndef ENABLE_LEAK_TRACKER - -// If leak tracking is disabled, we should do nothing. -TEST(LeakTrackerTest, NotEnabled) { - EXPECT_EQ(-1, LeakTracker::NumLiveInstances()); - EXPECT_EQ(-1, LeakTracker::NumLiveInstances()); - - // Use unique_ptr so compiler doesn't complain about unused variables. - std::unique_ptr a1(new ClassA); - std::unique_ptr b1(new ClassB); - std::unique_ptr b2(new ClassB); - - EXPECT_EQ(-1, LeakTracker::NumLiveInstances()); - EXPECT_EQ(-1, LeakTracker::NumLiveInstances()); -} - -#else - -TEST(LeakTrackerTest, Basic) { - { - ClassA a1; - - EXPECT_EQ(1, LeakTracker::NumLiveInstances()); - EXPECT_EQ(0, LeakTracker::NumLiveInstances()); - - ClassB b1; - ClassB b2; - - EXPECT_EQ(1, LeakTracker::NumLiveInstances()); - EXPECT_EQ(2, LeakTracker::NumLiveInstances()); - - std::unique_ptr a2(new ClassA); - - EXPECT_EQ(2, LeakTracker::NumLiveInstances()); - EXPECT_EQ(2, LeakTracker::NumLiveInstances()); - - a2.reset(); - - EXPECT_EQ(1, LeakTracker::NumLiveInstances()); - EXPECT_EQ(2, LeakTracker::NumLiveInstances()); - } - - EXPECT_EQ(0, LeakTracker::NumLiveInstances()); - EXPECT_EQ(0, LeakTracker::NumLiveInstances()); -} - -// Try some orderings of create/remove to hit different cases in the linked-list -// assembly. -TEST(LeakTrackerTest, LinkedList) { - EXPECT_EQ(0, LeakTracker::NumLiveInstances()); - - std::unique_ptr a1(new ClassA); - std::unique_ptr a2(new ClassA); - std::unique_ptr a3(new ClassA); - std::unique_ptr a4(new ClassA); - - EXPECT_EQ(4, LeakTracker::NumLiveInstances()); - - // Remove the head of the list (a1). - a1.reset(); - EXPECT_EQ(3, LeakTracker::NumLiveInstances()); - - // Remove the tail of the list (a4). - a4.reset(); - EXPECT_EQ(2, LeakTracker::NumLiveInstances()); - - // Append to the new tail of the list (a3). - std::unique_ptr a5(new ClassA); - EXPECT_EQ(3, LeakTracker::NumLiveInstances()); - - a2.reset(); - a3.reset(); - - EXPECT_EQ(1, LeakTracker::NumLiveInstances()); - - a5.reset(); - EXPECT_EQ(0, LeakTracker::NumLiveInstances()); -} - -TEST(LeakTrackerTest, NoOpCheckForLeaks) { - // There are no live instances of ClassA, so this should do nothing. - LeakTracker::CheckForLeaks(); -} - -#endif // ENABLE_LEAK_TRACKER - -} // namespace - -} // namespace debug -} // namespace base diff --git a/debug/proc_maps_linux.cc b/debug/proc_maps_linux.cc deleted file mode 100644 index 0bb44b45a..000000000 --- a/debug/proc_maps_linux.cc +++ /dev/null @@ -1,169 +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/debug/proc_maps_linux.h" - -#include -#include - -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/strings/string_split.h" -#include "build/build_config.h" - -#if defined(OS_LINUX) || defined(OS_ANDROID) -#include -#endif - -#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" -#endif - -namespace base { -namespace debug { - -// Scans |proc_maps| starting from |pos| returning true if the gate VMA was -// found, otherwise returns false. -static bool ContainsGateVMA(std::string* proc_maps, size_t pos) { -#if defined(ARCH_CPU_ARM_FAMILY) - // The gate VMA on ARM kernels is the interrupt vectors page. - return proc_maps->find(" [vectors]\n", pos) != std::string::npos; -#elif defined(ARCH_CPU_X86_64) - // The gate VMA on x86 64-bit kernels is the virtual system call page. - return proc_maps->find(" [vsyscall]\n", pos) != std::string::npos; -#else - // Otherwise assume there is no gate VMA in which case we shouldn't - // get duplicate entires. - return false; -#endif -} - -bool ReadProcMaps(std::string* proc_maps) { - // seq_file only writes out a page-sized amount on each call. Refer to header - // file for details. - const long kReadSize = sysconf(_SC_PAGESIZE); - - 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; - } - proc_maps->clear(); - - while (true) { - // To avoid a copy, resize |proc_maps| so read() can write directly into it. - // Compute |buffer| afterwards since resize() may reallocate. - size_t pos = proc_maps->size(); - proc_maps->resize(pos + kReadSize); - void* buffer = &(*proc_maps)[pos]; - - 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(); - return false; - } - - // ... and don't forget to trim off excess bytes. - proc_maps->resize(pos + bytes_read); - - if (bytes_read == 0) - break; - - // The gate VMA is handled as a special case after seq_file has finished - // iterating through all entries in the virtual memory table. - // - // Unfortunately, if additional entries are added at this point in time - // seq_file gets confused and the next call to read() will return duplicate - // entries including the gate VMA again. - // - // Avoid this by searching for the gate VMA and breaking early. - if (ContainsGateVMA(proc_maps, pos)) - break; - } - - return true; -} - -bool ParseProcMaps(const std::string& input, - std::vector* regions_out) { - CHECK(regions_out); - std::vector regions; - - // This isn't async safe nor terribly efficient, but it doesn't need to be at - // this point in time. - std::vector lines = SplitString( - input, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); - - 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()) { - DLOG(WARNING) << "Last line not empty"; - return false; - } - break; - } - - MappedMemoryRegion region; - const char* line = lines[i].c_str(); - char permissions[5] = {'\0'}; // Ensure NUL-terminated string. - uint8_t dev_major = 0; - uint8_t dev_minor = 0; - long inode = 0; - int path_index = 0; - - // Sample format from man 5 proc: - // - // address perms offset dev inode pathname - // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm - // - // The final %n term captures the offset in the input string, which is used - // to determine the path name. It *does not* increment the return value. - // Refer to man 3 sscanf for details. - 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; - } - - region.permissions = 0; - - if (permissions[0] == 'r') - region.permissions |= MappedMemoryRegion::READ; - else if (permissions[0] != '-') - return false; - - if (permissions[1] == 'w') - region.permissions |= MappedMemoryRegion::WRITE; - else if (permissions[1] != '-') - return false; - - if (permissions[2] == 'x') - region.permissions |= MappedMemoryRegion::EXECUTE; - else if (permissions[2] != '-') - return false; - - if (permissions[3] == 'p') - region.permissions |= MappedMemoryRegion::PRIVATE; - else if (permissions[3] != 's' && permissions[3] != 'S') // Shared memory. - return false; - - // Pushing then assigning saves us a string copy. - regions.push_back(region); - regions.back().path.assign(line + path_index); - } - - regions_out->swap(regions); - return true; -} - -} // namespace debug -} // namespace base diff --git a/debug/proc_maps_linux.h b/debug/proc_maps_linux.h deleted file mode 100644 index f5f8a59b8..000000000 --- a/debug/proc_maps_linux.h +++ /dev/null @@ -1,94 +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_DEBUG_PROC_MAPS_LINUX_H_ -#define BASE_DEBUG_PROC_MAPS_LINUX_H_ - -#include - -#include -#include - -#include "base/base_export.h" - -namespace base { -namespace debug { - -// Describes a region of mapped memory and the path of the file mapped. -struct MappedMemoryRegion { - enum Permission { - READ = 1 << 0, - WRITE = 1 << 1, - EXECUTE = 1 << 2, - PRIVATE = 1 << 3, // If set, region is private, otherwise it is shared. - }; - - // The address range [start,end) of mapped memory. - uintptr_t start; - uintptr_t end; - - // Byte offset into |path| of the range mapped into memory. - unsigned long long offset; - - // Image base, if this mapping corresponds to an ELF image. - uintptr_t base; - - // Bitmask of read/write/execute/private/shared permissions. - uint8_t permissions; - - // Name of the file mapped into memory. - // - // NOTE: path names aren't guaranteed to point at valid files. For example, - // "[heap]" and "[stack]" are used to represent the location of the process' - // heap and stack, respectively. - std::string path; -}; - -// Reads the data from /proc/self/maps and stores the result in |proc_maps|. -// Returns true if successful, false otherwise. -// -// There is *NO* guarantee that the resulting contents will be free of -// duplicates or even contain valid entries by time the method returns. -// -// -// THE GORY DETAILS -// -// Did you know it's next-to-impossible to atomically read the whole contents -// of /proc//maps? You would think that if we passed in a large-enough -// buffer to read() that It Should Just Work(tm), but sadly that's not the case. -// -// Linux's procfs uses seq_file [1] for handling iteration, text formatting, -// and dealing with resulting data that is larger than the size of a page. That -// last bit is especially important because it means that seq_file will never -// return more than the size of a page in a single call to read(). -// -// Unfortunately for a program like Chrome the size of /proc/self/maps is -// larger than the size of page so we're forced to call read() multiple times. -// If the virtual memory table changed in any way between calls to read() (e.g., -// a different thread calling mprotect()), it can make seq_file generate -// duplicate entries or skip entries. -// -// Even if seq_file was changed to keep flushing the contents of its page-sized -// buffer to the usermode buffer inside a single call to read(), it has to -// release its lock on the virtual memory table to handle page faults while -// copying data to usermode. This puts us in the same situation where the table -// can change while we're copying data. -// -// Alternatives such as fork()-and-suspend-the-parent-while-child-reads were -// attempted, but they present more subtle problems than it's worth. Depending -// on your use case your best bet may be to read /proc//maps prior to -// starting other threads. -// -// [1] http://kernelnewbies.org/Documents/SeqFileHowTo -BASE_EXPORT bool ReadProcMaps(std::string* proc_maps); - -// Parses /proc//maps input data and stores in |regions|. Returns true -// and updates |regions| if and only if all of |input| was successfully parsed. -BASE_EXPORT bool ParseProcMaps(const std::string& input, - std::vector* regions); - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_PROC_MAPS_LINUX_H_ diff --git a/debug/proc_maps_linux_unittest.cc b/debug/proc_maps_linux_unittest.cc deleted file mode 100644 index 7abf152b0..000000000 --- a/debug/proc_maps_linux_unittest.cc +++ /dev/null @@ -1,328 +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 -#include - -#include "base/debug/proc_maps_linux.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/path_service.h" -#include "base/strings/stringprintf.h" -#include "base/threading/platform_thread.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace debug { - -TEST(ProcMapsTest, Empty) { - std::vector regions; - EXPECT_TRUE(ParseProcMaps("", ®ions)); - EXPECT_EQ(0u, regions.size()); -} - -TEST(ProcMapsTest, NoSpaces) { - static const char kNoSpaces[] = - "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat\n"; - - std::vector regions; - ASSERT_TRUE(ParseProcMaps(kNoSpaces, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0x00400000u, regions[0].start); - EXPECT_EQ(0x0040b000u, regions[0].end); - EXPECT_EQ(0x00002200u, regions[0].offset); - EXPECT_EQ("/bin/cat", regions[0].path); -} - -TEST(ProcMapsTest, Spaces) { - static const char kSpaces[] = - "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/space cat\n"; - - std::vector regions; - ASSERT_TRUE(ParseProcMaps(kSpaces, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0x00400000u, regions[0].start); - EXPECT_EQ(0x0040b000u, regions[0].end); - EXPECT_EQ(0x00002200u, regions[0].offset); - EXPECT_EQ("/bin/space cat", regions[0].path); -} - -TEST(ProcMapsTest, NoNewline) { - static const char kNoSpaces[] = - "00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat"; - - std::vector regions; - ASSERT_FALSE(ParseProcMaps(kNoSpaces, ®ions)); -} - -TEST(ProcMapsTest, NoPath) { - static const char kNoPath[] = - "00400000-0040b000 rw-p 00000000 00:00 0 \n"; - - std::vector regions; - ASSERT_TRUE(ParseProcMaps(kNoPath, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0x00400000u, regions[0].start); - EXPECT_EQ(0x0040b000u, regions[0].end); - EXPECT_EQ(0x00000000u, regions[0].offset); - EXPECT_EQ("", regions[0].path); -} - -TEST(ProcMapsTest, Heap) { - static const char kHeap[] = - "022ac000-022cd000 rw-p 00000000 00:00 0 [heap]\n"; - - std::vector regions; - ASSERT_TRUE(ParseProcMaps(kHeap, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0x022ac000u, regions[0].start); - EXPECT_EQ(0x022cd000u, regions[0].end); - EXPECT_EQ(0x00000000u, regions[0].offset); - EXPECT_EQ("[heap]", regions[0].path); -} - -#if defined(ARCH_CPU_32_BITS) -TEST(ProcMapsTest, Stack32) { - static const char kStack[] = - "beb04000-beb25000 rw-p 00000000 00:00 0 [stack]\n"; - - std::vector regions; - ASSERT_TRUE(ParseProcMaps(kStack, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0xbeb04000u, regions[0].start); - EXPECT_EQ(0xbeb25000u, regions[0].end); - EXPECT_EQ(0x00000000u, regions[0].offset); - EXPECT_EQ("[stack]", regions[0].path); -} -#elif defined(ARCH_CPU_64_BITS) -TEST(ProcMapsTest, Stack64) { - static const char kStack[] = - "7fff69c5b000-7fff69c7d000 rw-p 00000000 00:00 0 [stack]\n"; - - std::vector regions; - ASSERT_TRUE(ParseProcMaps(kStack, ®ions)); - ASSERT_EQ(1u, regions.size()); - - EXPECT_EQ(0x7fff69c5b000u, regions[0].start); - EXPECT_EQ(0x7fff69c7d000u, regions[0].end); - EXPECT_EQ(0x00000000u, regions[0].offset); - EXPECT_EQ("[stack]", regions[0].path); -} -#endif - -TEST(ProcMapsTest, Multiple) { - static const char kMultiple[] = - "00400000-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n" - "0060a000-0060b000 r--p 0000a000 fc:00 794418 /bin/cat\n" - "0060b000-0060c000 rw-p 0000b000 fc:00 794418 /bin/cat\n"; - - std::vector regions; - ASSERT_TRUE(ParseProcMaps(kMultiple, ®ions)); - ASSERT_EQ(3u, regions.size()); - - EXPECT_EQ(0x00400000u, regions[0].start); - EXPECT_EQ(0x0040b000u, regions[0].end); - EXPECT_EQ(0x00000000u, regions[0].offset); - EXPECT_EQ("/bin/cat", regions[0].path); - - EXPECT_EQ(0x0060a000u, regions[1].start); - EXPECT_EQ(0x0060b000u, regions[1].end); - EXPECT_EQ(0x0000a000u, regions[1].offset); - EXPECT_EQ("/bin/cat", regions[1].path); - - EXPECT_EQ(0x0060b000u, regions[2].start); - EXPECT_EQ(0x0060c000u, regions[2].end); - EXPECT_EQ(0x0000b000u, regions[2].offset); - EXPECT_EQ("/bin/cat", regions[2].path); -} - -TEST(ProcMapsTest, Permissions) { - static struct { - const char* input; - uint8_t permissions; - } kTestCases[] = { - {"00400000-0040b000 ---s 00000000 fc:00 794418 /bin/cat\n", 0}, - {"00400000-0040b000 ---S 00000000 fc:00 794418 /bin/cat\n", 0}, - {"00400000-0040b000 r--s 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::READ}, - {"00400000-0040b000 -w-s 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::WRITE}, - {"00400000-0040b000 --xs 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::EXECUTE}, - {"00400000-0040b000 rwxs 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::READ | MappedMemoryRegion::WRITE | - MappedMemoryRegion::EXECUTE}, - {"00400000-0040b000 ---p 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::PRIVATE}, - {"00400000-0040b000 r--p 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::READ | MappedMemoryRegion::PRIVATE}, - {"00400000-0040b000 -w-p 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::WRITE | MappedMemoryRegion::PRIVATE}, - {"00400000-0040b000 --xp 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE}, - {"00400000-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n", - MappedMemoryRegion::READ | MappedMemoryRegion::WRITE | - MappedMemoryRegion::EXECUTE | MappedMemoryRegion::PRIVATE}, - }; - - for (size_t i = 0; i < arraysize(kTestCases); ++i) { - SCOPED_TRACE( - base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i].input)); - - std::vector regions; - EXPECT_TRUE(ParseProcMaps(kTestCases[i].input, ®ions)); - EXPECT_EQ(1u, regions.size()); - if (regions.empty()) - continue; - EXPECT_EQ(kTestCases[i].permissions, regions[0].permissions); - } -} - -#if defined(ADDRESS_SANITIZER) -// AddressSanitizer may move local variables to a dedicated "fake stack" which -// is outside the stack region listed in /proc/self/maps. We disable ASan -// instrumentation for this function to force the variable to be local. -__attribute__((no_sanitize_address)) -#endif -void CheckProcMapsRegions(const std::vector ®ions) { - // We should be able to find both the current executable as well as the stack - // mapped into memory. Use the address of |exe_path| as a way of finding the - // stack. - FilePath exe_path; - EXPECT_TRUE(PathService::Get(FILE_EXE, &exe_path)); - uintptr_t address = reinterpret_cast(&exe_path); - bool found_exe = false; - bool found_stack = false; - bool found_address = false; - - for (size_t i = 0; i < regions.size(); ++i) { - if (regions[i].path == exe_path.value()) { - // It's OK to find the executable mapped multiple times as there'll be - // multiple sections (e.g., text, data). - found_exe = true; - } - - if (regions[i].path == "[stack]") { -// On Android the test is run on a background thread, since [stack] is for -// the main thread, we cannot test this. -#if !defined(OS_ANDROID) - EXPECT_GE(address, regions[i].start); - EXPECT_LT(address, regions[i].end); -#endif - EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::READ); - EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::WRITE); - EXPECT_FALSE(regions[i].permissions & MappedMemoryRegion::EXECUTE); - EXPECT_TRUE(regions[i].permissions & MappedMemoryRegion::PRIVATE); - EXPECT_FALSE(found_stack) << "Found duplicate stacks"; - found_stack = true; - } - - if (address >= regions[i].start && address < regions[i].end) { - EXPECT_FALSE(found_address) << "Found same address in multiple regions"; - found_address = true; - } - } - - EXPECT_TRUE(found_exe); - EXPECT_TRUE(found_stack); - EXPECT_TRUE(found_address); -} - -TEST(ProcMapsTest, ReadProcMaps) { - std::string proc_maps; - ASSERT_TRUE(ReadProcMaps(&proc_maps)); - - std::vector regions; - ASSERT_TRUE(ParseProcMaps(proc_maps, ®ions)); - ASSERT_FALSE(regions.empty()); - - CheckProcMapsRegions(regions); -} - -TEST(ProcMapsTest, ReadProcMapsNonEmptyString) { - std::string old_string("I forgot to clear the string"); - std::string proc_maps(old_string); - ASSERT_TRUE(ReadProcMaps(&proc_maps)); - EXPECT_EQ(std::string::npos, proc_maps.find(old_string)); -} - -TEST(ProcMapsTest, MissingFields) { - static const char* const kTestCases[] = { - "00400000\n", // Missing end + beyond. - "00400000-0040b000\n", // Missing perms + beyond. - "00400000-0040b000 r-xp\n", // Missing offset + beyond. - "00400000-0040b000 r-xp 00000000\n", // Missing device + beyond. - "00400000-0040b000 r-xp 00000000 fc:00\n", // Missing inode + beyond. - "00400000-0040b000 00000000 fc:00 794418 /bin/cat\n", // Missing perms. - "00400000-0040b000 r-xp fc:00 794418 /bin/cat\n", // Missing offset. - "00400000-0040b000 r-xp 00000000 fc:00 /bin/cat\n", // Missing inode. - "00400000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing end. - "-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing start. - "00400000-0040b000 r-xp 00000000 794418 /bin/cat\n", // Missing device. - }; - - for (size_t i = 0; i < arraysize(kTestCases); ++i) { - SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i])); - std::vector regions; - EXPECT_FALSE(ParseProcMaps(kTestCases[i], ®ions)); - } -} - -TEST(ProcMapsTest, InvalidInput) { - static const char* const kTestCases[] = { - "thisisal-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n", - "0040000d-linvalid rwxp 00000000 fc:00 794418 /bin/cat\n", - "00400000-0040b000 inpu 00000000 fc:00 794418 /bin/cat\n", - "00400000-0040b000 rwxp tforproc fc:00 794418 /bin/cat\n", - "00400000-0040b000 rwxp 00000000 ma:ps 794418 /bin/cat\n", - "00400000-0040b000 rwxp 00000000 fc:00 parse! /bin/cat\n", - }; - - for (size_t i = 0; i < arraysize(kTestCases); ++i) { - SCOPED_TRACE(base::StringPrintf("kTestCases[%zu] = %s", i, kTestCases[i])); - std::vector regions; - EXPECT_FALSE(ParseProcMaps(kTestCases[i], ®ions)); - } -} - -TEST(ProcMapsTest, ParseProcMapsEmptyString) { - std::vector 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 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/debug/profiler.cc b/debug/profiler.cc deleted file mode 100644 index 1ee948334..000000000 --- a/debug/profiler.cc +++ /dev/null @@ -1,188 +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/debug/profiler.h" - -#include - -#include "base/debug/debugging_buildflags.h" -#include "base/process/process_handle.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/win/current_module.h" -#include "base/win/pe_image.h" -#endif // defined(OS_WIN) - -// TODO(peria): Enable profiling on Windows. -#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) -#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h" -#endif - -namespace base { -namespace debug { - -// TODO(peria): Enable profiling on Windows. -#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) - -static int profile_count = 0; - -void StartProfiling(const std::string& name) { - ++profile_count; - std::string full_name(name); - std::string pid = IntToString(GetCurrentProcId()); - std::string count = IntToString(profile_count); - ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid); - ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count); - ProfilerStart(full_name.c_str()); -} - -void StopProfiling() { - ProfilerFlush(); - ProfilerStop(); -} - -void FlushProfiling() { - ProfilerFlush(); -} - -bool BeingProfiled() { - return ProfilingIsEnabledForAllThreads(); -} - -void RestartProfilingAfterFork() { - ProfilerRegisterThread(); -} - -bool IsProfilingSupported() { - return true; -} - -#else - -void StartProfiling(const std::string& name) { -} - -void StopProfiling() { -} - -void FlushProfiling() { -} - -bool BeingProfiled() { - return false; -} - -void RestartProfilingAfterFork() { -} - -bool IsProfilingSupported() { - return false; -} - -#endif - -#if !defined(OS_WIN) - -ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { - return nullptr; -} - -DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() { - return nullptr; -} - -AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() { - return nullptr; -} - -MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() { - return nullptr; -} - -#else // defined(OS_WIN) - -namespace { - -struct FunctionSearchContext { - const char* name; - FARPROC function; -}; - -// Callback function to PEImage::EnumImportChunks. -bool FindResolutionFunctionInImports( - const base::win::PEImage &image, const char* module_name, - PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table, - PVOID cookie) { - FunctionSearchContext* context = - reinterpret_cast(cookie); - - DCHECK(context); - DCHECK(!context->function); - - // Our import address table contains pointers to the functions we import - // at this point. Let's retrieve the first such function and use it to - // find the module this import was resolved to by the loader. - const wchar_t* function_in_module = - reinterpret_cast(import_address_table->u1.Function); - - // Retrieve the module by a function in the module. - const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; - HMODULE module = NULL; - if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) { - // This can happen if someone IAT patches us to a thunk. - return true; - } - - // See whether this module exports the function we're looking for. - FARPROC exported_func = ::GetProcAddress(module, context->name); - if (exported_func != NULL) { - // We found it, return the function and terminate the enumeration. - context->function = exported_func; - return false; - } - - // Keep going. - return true; -} - -template -FunctionType FindFunctionInImports(const char* function_name) { - base::win::PEImage image(CURRENT_MODULE()); - - FunctionSearchContext ctx = { function_name, NULL }; - image.EnumImportChunks(FindResolutionFunctionInImports, &ctx); - - return reinterpret_cast(ctx.function); -} - -} // namespace - -ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() { - return FindFunctionInImports( - "ResolveReturnAddressLocation"); -} - -DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc() { - return FindFunctionInImports( - "OnDynamicFunctionEntry"); -} - -AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() { - return FindFunctionInImports( - "AddDynamicSymbol"); -} - -MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() { - return FindFunctionInImports( - "MoveDynamicSymbol"); -} - -#endif // defined(OS_WIN) - -} // namespace debug -} // namespace base diff --git a/debug/profiler.h b/debug/profiler.h deleted file mode 100644 index f706a1a3b..000000000 --- a/debug/profiler.h +++ /dev/null @@ -1,91 +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_DEBUG_PROFILER_H_ -#define BASE_DEBUG_PROFILER_H_ - -#include - -#include - -#include "base/base_export.h" - -// The Profiler functions allow usage of the underlying sampling based -// profiler. If the application has not been built with the necessary -// flags (-DENABLE_PROFILING and not -DNO_TCMALLOC) then these functions -// are noops. -namespace base { -namespace debug { - -// Start profiling with the supplied name. -// {pid} will be replaced by the process' pid and {count} will be replaced -// by the count of the profile run (starts at 1 with each process). -BASE_EXPORT void StartProfiling(const std::string& name); - -// Stop profiling and write out data. -BASE_EXPORT void StopProfiling(); - -// Force data to be written to file. -BASE_EXPORT void FlushProfiling(); - -// Returns true if process is being profiled. -BASE_EXPORT bool BeingProfiled(); - -// Reset profiling after a fork, which disables timers. -BASE_EXPORT void RestartProfilingAfterFork(); - -// Returns true iff this executable supports profiling. -BASE_EXPORT bool IsProfilingSupported(); - -// There's a class of profilers that use "return address swizzling" to get a -// hook on function exits. This class of profilers uses some form of entry hook, -// like e.g. binary instrumentation, or a compiler flag, that calls a hook each -// time a function is invoked. The hook then switches the return address on the -// stack for the address of an exit hook function, and pushes the original -// return address to a shadow stack of some type. When in due course the CPU -// executes a return to the exit hook, the exit hook will do whatever work it -// does on function exit, then arrange to return to the original return address. -// This class of profiler does not play well with programs that look at the -// return address, as does e.g. V8. V8 uses the return address to certain -// runtime functions to find the JIT code that called it, and from there finds -// the V8 data structures associated to the JS function involved. -// A return address resolution function is used to fix this. It allows such -// programs to resolve a location on stack where a return address originally -// resided, to the shadow stack location where the profiler stashed it. -typedef uintptr_t (*ReturnAddressLocationResolver)( - uintptr_t return_addr_location); - -// This type declaration must match V8's FunctionEntryHook. -typedef void (*DynamicFunctionEntryHook)(uintptr_t function, - uintptr_t return_addr_location); - -// The functions below here are to support profiling V8-generated code. -// V8 has provisions for generating a call to an entry hook for newly generated -// JIT code, and it can push symbol information on code generation and advise -// when the garbage collector moves code. The functions declarations below here -// make glue between V8's facilities and a profiler. - -// This type declaration must match V8's FunctionEntryHook. -typedef void (*DynamicFunctionEntryHook)(uintptr_t function, - uintptr_t return_addr_location); - -typedef void (*AddDynamicSymbol)(const void* address, - size_t length, - const char* name, - size_t name_len); -typedef void (*MoveDynamicSymbol)(const void* address, const void* new_address); - - -// If this binary is instrumented and the instrumentation supplies a function -// for each of those purposes, find and return the function in question. -// Otherwise returns NULL. -BASE_EXPORT ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc(); -BASE_EXPORT DynamicFunctionEntryHook GetProfilerDynamicFunctionEntryHookFunc(); -BASE_EXPORT AddDynamicSymbol GetProfilerAddDynamicSymbolFunc(); -BASE_EXPORT MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc(); - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_PROFILER_H_ diff --git a/debug/stack_trace.cc b/debug/stack_trace.cc deleted file mode 100644 index 771512176..000000000 --- a/debug/stack_trace.cc +++ /dev/null @@ -1,277 +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/debug/stack_trace.h" - -#include - -#include -#include - -#include "base/logging.h" -#include "base/macros.h" - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -#if defined(OS_LINUX) || defined(OS_ANDROID) -#include -#include "base/process/process_handle.h" -#include "base/threading/platform_thread.h" -#endif - -#if defined(OS_MACOSX) -#include -#endif - -#if defined(OS_LINUX) && defined(__GLIBC__) -extern "C" void* __libc_stack_end; -#endif - -#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -namespace base { -namespace debug { - -namespace { - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__) -// GCC and LLVM generate slightly different frames on ARM, see -// https://llvm.org/bugs/show_bug.cgi?id=18505 - LLVM generates -// x86-compatible frame, while GCC needs adjustment. -constexpr size_t kStackFrameAdjustment = sizeof(uintptr_t); -#else -constexpr size_t kStackFrameAdjustment = 0; -#endif - -uintptr_t GetNextStackFrame(uintptr_t fp) { - return reinterpret_cast(fp)[0] - kStackFrameAdjustment; -} - -uintptr_t GetStackFramePC(uintptr_t fp) { - return reinterpret_cast(fp)[1]; -} - -bool IsStackFrameValid(uintptr_t fp, uintptr_t prev_fp, uintptr_t stack_end) { - // With the stack growing downwards, older stack frame must be - // at a greater address that the current one. - if (fp <= prev_fp) return false; - - // Assume huge stack frames are bogus. - if (fp - prev_fp > 100000) return false; - - // Check alignment. - if (fp & (sizeof(uintptr_t) - 1)) return false; - - if (stack_end) { - // Both fp[0] and fp[1] must be within the stack. - if (fp > stack_end - 2 * sizeof(uintptr_t)) return false; - - // Additional check to filter out false positives. - if (GetStackFramePC(fp) < 32768) return false; - } - - return true; -}; - -// ScanStackForNextFrame() scans the stack for a valid frame to allow unwinding -// past system libraries. Only supported on Linux where system libraries are -// usually in the middle of the trace: -// -// TraceStackFramePointers -// -// base::WorkSourceDispatch <-- unwinding stops (next frame is invalid), -// g_main_context_dispatch ScanStackForNextFrame() is called -// -// g_main_context_iteration -// base::MessagePumpGlib::Run <-- ScanStackForNextFrame() finds valid frame, -// base::RunLoop::Run unwinding resumes -// -// __libc_start_main -// -// For stack scanning to be efficient it's very important for the thread to -// be started by Chrome. In that case we naturally terminate unwinding once -// we reach the origin of the stack (i.e. GetStackEnd()). If the thread is -// not started by Chrome (e.g. Android's main thread), then we end up always -// scanning area at the origin of the stack, wasting time and not finding any -// frames (since Android libraries don't have frame pointers). -// -// ScanStackForNextFrame() returns 0 if it couldn't find a valid frame -// (or if stack scanning is not supported on the current platform). -uintptr_t ScanStackForNextFrame(uintptr_t fp, uintptr_t stack_end) { -#if defined(OS_LINUX) - // Enough to resume almost all prematurely terminated traces. - constexpr size_t kMaxStackScanArea = 8192; - - if (!stack_end) { - // Too dangerous to scan without knowing where the stack ends. - return 0; - } - - fp += sizeof(uintptr_t); // current frame is known to be invalid - uintptr_t last_fp_to_scan = std::min(fp + kMaxStackScanArea, stack_end) - - sizeof(uintptr_t); - for (;fp <= last_fp_to_scan; fp += sizeof(uintptr_t)) { - uintptr_t next_fp = GetNextStackFrame(fp); - if (IsStackFrameValid(next_fp, fp, stack_end)) { - // Check two frames deep. Since stack frame is just a pointer to - // a higher address on the stack, it's relatively easy to find - // something that looks like one. However two linked frames are - // far less likely to be bogus. - uintptr_t next2_fp = GetNextStackFrame(next_fp); - if (IsStackFrameValid(next2_fp, next_fp, stack_end)) { - return fp; - } - } - } -#endif // defined(OS_LINUX) - - return 0; -} - -// Links stack frame |fp| to |parent_fp|, so that during stack unwinding -// TraceStackFramePointers() visits |parent_fp| after visiting |fp|. -// Both frame pointers must come from __builtin_frame_address(). -// Returns previous stack frame |fp| was linked to. -void* LinkStackFrames(void* fpp, void* parent_fp) { - uintptr_t fp = reinterpret_cast(fpp) - kStackFrameAdjustment; - void* prev_parent_fp = reinterpret_cast(fp)[0]; - reinterpret_cast(fp)[0] = parent_fp; - return prev_parent_fp; -} - -#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -} // namespace - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) -uintptr_t GetStackEnd() { -#if defined(OS_ANDROID) - // Bionic reads proc/maps on every call to pthread_getattr_np() when called - // from the main thread. So we need to cache end of stack in that case to get - // acceptable performance. - // For all other threads pthread_getattr_np() is fast enough as it just reads - // values from its pthread_t argument. - static uintptr_t main_stack_end = 0; - - bool is_main_thread = GetCurrentProcId() == PlatformThread::CurrentId(); - if (is_main_thread && main_stack_end) { - return main_stack_end; - } - - uintptr_t stack_begin = 0; - size_t stack_size = 0; - pthread_attr_t attributes; - int error = pthread_getattr_np(pthread_self(), &attributes); - if (!error) { - error = pthread_attr_getstack( - &attributes, reinterpret_cast(&stack_begin), &stack_size); - pthread_attr_destroy(&attributes); - } - DCHECK(!error); - - uintptr_t stack_end = stack_begin + stack_size; - if (is_main_thread) { - main_stack_end = stack_end; - } - return stack_end; // 0 in case of error - -#elif defined(OS_LINUX) && defined(__GLIBC__) - - if (GetCurrentProcId() == PlatformThread::CurrentId()) { - // For the main thread we have a shortcut. - return reinterpret_cast(__libc_stack_end); - } - -// No easy way to get end of the stack for non-main threads, -// see crbug.com/617730. -#elif defined(OS_MACOSX) - return reinterpret_cast(pthread_get_stackaddr_np(pthread_self())); -#endif - - // Don't know how to get end of the stack. - return 0; -} -#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -StackTrace::StackTrace() : StackTrace(arraysize(trace_)) {} - -StackTrace::StackTrace(const void* const* trace, size_t count) { - count = std::min(count, arraysize(trace_)); - if (count) - memcpy(trace_, trace, count * sizeof(trace_[0])); - count_ = count; -} - -const void *const *StackTrace::Addresses(size_t* count) const { - *count = count_; - if (count_) - return trace_; - return nullptr; -} - -std::string StackTrace::ToString() const { - std::stringstream stream; -#if !defined(__UCLIBC__) && !defined(_AIX) - OutputToStream(&stream); -#endif - return stream.str(); -} - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -size_t TraceStackFramePointers(const void** out_trace, - size_t max_depth, - size_t skip_initial) { - // Usage of __builtin_frame_address() enables frame pointers in this - // function even if they are not enabled globally. So 'fp' will always - // be valid. - uintptr_t fp = reinterpret_cast(__builtin_frame_address(0)) - - kStackFrameAdjustment; - - uintptr_t stack_end = GetStackEnd(); - - size_t depth = 0; - while (depth < max_depth) { - if (skip_initial != 0) { - skip_initial--; - } else { - out_trace[depth++] = reinterpret_cast(GetStackFramePC(fp)); - } - - uintptr_t next_fp = GetNextStackFrame(fp); - if (IsStackFrameValid(next_fp, fp, stack_end)) { - fp = next_fp; - continue; - } - - next_fp = ScanStackForNextFrame(fp, stack_end); - if (next_fp) { - fp = next_fp; - continue; - } - - // Failed to find next frame. - break; - } - - return depth; -} - -ScopedStackFrameLinker::ScopedStackFrameLinker(void* fp, void* parent_fp) - : fp_(fp), - parent_fp_(parent_fp), - original_parent_fp_(LinkStackFrames(fp, parent_fp)) {} - -ScopedStackFrameLinker::~ScopedStackFrameLinker() { - void* previous_parent_fp = LinkStackFrames(fp_, original_parent_fp_); - CHECK_EQ(parent_fp_, previous_parent_fp) - << "Stack frame's parent pointer has changed!"; -} - -#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -} // namespace debug -} // namespace base diff --git a/debug/stack_trace.h b/debug/stack_trace.h deleted file mode 100644 index 81e6672e8..000000000 --- a/debug/stack_trace.h +++ /dev/null @@ -1,197 +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_DEBUG_STACK_TRACE_H_ -#define BASE_DEBUG_STACK_TRACE_H_ - -#include - -#include -#include - -#include "base/base_export.h" -#include "base/debug/debugging_buildflags.h" -#include "base/macros.h" -#include "build/build_config.h" - -#if defined(OS_POSIX) -#include -#endif - -#if defined(OS_WIN) -struct _EXCEPTION_POINTERS; -struct _CONTEXT; -#endif - -namespace base { -namespace debug { - -// Enables stack dump to console output on exception and signals. -// When enabled, the process will quit immediately. This is meant to be used in -// unit_tests only! This is not thread-safe: only call from main thread. -// In sandboxed processes, this has to be called before the sandbox is turned -// on. -// Calling this function on Linux opens /proc/self/maps and caches its -// contents. In non-official 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 EnableInProcessStackDumping(); - -#if defined(OS_POSIX) -BASE_EXPORT void SetStackDumpFirstChanceCallback(bool (*handler)(int, - void*, - void*)); -#endif - -// Returns end of the stack, or 0 if we couldn't get it. -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) -BASE_EXPORT uintptr_t GetStackEnd(); -#endif - -// 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. -class BASE_EXPORT StackTrace { - public: - // Creates a stacktrace from the current location. - StackTrace(); - - // Creates a stacktrace from the current location, of up to |count| entries. - // |count| will be limited to at most |kMaxTraces|. - explicit StackTrace(size_t count); - - // Creates a stacktrace from an existing array of instruction - // pointers (such as returned by Addresses()). |count| will be - // limited to at most |kMaxTraces|. - StackTrace(const void* const* trace, size_t count); - -#if defined(OS_WIN) - // 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 _CONTEXT* context); -#endif - - // Copying and assignment are allowed with the default functions. - - // Gets an array of instruction pointer values. |*count| will be set to the - // number of elements in the returned array. - const void* const* Addresses(size_t* count) const; - - // Prints the stack trace to stderr. - void Print() const; - -#if !defined(__UCLIBC__) & !defined(_AIX) - // 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; - - private: -#if defined(OS_WIN) - void InitTrace(const _CONTEXT* context_record); -#endif - - // From http://msdn.microsoft.com/en-us/library/bb204633.aspx, - // the sum of FramesToSkip and FramesToCapture must be less than 63, - // so set it to 62. Even if on POSIX it could be a larger value, it usually - // doesn't give much more information. - static const int kMaxTraces = 62; - - void* trace_[kMaxTraces]; - - // The number of valid frames in |trace_|. - size_t count_; -}; - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) -// Traces the stack by using frame pointers. This function is faster but less -// reliable than StackTrace. It should work for debug and profiling builds, -// but not for release builds (although there are some exceptions). -// -// Writes at most |max_depth| frames (instruction pointers) into |out_trace| -// after skipping |skip_initial| frames. Note that the function itself is not -// added to the trace so |skip_initial| should be 0 in most cases. -// Returns number of frames written. -BASE_EXPORT size_t TraceStackFramePointers(const void** out_trace, - size_t max_depth, - size_t skip_initial); - -// Links stack frame |fp| to |parent_fp|, so that during stack unwinding -// TraceStackFramePointers() visits |parent_fp| after visiting |fp|. -// Both frame pointers must come from __builtin_frame_address(). -// Destructor restores original linkage of |fp| to avoid corrupting caller's -// frame register on return. -// -// This class can be used to repair broken stack frame chain in cases -// when execution flow goes into code built without frame pointers: -// -// void DoWork() { -// Call_SomeLibrary(); -// } -// static __thread void* g_saved_fp; -// void Call_SomeLibrary() { -// g_saved_fp = __builtin_frame_address(0); -// some_library_call(...); // indirectly calls SomeLibrary_Callback() -// } -// void SomeLibrary_Callback() { -// ScopedStackFrameLinker linker(__builtin_frame_address(0), g_saved_fp); -// ... -// TraceStackFramePointers(...); -// } -// -// This produces the following trace: -// -// #0 SomeLibrary_Callback() -// #1
-// #2 DoWork() -// ...rest of the trace... -// -// SomeLibrary doesn't use frame pointers, so when SomeLibrary_Callback() -// is called, stack frame register contains bogus value that becomes callback' -// parent frame address. Without ScopedStackFrameLinker unwinding would've -// stopped at that bogus frame address yielding just two first frames (#0, #1). -// ScopedStackFrameLinker overwrites callback's parent frame address with -// Call_SomeLibrary's frame, so unwinder produces full trace without even -// noticing that stack frame chain was broken. -class BASE_EXPORT ScopedStackFrameLinker { - public: - ScopedStackFrameLinker(void* fp, void* parent_fp); - ~ScopedStackFrameLinker(); - - private: - void* fp_; - void* parent_fp_; - void* original_parent_fp_; - - DISALLOW_COPY_AND_ASSIGN(ScopedStackFrameLinker); -}; - -#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -namespace internal { - -#if defined(OS_POSIX) && !defined(OS_ANDROID) -// 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. -BASE_EXPORT char *itoa_r(intptr_t i, - char *buf, - size_t sz, - int base, - size_t padding); -#endif // defined(OS_POSIX) && !defined(OS_ANDROID) - -} // namespace internal - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_STACK_TRACE_H_ diff --git a/debug/stack_trace_android.cc b/debug/stack_trace_android.cc deleted file mode 100644 index 329204c8c..000000000 --- a/debug/stack_trace_android.cc +++ /dev/null @@ -1,134 +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/debug/stack_trace.h" - -#include -#include -#include - -#include -#include - -#include "base/debug/proc_maps_linux.h" -#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 { - StackCrawlState(uintptr_t* frames, size_t max_depth) - : frames(frames), - frame_count(0), - max_depth(max_depth), - have_skipped_self(false) {} - - uintptr_t* frames; - size_t frame_count; - size_t max_depth; - bool have_skipped_self; -}; - -_Unwind_Reason_Code TraceStackFrame(_Unwind_Context* context, void* arg) { - StackCrawlState* state = static_cast(arg); - uintptr_t ip = _Unwind_GetIP(context); - - // The first stack frame is this function itself. Skip it. - if (ip != 0 && !state->have_skipped_self) { - state->have_skipped_self = true; - return _URC_NO_REASON; - } - - state->frames[state->frame_count++] = ip; - if (state->frame_count >= state->max_depth) - return _URC_END_OF_STACK; - return _URC_NO_REASON; -} - -} // namespace - -namespace base { -namespace debug { - -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. - // TODO(phajdan.jr): De-duplicate this SIGPIPE code. - struct sigaction action; - memset(&action, 0, sizeof(action)); - action.sa_handler = SIG_IGN; - sigemptyset(&action.sa_mask); - return (sigaction(SIGPIPE, &action, NULL) == 0); -} - -StackTrace::StackTrace(size_t count) { - count = std::min(arraysize(trace_), count); - - StackCrawlState state(reinterpret_cast(trace_), count); - _Unwind_Backtrace(&TraceStackFrame, &state); - count_ = state.frame_count; -} - -void StackTrace::Print() const { - std::string backtrace = ToString(); - __android_log_write(ANDROID_LOG_ERROR, "chromium", backtrace.c_str()); -} - -// NOTE: Native libraries in APKs are stripped before installing. Print out the -// relocatable address and library names so host computers can use tools to -// symbolize and demangle (e.g., addr2line, c++filt). -void StackTrace::OutputToStream(std::ostream* os) const { - std::string proc_maps; - std::vector regions; - // Allow IO to read /proc/self/maps. Reading this file doesn't hit the disk - // since it lives in procfs, and this is currently used to print a stack trace - // on fatal log messages in debug builds only. If the restriction is enabled - // then it will recursively trigger fatal failures when this enters on the - // UI thread. - base::ThreadRestrictions::ScopedAllowIO allow_io; - if (!ReadProcMaps(&proc_maps)) { - __android_log_write( - ANDROID_LOG_ERROR, "chromium", "Failed to read /proc/self/maps"); - } else if (!ParseProcMaps(proc_maps, ®ions)) { - __android_log_write( - ANDROID_LOG_ERROR, "chromium", "Failed to parse /proc/self/maps"); - } - - for (size_t i = 0; i < count_; ++i) { - // Subtract one as return address of function may be in the next - // function when a function is annotated as noreturn. - uintptr_t address = reinterpret_cast(trace_[i]) - 1; - - std::vector::iterator iter = regions.begin(); - while (iter != regions.end()) { - if (address >= iter->start && address < iter->end && - !iter->path.empty()) { - break; - } - ++iter; - } - - *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+" FMT_ADDR, path, rel_pc); - } else { - *os << ""; - } - - *os << "\n"; - } -} - -} // namespace debug -} // namespace base diff --git a/debug/stack_trace_fuchsia.cc b/debug/stack_trace_fuchsia.cc deleted file mode 100644 index f9969331b..000000000 --- a/debug/stack_trace_fuchsia.cc +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2017 The Chromium 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/stack_trace.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "base/logging.h" - -namespace base { -namespace debug { - -namespace { - -const char kProcessNamePrefix[] = "app:"; -const size_t kProcessNamePrefixLen = arraysize(kProcessNamePrefix) - 1; - -struct BacktraceData { - void** trace_array; - size_t* count; - size_t max; -}; - -_Unwind_Reason_Code UnwindStore(struct _Unwind_Context* context, - void* user_data) { - BacktraceData* data = reinterpret_cast(user_data); - uintptr_t pc = _Unwind_GetIP(context); - data->trace_array[*data->count] = reinterpret_cast(pc); - *data->count += 1; - if (*data->count == data->max) - return _URC_END_OF_STACK; - return _URC_NO_REASON; -} - -// Stores and queries debugging symbol map info for the current process. -class SymbolMap { - public: - struct Entry { - void* addr; - char name[ZX_MAX_NAME_LEN + kProcessNamePrefixLen]; - }; - - SymbolMap(); - ~SymbolMap() = default; - - // Gets the symbol map entry for |address|. Returns null if no entry could be - // found for the address, or if the symbol map could not be queried. - Entry* GetForAddress(void* address); - - private: - // Component builds of Chrome pull about 250 shared libraries (on Linux), so - // 512 entries should be enough in most cases. - static const size_t kMaxMapEntries = 512; - - void Populate(); - - // Sorted in descending order by address, for lookup purposes. - Entry entries_[kMaxMapEntries]; - - size_t count_ = 0; - bool valid_ = false; - - DISALLOW_COPY_AND_ASSIGN(SymbolMap); -}; - -SymbolMap::SymbolMap() { - Populate(); -} - -SymbolMap::Entry* SymbolMap::GetForAddress(void* address) { - if (!valid_) { - return nullptr; - } - - // Working backwards in the address space, return the first map entry whose - // address comes before |address| (thereby enclosing it.) - for (size_t i = 0; i < count_; ++i) { - if (address >= entries_[i].addr) { - return &entries_[i]; - } - } - return nullptr; -} - -void SymbolMap::Populate() { - zx_handle_t process = zx_process_self(); - - // Try to fetch the name of the process' main executable, which was set as the - // name of the |process| kernel object. - // TODO(wez): Object names can only have up to ZX_MAX_NAME_LEN characters, so - // if we keep hitting problems with truncation, find a way to plumb argv[0] - // through to here instead, e.g. using CommandLine::GetProgramName(). - char app_name[arraysize(SymbolMap::Entry::name)]; - strcpy(app_name, kProcessNamePrefix); - zx_status_t status = zx_object_get_property( - process, ZX_PROP_NAME, app_name + kProcessNamePrefixLen, - sizeof(app_name) - kProcessNamePrefixLen); - if (status != ZX_OK) { - DPLOG(WARNING) - << "Couldn't get name, falling back to 'app' for program name: " - << status; - strlcat(app_name, "app", sizeof(app_name)); - } - - // Retrieve the debug info struct. - uintptr_t debug_addr; - status = zx_object_get_property(process, ZX_PROP_PROCESS_DEBUG_ADDR, - &debug_addr, sizeof(debug_addr)); - if (status != ZX_OK) { - DPLOG(ERROR) << "Couldn't get symbol map for process: " << status; - return; - } - r_debug* debug_info = reinterpret_cast(debug_addr); - - // Get the link map from the debug info struct. - link_map* lmap = reinterpret_cast(debug_info->r_map); - if (!lmap) { - DPLOG(ERROR) << "Null link_map for process."; - return; - } - - // Copy the contents of the link map linked list to |entries_|. - while (lmap != nullptr) { - if (count_ >= arraysize(entries_)) { - break; - } - SymbolMap::Entry* next_entry = &entries_[count_]; - count_++; - - next_entry->addr = reinterpret_cast(lmap->l_addr); - char* name_to_use = lmap->l_name[0] ? lmap->l_name : app_name; - strlcpy(next_entry->name, name_to_use, sizeof(next_entry->name)); - lmap = lmap->l_next; - } - - std::sort( - &entries_[0], &entries_[count_ - 1], - [](const Entry& a, const Entry& b) -> bool { return a.addr >= b.addr; }); - - valid_ = true; -} - -} // namespace - -// static -bool EnableInProcessStackDumping() { - // StackTrace works to capture the current stack (e.g. for diagnostics added - // to code), but for local capture and print of backtraces, we just let the - // system crashlogger take over. It handles printing out a nicely formatted - // backtrace with dso information, relative offsets, etc. that we can then - // filter with addr2line in the run script to get file/line info. - return true; -} - -StackTrace::StackTrace(size_t count) : count_(0) { - BacktraceData data = {&trace_[0], &count_, - std::min(count, static_cast(kMaxTraces))}; - _Unwind_Backtrace(&UnwindStore, &data); -} - -void StackTrace::Print() const { - OutputToStream(&std::cerr); -} - -// Sample stack trace output is designed to be similar to Fuchsia's crashlogger: -// bt#00: pc 0x1527a058aa00 (app:/system/base_unittests,0x18bda00) -// bt#01: pc 0x1527a0254b5c (app:/system/base_unittests,0x1587b5c) -// bt#02: pc 0x15279f446ece (app:/system/base_unittests,0x779ece) -// ... -// bt#21: pc 0x1527a05b51b4 (app:/system/base_unittests,0x18e81b4) -// bt#22: pc 0x54fdbf3593de (libc.so,0x1c3de) -// bt#23: end -void StackTrace::OutputToStream(std::ostream* os) const { - SymbolMap map; - - size_t i = 0; - for (; (i < count_) && os->good(); ++i) { - SymbolMap::Entry* entry = map.GetForAddress(trace_[i]); - if (entry) { - size_t offset = reinterpret_cast(trace_[i]) - - reinterpret_cast(entry->addr); - *os << "bt#" << std::setw(2) << std::setfill('0') << i << std::setw(0) - << ": pc " << trace_[i] << " (" << entry->name << ",0x" << std::hex - << offset << std::dec << std::setw(0) << ")\n"; - } else { - // Fallback if the DSO map isn't available. - // Logged PC values are absolute memory addresses, and the shared object - // name is not emitted. - *os << "bt#" << std::setw(2) << std::setfill('0') << i << std::setw(0) - << ": pc " << trace_[i] << "\n"; - } - } - - (*os) << "bt#" << std::setw(2) << i << ": end\n"; -} - -} // namespace debug -} // namespace base diff --git a/debug/stack_trace_posix.cc b/debug/stack_trace_posix.cc deleted file mode 100644 index f3f05dad2..000000000 --- a/debug/stack_trace_posix.cc +++ /dev/null @@ -1,898 +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/debug/stack_trace.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#if !defined(USE_SYMBOLIZE) -#include -#endif -#if !defined(__UCLIBC__) && !defined(_AIX) -#include -#endif - -#if defined(OS_MACOSX) -#include -#endif - -#if defined(OS_LINUX) -#include "base/debug/proc_maps_linux.h" -#endif - -#include "base/cfi_buildflags.h" -#include "base/debug/debugger.h" -#include "base/files/scoped_file.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/free_deleter.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" -#endif - -namespace base { -namespace debug { - -namespace { - -volatile sig_atomic_t in_signal_handler = 0; - -bool (*try_handle_signal)(int, void*, void*) = nullptr; - -#if !defined(USE_SYMBOLIZE) -// The prefix used for mangled symbols, per the Itanium C++ ABI: -// http://www.codesourcery.com/cxx-abi/abi.html#mangling -const char kMangledSymbolPrefix[] = "_Z"; - -// Characters that can be used for symbols, generated by Ruby: -// (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join -const char kSymbolCharacters[] = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; -#endif // !defined(USE_SYMBOLIZE) - -#if !defined(USE_SYMBOLIZE) -// Demangles C++ symbols in the given text. Example: -// -// "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]" -// => -// "out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]" -void DemangleSymbols(std::string* text) { - // Note: code in this function is NOT async-signal safe (std::string uses - // malloc internally). - -#if !defined(__UCLIBC__) && !defined(_AIX) - std::string::size_type search_from = 0; - while (search_from < text->size()) { - // Look for the start of a mangled symbol, from search_from. - std::string::size_type mangled_start = - text->find(kMangledSymbolPrefix, search_from); - if (mangled_start == std::string::npos) { - break; // Mangled symbol not found. - } - - // Look for the end of the mangled symbol. - std::string::size_type mangled_end = - text->find_first_not_of(kSymbolCharacters, mangled_start); - if (mangled_end == std::string::npos) { - mangled_end = text->size(); - } - std::string mangled_symbol = - text->substr(mangled_start, mangled_end - mangled_start); - - // Try to demangle the mangled symbol candidate. - int status = 0; - std::unique_ptr demangled_symbol( - abi::__cxa_demangle(mangled_symbol.c_str(), nullptr, 0, &status)); - if (status == 0) { // Demangling is successful. - // Remove the mangled symbol. - text->erase(mangled_start, mangled_end - mangled_start); - // Insert the demangled symbol. - text->insert(mangled_start, demangled_symbol.get()); - // Next time, we'll start right after the demangled symbol we inserted. - search_from = mangled_start + strlen(demangled_symbol.get()); - } else { - // Failed to demangle. Retry after the "_Z" we just found. - search_from = mangled_start + 2; - } - } -#endif // !defined(__UCLIBC__) && !defined(_AIX) -} -#endif // !defined(USE_SYMBOLIZE) - -class BacktraceOutputHandler { - public: - virtual void HandleOutput(const char* output) = 0; - - protected: - virtual ~BacktraceOutputHandler() = default; -}; - -#if !defined(__UCLIBC__) && !defined(_AIX) -void OutputPointer(void* pointer, BacktraceOutputHandler* handler) { - // 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(pointer), - buf, sizeof(buf), 16, 12); - handler->HandleOutput(buf); -} - -#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, - 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 (size_t i = 0; i < size; ++i) { - OutputFrameId(i, handler); - handler->HandleOutput(" "); - OutputPointer(trace[i], handler); - handler->HandleOutput(" "); - - char buf[1024] = { '\0' }; - - // Subtract by one as return address of function may be in the next - // function when a function is annotated as noreturn. - void* address = static_cast(trace[i]) - 1; - if (google::Symbolize(address, buf, sizeof(buf))) - handler->HandleOutput(buf); - else - handler->HandleOutput(""); - - handler->HandleOutput("\n"); - } -#else - 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) { - std::unique_ptr trace_symbols( - backtrace_symbols(trace, size)); - if (trace_symbols.get()) { - 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()); - handler->HandleOutput("\n"); - } - - printed = true; - } - } - - if (!printed) { - for (size_t i = 0; i < size; ++i) { - handler->HandleOutput(" ["); - OutputPointer(trace[i], handler); - handler->HandleOutput("]\n"); - } - } -#endif // defined(USE_SYMBOLIZE) -} -#endif // !defined(__UCLIBC__) && !defined(_AIX) - -void PrintToStderr(const char* output) { - // 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. - ignore_result(HANDLE_EINTR(write(STDERR_FILENO, output, strlen(output)))); -} - -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. - - // Give a registered callback a chance to recover from this signal - // - // V8 uses guard regions to guarantee memory safety in WebAssembly. This means - // some signals might be expected if they originate from Wasm code while - // accessing the guard region. We give V8 the chance to handle and recover - // from these signals first. - if (try_handle_signal != nullptr && - try_handle_signal(signal, info, void_context)) { - // The first chance handler took care of this. The SA_RESETHAND flag - // replaced this signal handler upon entry, but we want to stay - // installed. Thus, we reinstall ourselves before returning. - struct sigaction action; - memset(&action, 0, sizeof(action)); - action.sa_flags = SA_RESETHAND | SA_SIGINFO; - action.sa_sigaction = &StackDumpSignalHandler; - sigemptyset(&action.sa_mask); - - sigaction(signal, &action, nullptr); - return; - } - -// Do not take the "in signal handler" code path on Mac in a DCHECK-enabled -// build, as this prevents seeing a useful (symbolized) stack trace on a crash -// or DCHECK() failure. While it may not be fully safe to run the stack symbol -// printing code, in practice it's better to provide meaningful stack traces - -// and the risk is low given we're likely crashing already. -#if !defined(OS_MACOSX) || !DCHECK_IS_ON() - // Record the fact that we are in the signal handler now, so that the rest - // of StackTrace can behave in an async-signal-safe manner. - in_signal_handler = 1; -#endif - - if (BeingDebugged()) - BreakDebugger(); - - PrintToStderr("Received signal "); - char buf[1024] = { 0 }; - internal::itoa_r(signal, buf, sizeof(buf), 10, 0); - PrintToStderr(buf); - if (signal == SIGBUS) { - if (info->si_code == BUS_ADRALN) - PrintToStderr(" BUS_ADRALN "); - else if (info->si_code == BUS_ADRERR) - PrintToStderr(" BUS_ADRERR "); - else if (info->si_code == BUS_OBJERR) - PrintToStderr(" BUS_OBJERR "); - else - PrintToStderr(" "); - } else if (signal == SIGFPE) { - if (info->si_code == FPE_FLTDIV) - PrintToStderr(" FPE_FLTDIV "); - else if (info->si_code == FPE_FLTINV) - PrintToStderr(" FPE_FLTINV "); - else if (info->si_code == FPE_FLTOVF) - PrintToStderr(" FPE_FLTOVF "); - else if (info->si_code == FPE_FLTRES) - PrintToStderr(" FPE_FLTRES "); - else if (info->si_code == FPE_FLTSUB) - PrintToStderr(" FPE_FLTSUB "); - else if (info->si_code == FPE_FLTUND) - PrintToStderr(" FPE_FLTUND "); - else if (info->si_code == FPE_INTDIV) - PrintToStderr(" FPE_INTDIV "); - else if (info->si_code == FPE_INTOVF) - PrintToStderr(" FPE_INTOVF "); - else - PrintToStderr(" "); - } else if (signal == SIGILL) { - if (info->si_code == ILL_BADSTK) - PrintToStderr(" ILL_BADSTK "); - else if (info->si_code == ILL_COPROC) - PrintToStderr(" ILL_COPROC "); - else if (info->si_code == ILL_ILLOPN) - PrintToStderr(" ILL_ILLOPN "); - else if (info->si_code == ILL_ILLADR) - PrintToStderr(" ILL_ILLADR "); - else if (info->si_code == ILL_ILLTRP) - PrintToStderr(" ILL_ILLTRP "); - else if (info->si_code == ILL_PRVOPC) - PrintToStderr(" ILL_PRVOPC "); - else if (info->si_code == ILL_PRVREG) - PrintToStderr(" ILL_PRVREG "); - else - PrintToStderr(" "); - } else if (signal == SIGSEGV) { - if (info->si_code == SEGV_MAPERR) - PrintToStderr(" SEGV_MAPERR "); - else if (info->si_code == SEGV_ACCERR) - PrintToStderr(" SEGV_ACCERR "); - else - PrintToStderr(" "); - } - if (signal == SIGBUS || signal == SIGFPE || - signal == SIGILL || signal == SIGSEGV) { - internal::itoa_r(reinterpret_cast(info->si_addr), - buf, sizeof(buf), 16, 12); - PrintToStderr(buf); - } - PrintToStderr("\n"); - -#if BUILDFLAG(CFI_ENFORCEMENT_TRAP) - if (signal == SIGILL && info->si_code == ILL_ILLOPN) { - PrintToStderr( - "CFI: Most likely a control flow integrity violation; for more " - "information see:\n"); - PrintToStderr( - "https://www.chromium.org/developers/testing/control-flow-integrity\n"); - } -#endif // BUILDFLAG(CFI_ENFORCEMENT_TRAP) - - debug::StackTrace().Print(); - -#if defined(OS_LINUX) -#if ARCH_CPU_X86_FAMILY - ucontext_t* context = reinterpret_cast(void_context); - const struct { - const char* label; - greg_t value; - } registers[] = { -#if ARCH_CPU_32_BITS - { " gs: ", context->uc_mcontext.gregs[REG_GS] }, - { " fs: ", context->uc_mcontext.gregs[REG_FS] }, - { " es: ", context->uc_mcontext.gregs[REG_ES] }, - { " ds: ", context->uc_mcontext.gregs[REG_DS] }, - { " edi: ", context->uc_mcontext.gregs[REG_EDI] }, - { " esi: ", context->uc_mcontext.gregs[REG_ESI] }, - { " ebp: ", context->uc_mcontext.gregs[REG_EBP] }, - { " esp: ", context->uc_mcontext.gregs[REG_ESP] }, - { " ebx: ", context->uc_mcontext.gregs[REG_EBX] }, - { " edx: ", context->uc_mcontext.gregs[REG_EDX] }, - { " ecx: ", context->uc_mcontext.gregs[REG_ECX] }, - { " eax: ", context->uc_mcontext.gregs[REG_EAX] }, - { " trp: ", context->uc_mcontext.gregs[REG_TRAPNO] }, - { " err: ", context->uc_mcontext.gregs[REG_ERR] }, - { " ip: ", context->uc_mcontext.gregs[REG_EIP] }, - { " cs: ", context->uc_mcontext.gregs[REG_CS] }, - { " efl: ", context->uc_mcontext.gregs[REG_EFL] }, - { " usp: ", context->uc_mcontext.gregs[REG_UESP] }, - { " ss: ", context->uc_mcontext.gregs[REG_SS] }, -#elif ARCH_CPU_64_BITS - { " r8: ", context->uc_mcontext.gregs[REG_R8] }, - { " r9: ", context->uc_mcontext.gregs[REG_R9] }, - { " r10: ", context->uc_mcontext.gregs[REG_R10] }, - { " r11: ", context->uc_mcontext.gregs[REG_R11] }, - { " r12: ", context->uc_mcontext.gregs[REG_R12] }, - { " r13: ", context->uc_mcontext.gregs[REG_R13] }, - { " r14: ", context->uc_mcontext.gregs[REG_R14] }, - { " r15: ", context->uc_mcontext.gregs[REG_R15] }, - { " di: ", context->uc_mcontext.gregs[REG_RDI] }, - { " si: ", context->uc_mcontext.gregs[REG_RSI] }, - { " bp: ", context->uc_mcontext.gregs[REG_RBP] }, - { " bx: ", context->uc_mcontext.gregs[REG_RBX] }, - { " dx: ", context->uc_mcontext.gregs[REG_RDX] }, - { " ax: ", context->uc_mcontext.gregs[REG_RAX] }, - { " cx: ", context->uc_mcontext.gregs[REG_RCX] }, - { " sp: ", context->uc_mcontext.gregs[REG_RSP] }, - { " ip: ", context->uc_mcontext.gregs[REG_RIP] }, - { " efl: ", context->uc_mcontext.gregs[REG_EFL] }, - { " cgf: ", context->uc_mcontext.gregs[REG_CSGSFS] }, - { " erf: ", context->uc_mcontext.gregs[REG_ERR] }, - { " trp: ", context->uc_mcontext.gregs[REG_TRAPNO] }, - { " msk: ", context->uc_mcontext.gregs[REG_OLDMASK] }, - { " cr2: ", context->uc_mcontext.gregs[REG_CR2] }, -#endif // ARCH_CPU_32_BITS - }; - -#if ARCH_CPU_32_BITS - const int kRegisterPadding = 8; -#elif ARCH_CPU_64_BITS - const int kRegisterPadding = 16; -#endif - - for (size_t i = 0; i < arraysize(registers); i++) { - PrintToStderr(registers[i].label); - internal::itoa_r(registers[i].value, buf, sizeof(buf), - 16, kRegisterPadding); - PrintToStderr(buf); - - if ((i + 1) % 4 == 0) - PrintToStderr("\n"); - } - PrintToStderr("\n"); -#endif // ARCH_CPU_X86_FAMILY -#endif // defined(OS_LINUX) - - PrintToStderr("[end of stack trace]\n"); - -#if defined(OS_MACOSX) && !defined(OS_IOS) - if (::signal(signal, SIG_DFL) == SIG_ERR) - _exit(1); -#else - // Non-Mac OSes should probably reraise the signal as well, but the Linux - // sandbox tests break on CrOS devices. - // https://code.google.com/p/chromium/issues/detail?id=551681 - PrintToStderr("Calling _exit(1). Core file will not be generated.\n"); - _exit(1); -#endif // defined(OS_MACOSX) && !defined(OS_IOS) -} - -class PrintBacktraceOutputHandler : public BacktraceOutputHandler { - public: - PrintBacktraceOutputHandler() = default; - - void HandleOutput(const char* output) override { - // 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. - PrintToStderr(output); - } - - private: - DISALLOW_COPY_AND_ASSIGN(PrintBacktraceOutputHandler); -}; - -class StreamBacktraceOutputHandler : public BacktraceOutputHandler { - public: - explicit StreamBacktraceOutputHandler(std::ostream* os) : os_(os) { - } - - void HandleOutput(const char* output) override { (*os_) << output; } - - private: - std::ostream* os_; - - DISALLOW_COPY_AND_ASSIGN(StreamBacktraceOutputHandler); -}; - -void WarmUpBacktrace() { - // Warm up stack trace infrastructure. It turns out that on the first - // call glibc initializes some internal data structures using pthread_once, - // and even backtrace() can call malloc(), leading to hangs. - // - // Example stack trace snippet (with tcmalloc): - // - // #8 0x0000000000a173b5 in tc_malloc - // at ./third_party/tcmalloc/chromium/src/debugallocation.cc:1161 - // #9 0x00007ffff7de7900 in _dl_map_object_deps at dl-deps.c:517 - // #10 0x00007ffff7ded8a9 in dl_open_worker at dl-open.c:262 - // #11 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178 - // #12 0x00007ffff7ded31a in _dl_open (file=0x7ffff625e298 "libgcc_s.so.1") - // at dl-open.c:639 - // #13 0x00007ffff6215602 in do_dlopen at dl-libc.c:89 - // #14 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178 - // #15 0x00007ffff62156c4 in dlerror_run at dl-libc.c:48 - // #16 __GI___libc_dlopen_mode at dl-libc.c:165 - // #17 0x00007ffff61ef8f5 in init - // at ../sysdeps/x86_64/../ia64/backtrace.c:53 - // #18 0x00007ffff6aad400 in pthread_once - // at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_once.S:104 - // #19 0x00007ffff61efa14 in __GI___backtrace - // at ../sysdeps/x86_64/../ia64/backtrace.c:104 - // #20 0x0000000000752a54 in base::debug::StackTrace::StackTrace - // at base/debug/stack_trace_posix.cc:175 - // #21 0x00000000007a4ae5 in - // base::(anonymous namespace)::StackDumpSignalHandler - // at base/process_util_posix.cc:172 - // #22 - StackTrace stack_trace; -} - -#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>::get(); - } - - private: - friend struct DefaultSingletonTraits; - - SandboxSymbolizeHelper() - : is_initialized_(false) { - Init(); - } - - ~SandboxSymbolizeHelper() { - UnregisterCallback(); - CloseObjectFiles(); - } - - // Returns a O_RDONLY file descriptor for |file_path| if it was opened - // successfully 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(OFFICIAL_BUILD) - if (file_path) { - // The assumption here is that iterating over std::map - // using a const_iterator does not allocate dynamic memory, hense it is - // async-signal-safe. - std::map::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 = HANDLE_EINTR(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(OFFICIAL_BUILD) - - 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 using a const_iterator does not allocate - // dynamic memory, hence it is async-signal-safe. - for (const MappedMemoryRegion& region : instance->regions_) { - if (region.start <= pc && pc < region.end) { - start_address = region.start; - base_address = region.base; - 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; - } - - // Set the base address for each memory region by reading ELF headers in - // process memory. - void SetBaseAddressesForMemoryRegions() { - base::ScopedFD mem_fd( - HANDLE_EINTR(open("/proc/self/mem", O_RDONLY | O_CLOEXEC))); - if (!mem_fd.is_valid()) - return; - - auto safe_memcpy = [&mem_fd](void* dst, uintptr_t src, size_t size) { - return HANDLE_EINTR(pread(mem_fd.get(), dst, size, src)) == ssize_t(size); - }; - - uintptr_t cur_base = 0; - for (auto& r : regions_) { - ElfW(Ehdr) ehdr; - static_assert(SELFMAG <= sizeof(ElfW(Ehdr)), "SELFMAG too large"); - if ((r.permissions & MappedMemoryRegion::READ) && - safe_memcpy(&ehdr, r.start, sizeof(ElfW(Ehdr))) && - memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) { - switch (ehdr.e_type) { - case ET_EXEC: - cur_base = 0; - break; - case ET_DYN: - // Find the segment containing file offset 0. This will correspond - // to the ELF header that we just read. Normally this will have - // virtual address 0, but this is not guaranteed. We must subtract - // the virtual address from the address where the ELF header was - // mapped to get the base address. - // - // If we fail to find a segment for file offset 0, use the address - // of the ELF header as the base address. - cur_base = r.start; - for (unsigned i = 0; i != ehdr.e_phnum; ++i) { - ElfW(Phdr) phdr; - if (safe_memcpy(&phdr, r.start + ehdr.e_phoff + i * sizeof(phdr), - sizeof(phdr)) && - phdr.p_type == PT_LOAD && phdr.p_offset == 0) { - cur_base = r.start - phdr.p_vaddr; - break; - } - } - break; - default: - // ET_REL or ET_CORE. These aren't directly executable, so they - // don't affect the base address. - break; - } - } - - r.base = cur_base; - } - } - - // 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; - } - - SetBaseAddressesForMemoryRegions(); - - 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 safe for production builds. Hence it is only done in non-official - // builds. For more details, take a look at: http://crbug.com/341966. -#if !defined(OFFICIAL_BUILD) - // Open the object files for all read-only executable regions and cache - // their file descriptors. - std::vector::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(OFFICIAL_BUILD) - } - - // Initializes and installs the symbolization callback. - void Init() { - if (CacheMemoryRegions()) { - OpenSymbolFiles(); - google::InstallSymbolizeOpenObjectFileCallback( - &OpenObjectFileContainingPc); - } - } - - // Unregister symbolization callback. - void UnregisterCallback() { - if (is_initialized_) { - google::InstallSymbolizeOpenObjectFileCallback(nullptr); - is_initialized_ = false; - } - } - - // Closes all file descriptors owned by this instance. - void CloseObjectFiles() { -#if !defined(OFFICIAL_BUILD) - std::map::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(OFFICIAL_BUILD) - } - - // Set to true upon successful initialization. - bool is_initialized_; - -#if !defined(OFFICIAL_BUILD) - // 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 production builds. - std::map modules_; -#endif // !defined(OFFICIAL_BUILD) - - // Cache for the process memory regions. Produced by parsing the contents - // of /proc/self/maps cache. - std::vector regions_; - - DISALLOW_COPY_AND_ASSIGN(SandboxSymbolizeHelper); -}; -#endif // USE_SYMBOLIZE - -} // namespace - -bool EnableInProcessStackDumping() { -#if defined(USE_SYMBOLIZE) - SandboxSymbolizeHelper::GetInstance(); -#endif // USE_SYMBOLIZE - - // 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 sigpipe_action; - memset(&sigpipe_action, 0, sizeof(sigpipe_action)); - sigpipe_action.sa_handler = SIG_IGN; - sigemptyset(&sigpipe_action.sa_mask); - bool success = (sigaction(SIGPIPE, &sigpipe_action, nullptr) == 0); - - // Avoid hangs during backtrace initialization, see above. - WarmUpBacktrace(); - - struct sigaction action; - memset(&action, 0, sizeof(action)); - action.sa_flags = SA_RESETHAND | SA_SIGINFO; - action.sa_sigaction = &StackDumpSignalHandler; - sigemptyset(&action.sa_mask); - - success &= (sigaction(SIGILL, &action, nullptr) == 0); - success &= (sigaction(SIGABRT, &action, nullptr) == 0); - success &= (sigaction(SIGFPE, &action, nullptr) == 0); - success &= (sigaction(SIGBUS, &action, nullptr) == 0); - success &= (sigaction(SIGSEGV, &action, nullptr) == 0); -// On Linux, SIGSYS is reserved by the kernel for seccomp-bpf sandboxing. -#if !defined(OS_LINUX) - success &= (sigaction(SIGSYS, &action, nullptr) == 0); -#endif // !defined(OS_LINUX) - - return success; -} - -void SetStackDumpFirstChanceCallback(bool (*handler)(int, void*, void*)) { - DCHECK(try_handle_signal == nullptr || handler == nullptr); - try_handle_signal = handler; -} - -StackTrace::StackTrace(size_t count) { -// 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__) && !defined(_AIX) - count = std::min(arraysize(trace_), count); - - // Though the backtrace API man page does not list any possible negative - // return values, we take no chance. - count_ = base::saturated_cast(backtrace(trace_, count)); -#else - count_ = 0; -#endif -} - -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. - -#if !defined(__UCLIBC__) && !defined(_AIX) - PrintBacktraceOutputHandler handler; - ProcessBacktrace(trace_, count_, &handler); -#endif -} - -#if !defined(__UCLIBC__) && !defined(_AIX) -void StackTrace::OutputToStream(std::ostream* os) const { - StreamBacktraceOutputHandler handler(os); - ProcessBacktrace(trace_, count_, &handler); -} -#endif - -namespace internal { - -// 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 nullptr; - - if (base < 2 || base > 16) { - buf[0] = '\000'; - return nullptr; - } - - char* start = buf; - - uintptr_t j = i; - - // Handle negative numbers (only for base 10). - if (i < 0 && base == 10) { - // This does "j = -i" while avoiding integer overflow. - j = static_cast(-(i + 1)) + 1; - - // Make sure we can write the '-' character. - if (++n > sz) { - buf[0] = '\000'; - return nullptr; - } - *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 nullptr; - } - - // 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; -} - -} // namespace internal - -} // namespace debug -} // namespace base diff --git a/debug/stack_trace_unittest.cc b/debug/stack_trace_unittest.cc deleted file mode 100644 index 959cd533c..000000000 --- a/debug/stack_trace_unittest.cc +++ /dev/null @@ -1,320 +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 - -#include -#include -#include - -#include "base/debug/debugging_buildflags.h" -#include "base/debug/stack_trace.h" -#include "base/logging.h" -#include "base/process/kill.h" -#include "base/process/process_handle.h" -#include "base/test/test_timeouts.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/multiprocess_func_list.h" - -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_IOS) -#include "base/test/multiprocess_test.h" -#endif - -namespace base { -namespace debug { - -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_IOS) -typedef MultiProcessTest StackTraceTest; -#else -typedef testing::Test StackTraceTest; -#endif - -// Note: On Linux, this test currently only fully works on Debug builds. -// See comments in the #ifdef soup if you intend to change this. -#if defined(OS_WIN) -// Always fails on Windows: crbug.com/32070 -#define MAYBE_OutputToStream DISABLED_OutputToStream -#else -#define MAYBE_OutputToStream OutputToStream -#endif -#if !defined(__UCLIBC__) && !defined(_AIX) -TEST_F(StackTraceTest, MAYBE_OutputToStream) { - StackTrace trace; - - // Dump the trace into a string. - std::ostringstream os; - trace.OutputToStream(&os); - std::string backtrace_message = os.str(); - - // ToString() should produce the same output. - EXPECT_EQ(backtrace_message, trace.ToString()); - -#if defined(OS_POSIX) && !defined(OS_MACOSX) && NDEBUG - // Stack traces require an extra data table that bloats our binaries, - // so they're turned off for release builds. We stop the test here, - // at least letting us verify that the calls don't crash. - return; -#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && NDEBUG - - size_t frames_found = 0; - trace.Addresses(&frames_found); - ASSERT_GE(frames_found, 5u) << - "No stack frames found. Skipping rest of test."; - - // Check if the output has symbol initialization warning. If it does, fail. - ASSERT_EQ(backtrace_message.find("Dumping unresolved backtrace"), - std::string::npos) << - "Unable to resolve symbols. Skipping rest of test."; - -#if defined(OS_MACOSX) -#if 0 - // Disabled due to -fvisibility=hidden in build config. - - // Symbol resolution via the backtrace_symbol function does not work well - // in OS X. - // See this thread: - // - // http://lists.apple.com/archives/darwin-dev/2009/Mar/msg00111.html - // - // Just check instead that we find our way back to the "start" symbol - // which should be the first symbol in the trace. - // - // TODO(port): Find a more reliable way to resolve symbols. - - // Expect to at least find main. - EXPECT_TRUE(backtrace_message.find("start") != std::string::npos) - << "Expected to find start in backtrace:\n" - << backtrace_message; - -#endif -#elif defined(USE_SYMBOLIZE) - // This branch is for gcc-compiled code, but not Mac due to the - // above #if. - // Expect a demangled symbol. - EXPECT_TRUE(backtrace_message.find("testing::Test::Run()") != - std::string::npos) - << "Expected a demangled symbol in backtrace:\n" - << backtrace_message; - -#elif 0 - // This is the fall-through case; it used to cover Windows. - // But it's disabled because of varying buildbot configs; - // some lack symbols. - - // Expect to at least find main. - EXPECT_TRUE(backtrace_message.find("main") != std::string::npos) - << "Expected to find main in backtrace:\n" - << backtrace_message; - -#if defined(OS_WIN) -// MSVC doesn't allow the use of C99's __func__ within C++, so we fake it with -// MSVC's __FUNCTION__ macro. -#define __func__ __FUNCTION__ -#endif - - // Expect to find this function as well. - // Note: This will fail if not linked with -rdynamic (aka -export_dynamic) - EXPECT_TRUE(backtrace_message.find(__func__) != std::string::npos) - << "Expected to find " << __func__ << " in backtrace:\n" - << backtrace_message; - -#endif // define(OS_MACOSX) -} - -#if !defined(OFFICIAL_BUILD) && !defined(NO_UNWIND_TABLES) -// Disabled in Official builds, where Link-Time Optimization can result in two -// or fewer stack frames being available, causing the test to fail. -TEST_F(StackTraceTest, TruncatedTrace) { - StackTrace trace; - - size_t count = 0; - trace.Addresses(&count); - ASSERT_LT(2u, count); - - StackTrace truncated(2); - truncated.Addresses(&count); - EXPECT_EQ(2u, count); -} -#endif // !defined(OFFICIAL_BUILD) - -// The test is used for manual testing, e.g., to see the raw output. -TEST_F(StackTraceTest, DebugOutputToStream) { - StackTrace trace; - std::ostringstream os; - trace.OutputToStream(&os); - VLOG(1) << os.str(); -} - -// The test is used for manual testing, e.g., to see the raw output. -TEST_F(StackTraceTest, DebugPrintBacktrace) { - StackTrace().Print(); -} -#endif // !defined(__UCLIBC__) - -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) -#if !defined(OS_IOS) -static char* newArray() { - // Clang warns about the mismatched new[]/delete if they occur in the same - // function. - return new char[10]; -} - -MULTIPROCESS_TEST_MAIN(MismatchedMallocChildProcess) { - char* pointer = newArray(); - delete pointer; - return 2; -} - -// Regression test for StackDumpingSignalHandler async-signal unsafety. -// Combined with tcmalloc's debugallocation, that signal handler -// and e.g. mismatched new[]/delete would cause a hang because -// of re-entering malloc. -TEST_F(StackTraceTest, AsyncSignalUnsafeSignalHandlerHang) { - Process child = SpawnChild("MismatchedMallocChildProcess"); - ASSERT_TRUE(child.IsValid()); - int exit_code; - ASSERT_TRUE( - child.WaitForExitWithTimeout(TestTimeouts::action_timeout(), &exit_code)); -} -#endif // !defined(OS_IOS) - -namespace { - -std::string itoa_r_wrapper(intptr_t i, size_t sz, int base, size_t padding) { - char buffer[1024]; - CHECK_LE(sz, sizeof(buffer)); - - char* result = internal::itoa_r(i, buffer, sz, base, padding); - EXPECT_TRUE(result); - return std::string(buffer); -} - -} // namespace - -TEST_F(StackTraceTest, itoa_r) { - EXPECT_EQ("0", itoa_r_wrapper(0, 128, 10, 0)); - EXPECT_EQ("-1", itoa_r_wrapper(-1, 128, 10, 0)); - - // Test edge cases. - if (sizeof(intptr_t) == 4) { - EXPECT_EQ("ffffffff", itoa_r_wrapper(-1, 128, 16, 0)); - EXPECT_EQ("-2147483648", - itoa_r_wrapper(std::numeric_limits::min(), 128, 10, 0)); - EXPECT_EQ("2147483647", - itoa_r_wrapper(std::numeric_limits::max(), 128, 10, 0)); - - EXPECT_EQ("80000000", - itoa_r_wrapper(std::numeric_limits::min(), 128, 16, 0)); - EXPECT_EQ("7fffffff", - itoa_r_wrapper(std::numeric_limits::max(), 128, 16, 0)); - } else if (sizeof(intptr_t) == 8) { - EXPECT_EQ("ffffffffffffffff", itoa_r_wrapper(-1, 128, 16, 0)); - EXPECT_EQ("-9223372036854775808", - itoa_r_wrapper(std::numeric_limits::min(), 128, 10, 0)); - EXPECT_EQ("9223372036854775807", - itoa_r_wrapper(std::numeric_limits::max(), 128, 10, 0)); - - EXPECT_EQ("8000000000000000", - itoa_r_wrapper(std::numeric_limits::min(), 128, 16, 0)); - EXPECT_EQ("7fffffffffffffff", - itoa_r_wrapper(std::numeric_limits::max(), 128, 16, 0)); - } else { - ADD_FAILURE() << "Missing test case for your size of intptr_t (" - << sizeof(intptr_t) << ")"; - } - - // Test hex output. - EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 0)); - EXPECT_EQ("deadbeef", itoa_r_wrapper(0xdeadbeef, 128, 16, 0)); - - // Check that itoa_r respects passed buffer size limit. - char buffer[1024]; - EXPECT_TRUE(internal::itoa_r(0xdeadbeef, buffer, 10, 16, 0)); - EXPECT_TRUE(internal::itoa_r(0xdeadbeef, buffer, 9, 16, 0)); - EXPECT_FALSE(internal::itoa_r(0xdeadbeef, buffer, 8, 16, 0)); - EXPECT_FALSE(internal::itoa_r(0xdeadbeef, buffer, 7, 16, 0)); - EXPECT_TRUE(internal::itoa_r(0xbeef, buffer, 5, 16, 4)); - EXPECT_FALSE(internal::itoa_r(0xbeef, buffer, 5, 16, 5)); - EXPECT_FALSE(internal::itoa_r(0xbeef, buffer, 5, 16, 6)); - - // Test padding. - EXPECT_EQ("1", itoa_r_wrapper(1, 128, 10, 0)); - EXPECT_EQ("1", itoa_r_wrapper(1, 128, 10, 1)); - EXPECT_EQ("01", itoa_r_wrapper(1, 128, 10, 2)); - EXPECT_EQ("001", itoa_r_wrapper(1, 128, 10, 3)); - EXPECT_EQ("0001", itoa_r_wrapper(1, 128, 10, 4)); - EXPECT_EQ("00001", itoa_r_wrapper(1, 128, 10, 5)); - EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 0)); - EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 1)); - EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 2)); - EXPECT_EQ("688", itoa_r_wrapper(0x688, 128, 16, 3)); - EXPECT_EQ("0688", itoa_r_wrapper(0x688, 128, 16, 4)); - EXPECT_EQ("00688", itoa_r_wrapper(0x688, 128, 16, 5)); -} -#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) - -#if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -template -void NOINLINE ExpectStackFramePointers(const void** frames, - size_t max_depth) { - code_start: - // Calling __builtin_frame_address() forces compiler to emit - // frame pointers, even if they are not enabled. - EXPECT_NE(nullptr, __builtin_frame_address(0)); - ExpectStackFramePointers(frames, max_depth); - - constexpr size_t frame_index = Depth - 1; - const void* frame = frames[frame_index]; - EXPECT_GE(frame, &&code_start) << "For frame at index " << frame_index; - EXPECT_LE(frame, &&code_end) << "For frame at index " << frame_index; - code_end: return; -} - -template <> -void NOINLINE ExpectStackFramePointers<1>(const void** frames, - size_t max_depth) { - code_start: - // Calling __builtin_frame_address() forces compiler to emit - // frame pointers, even if they are not enabled. - EXPECT_NE(nullptr, __builtin_frame_address(0)); - size_t count = TraceStackFramePointers(frames, max_depth, 0); - ASSERT_EQ(max_depth, count); - - const void* frame = frames[0]; - EXPECT_GE(frame, &&code_start) << "For the top frame"; - EXPECT_LE(frame, &&code_end) << "For the top frame"; - code_end: return; -} - -#if defined(MEMORY_SANITIZER) -// The test triggers use-of-uninitialized-value errors on MSan bots. -// This is expected because we're walking and reading the stack, and -// sometimes we read fp / pc from the place that previously held -// uninitialized value. -#define MAYBE_TraceStackFramePointers DISABLED_TraceStackFramePointers -#else -#define MAYBE_TraceStackFramePointers TraceStackFramePointers -#endif -TEST_F(StackTraceTest, MAYBE_TraceStackFramePointers) { - constexpr size_t kDepth = 5; - const void* frames[kDepth]; - ExpectStackFramePointers(frames, kDepth); -} - -#if defined(OS_ANDROID) || defined(OS_MACOSX) -#define MAYBE_StackEnd StackEnd -#else -#define MAYBE_StackEnd DISABLED_StackEnd -#endif - -TEST_F(StackTraceTest, MAYBE_StackEnd) { - EXPECT_NE(0u, GetStackEnd()); -} - -#endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) - -} // namespace debug -} // namespace base diff --git a/debug/stack_trace_win.cc b/debug/stack_trace_win.cc deleted file mode 100644 index 1ef2a0670..000000000 --- a/debug/stack_trace_win.cc +++ /dev/null @@ -1,365 +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/debug/stack_trace.h" - -#include -#include -#include - -#include -#include -#include - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/singleton.h" -#include "base/synchronization/lock.h" - -namespace base { -namespace debug { - -namespace { - -// Previous unhandled filter. Will be called if not NULL when we intercept an -// exception. Only used in unit tests. -LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = NULL; - -bool g_initialized_symbols = false; -DWORD g_init_error = ERROR_SUCCESS; - -// Prints the exception call stack. -// This is the unit tests exception filter. -long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) { - DWORD exc_code = info->ExceptionRecord->ExceptionCode; - std::cerr << "Received fatal exception "; - switch (exc_code) { - case EXCEPTION_ACCESS_VIOLATION: - std::cerr << "EXCEPTION_ACCESS_VIOLATION"; - break; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - std::cerr << "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; - break; - case EXCEPTION_BREAKPOINT: - std::cerr << "EXCEPTION_BREAKPOINT"; - break; - case EXCEPTION_DATATYPE_MISALIGNMENT: - std::cerr << "EXCEPTION_DATATYPE_MISALIGNMENT"; - break; - case EXCEPTION_FLT_DENORMAL_OPERAND: - std::cerr << "EXCEPTION_FLT_DENORMAL_OPERAND"; - break; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - std::cerr << "EXCEPTION_FLT_DIVIDE_BY_ZERO"; - break; - case EXCEPTION_FLT_INEXACT_RESULT: - std::cerr << "EXCEPTION_FLT_INEXACT_RESULT"; - break; - case EXCEPTION_FLT_INVALID_OPERATION: - std::cerr << "EXCEPTION_FLT_INVALID_OPERATION"; - break; - case EXCEPTION_FLT_OVERFLOW: - std::cerr << "EXCEPTION_FLT_OVERFLOW"; - break; - case EXCEPTION_FLT_STACK_CHECK: - std::cerr << "EXCEPTION_FLT_STACK_CHECK"; - break; - case EXCEPTION_FLT_UNDERFLOW: - std::cerr << "EXCEPTION_FLT_UNDERFLOW"; - break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - std::cerr << "EXCEPTION_ILLEGAL_INSTRUCTION"; - break; - case EXCEPTION_IN_PAGE_ERROR: - std::cerr << "EXCEPTION_IN_PAGE_ERROR"; - break; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - std::cerr << "EXCEPTION_INT_DIVIDE_BY_ZERO"; - break; - case EXCEPTION_INT_OVERFLOW: - std::cerr << "EXCEPTION_INT_OVERFLOW"; - break; - case EXCEPTION_INVALID_DISPOSITION: - std::cerr << "EXCEPTION_INVALID_DISPOSITION"; - break; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - std::cerr << "EXCEPTION_NONCONTINUABLE_EXCEPTION"; - break; - case EXCEPTION_PRIV_INSTRUCTION: - std::cerr << "EXCEPTION_PRIV_INSTRUCTION"; - break; - case EXCEPTION_SINGLE_STEP: - std::cerr << "EXCEPTION_SINGLE_STEP"; - break; - case EXCEPTION_STACK_OVERFLOW: - std::cerr << "EXCEPTION_STACK_OVERFLOW"; - break; - default: - std::cerr << "0x" << std::hex << exc_code; - break; - } - std::cerr << "\n"; - - debug::StackTrace(info).Print(); - if (g_previous_filter) - return g_previous_filter(info); - return EXCEPTION_CONTINUE_SEARCH; -} - -FilePath GetExePath() { - wchar_t system_buffer[MAX_PATH]; - GetModuleFileName(NULL, system_buffer, MAX_PATH); - system_buffer[MAX_PATH - 1] = L'\0'; - return FilePath(system_buffer); -} - -bool InitializeSymbols() { - if (g_initialized_symbols) - return g_init_error == ERROR_SUCCESS; - g_initialized_symbols = true; - // Defer symbol load until they're needed, use undecorated names, and get line - // numbers. - SymSetOptions(SYMOPT_DEFERRED_LOADS | - SYMOPT_UNDNAME | - SYMOPT_LOAD_LINES); - if (!SymInitialize(GetCurrentProcess(), NULL, TRUE)) { - g_init_error = GetLastError(); - // TODO(awong): Handle error: SymInitialize can fail with - // ERROR_INVALID_PARAMETER. - // When it fails, we should not call debugbreak since it kills the current - // process (prevents future tests from running or kills the browser - // process). - DLOG(ERROR) << "SymInitialize failed: " << g_init_error; - return false; - } - - // When transferring the binaries e.g. between bots, path put - // into the executable will get off. To still retrieve symbols correctly, - // add the directory of the executable to symbol search path. - // All following errors are non-fatal. - const size_t kSymbolsArraySize = 1024; - std::unique_ptr symbols_path(new wchar_t[kSymbolsArraySize]); - - // Note: The below function takes buffer size as number of characters, - // not number of bytes! - if (!SymGetSearchPathW(GetCurrentProcess(), - symbols_path.get(), - kSymbolsArraySize)) { - g_init_error = GetLastError(); - DLOG(WARNING) << "SymGetSearchPath failed: " << g_init_error; - return false; - } - - std::wstring new_path(std::wstring(symbols_path.get()) + - L";" + GetExePath().DirName().value()); - if (!SymSetSearchPathW(GetCurrentProcess(), new_path.c_str())) { - g_init_error = GetLastError(); - DLOG(WARNING) << "SymSetSearchPath failed." << g_init_error; - return false; - } - - g_init_error = ERROR_SUCCESS; - return true; -} - -// SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family -// of functions. The Sym* family of functions may only be invoked by one -// thread at a time. SymbolContext code may access a symbol server over the -// network while holding the lock for this singleton. In the case of high -// latency, this code will adversely affect performance. -// -// There is also a known issue where this backtrace code can interact -// badly with breakpad if breakpad is invoked in a separate thread while -// we are using the Sym* functions. This is because breakpad does now -// share a lock with this function. See this related bug: -// -// https://crbug.com/google-breakpad/311 -// -// This is a very unlikely edge case, and the current solution is to -// just ignore it. -class SymbolContext { - public: - static SymbolContext* GetInstance() { - // We use a leaky singleton because code may call this during process - // termination. - return - Singleton >::get(); - } - - // For the given trace, attempts to resolve the symbols, and output a trace - // to the ostream os. The format for each line of the backtrace is: - // - // SymbolName[0xAddress+Offset] (FileName:LineNo) - // - // This function should only be called if Init() has been called. We do not - // LOG(FATAL) here because this code is called might be triggered by a - // LOG(FATAL) itself. Also, it should not be calling complex code that is - // extensible like PathService since that can in turn fire CHECKs. - void OutputTraceToStream(const void* const* trace, - size_t count, - std::ostream* os) { - base::AutoLock lock(lock_); - - for (size_t i = 0; (i < count) && os->good(); ++i) { - const int kMaxNameLength = 256; - DWORD_PTR frame = reinterpret_cast(trace[i]); - - // Code adapted from MSDN example: - // http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx - ULONG64 buffer[ - (sizeof(SYMBOL_INFO) + - kMaxNameLength * sizeof(wchar_t) + - sizeof(ULONG64) - 1) / - sizeof(ULONG64)]; - memset(buffer, 0, sizeof(buffer)); - - // Initialize symbol information retrieval structures. - DWORD64 sym_displacement = 0; - PSYMBOL_INFO symbol = reinterpret_cast(&buffer[0]); - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - symbol->MaxNameLen = kMaxNameLength - 1; - BOOL has_symbol = SymFromAddr(GetCurrentProcess(), frame, - &sym_displacement, symbol); - - // Attempt to retrieve line number information. - DWORD line_displacement = 0; - IMAGEHLP_LINE64 line = {}; - line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - BOOL has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame, - &line_displacement, &line); - - // Output the backtrace line. - (*os) << "\t"; - if (has_symbol) { - (*os) << symbol->Name << " [0x" << trace[i] << "+" - << sym_displacement << "]"; - } else { - // If there is no symbol information, add a spacer. - (*os) << "(No symbol) [0x" << trace[i] << "]"; - } - if (has_line) { - (*os) << " (" << line.FileName << ":" << line.LineNumber << ")"; - } - (*os) << "\n"; - } - } - - private: - friend struct DefaultSingletonTraits; - - SymbolContext() { - InitializeSymbols(); - } - - base::Lock lock_; - DISALLOW_COPY_AND_ASSIGN(SymbolContext); -}; - -} // namespace - -bool EnableInProcessStackDumping() { - // Add stack dumping support on exception on windows. Similar to OS_POSIX - // signal() handling in process_util_posix.cc. - g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter); - - // Need to initialize symbols early in the process or else this fails on - // swarming (since symbols are in different directory than in the exes) and - // also release x64. - return InitializeSymbols(); -} - -// Disable optimizations for the StackTrace::StackTrace function. It is -// important to disable at least frame pointer optimization ("y"), since -// that breaks CaptureStackBackTrace() and prevents StackTrace from working -// in Release builds (it may still be janky if other frames are using FPO, -// but at least it will make it further). -#if defined(COMPILER_MSVC) -#pragma optimize("", off) -#endif - -StackTrace::StackTrace(size_t count) { - count = std::min(arraysize(trace_), count); - - // When walking our own stack, use CaptureStackBackTrace(). - count_ = CaptureStackBackTrace(0, count, trace_, NULL); -} - -#if defined(COMPILER_MSVC) -#pragma optimize("", on) -#endif - -StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) { - InitTrace(exception_pointers->ContextRecord); -} - -StackTrace::StackTrace(const CONTEXT* context) { - InitTrace(context); -} - -void StackTrace::InitTrace(const CONTEXT* context_record) { - // StackWalk64 modifies the register context in place, so we have to copy it - // so that downstream exception handlers get the right context. The incoming - // context may have had more register state (YMM, etc) than we need to unwind - // the stack. Typically StackWalk64 only needs integer and control registers. - CONTEXT context_copy; - memcpy(&context_copy, context_record, sizeof(context_copy)); - context_copy.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; - - // When walking an exception stack, we need to use StackWalk64(). - count_ = 0; - // Initialize stack walking. - STACKFRAME64 stack_frame; - memset(&stack_frame, 0, sizeof(stack_frame)); -#if defined(_WIN64) - int machine_type = IMAGE_FILE_MACHINE_AMD64; - stack_frame.AddrPC.Offset = context_record->Rip; - stack_frame.AddrFrame.Offset = context_record->Rbp; - stack_frame.AddrStack.Offset = context_record->Rsp; -#else - int machine_type = IMAGE_FILE_MACHINE_I386; - stack_frame.AddrPC.Offset = context_record->Eip; - stack_frame.AddrFrame.Offset = context_record->Ebp; - stack_frame.AddrStack.Offset = context_record->Esp; -#endif - stack_frame.AddrPC.Mode = AddrModeFlat; - stack_frame.AddrFrame.Mode = AddrModeFlat; - stack_frame.AddrStack.Mode = AddrModeFlat; - while (StackWalk64(machine_type, - GetCurrentProcess(), - GetCurrentThread(), - &stack_frame, - &context_copy, - NULL, - &SymFunctionTableAccess64, - &SymGetModuleBase64, - NULL) && - count_ < arraysize(trace_)) { - trace_[count_++] = reinterpret_cast(stack_frame.AddrPC.Offset); - } - - for (size_t i = count_; i < arraysize(trace_); ++i) - trace_[i] = NULL; -} - -void StackTrace::Print() const { - OutputToStream(&std::cerr); -} - -void StackTrace::OutputToStream(std::ostream* os) const { - SymbolContext* context = SymbolContext::GetInstance(); - if (g_init_error != ERROR_SUCCESS) { - (*os) << "Error initializing symbols (" << g_init_error - << "). Dumping unresolved backtrace:\n"; - for (size_t i = 0; (i < count_) && os->good(); ++i) { - (*os) << "\t" << trace_[i] << "\n"; - } - } else { - (*os) << "Backtrace:\n"; - context->OutputTraceToStream(trace_, count_, os); - } -} - -} // namespace debug -} // namespace base diff --git a/debug/task_annotator.cc b/debug/task_annotator.cc deleted file mode 100644 index 18083c120..000000000 --- a/debug/task_annotator.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/debug/task_annotator.h" - -#include - -#include "base/debug/activity_tracker.h" -#include "base/debug/alias.h" -#include "base/no_destructor.h" -#include "base/pending_task.h" -#include "base/threading/thread_local.h" -#include "base/trace_event/trace_event.h" - -namespace base { -namespace debug { - -namespace { - -TaskAnnotator::ObserverForTesting* g_task_annotator_observer = nullptr; - -// Returns the TLS slot that stores the PendingTask currently in progress on -// each thread. Used to allow creating a breadcrumb of program counters on the -// stack to help identify a task's origin in crashes. -ThreadLocalPointer* GetTLSForCurrentPendingTask() { - static NoDestructor> - tls_for_current_pending_task; - return tls_for_current_pending_task.get(); -} - -} // namespace - -TaskAnnotator::TaskAnnotator() = default; - -TaskAnnotator::~TaskAnnotator() = default; - -void TaskAnnotator::WillQueueTask(const char* queue_function, - PendingTask* pending_task) { - if (queue_function) { - TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), - queue_function, - TRACE_ID_MANGLE(GetTaskTraceID(*pending_task)), - TRACE_EVENT_FLAG_FLOW_OUT); - } - - // TODO(https://crbug.com/826902): Fix callers that invoke WillQueueTask() - // twice for the same PendingTask. - // DCHECK(!pending_task.task_backtrace[0]) - // << "Task backtrace was already set, task posted twice??"; - if (!pending_task->task_backtrace[0]) { - const PendingTask* parent_task = GetTLSForCurrentPendingTask()->Get(); - if (parent_task) { - pending_task->task_backtrace[0] = - parent_task->posted_from.program_counter(); - std::copy(parent_task->task_backtrace.begin(), - parent_task->task_backtrace.end() - 1, - pending_task->task_backtrace.begin() + 1); - } - } -} - -void TaskAnnotator::RunTask(const char* queue_function, - PendingTask* pending_task) { - ScopedTaskRunActivity task_activity(*pending_task); - - if (queue_function) { - TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), - queue_function, - TRACE_ID_MANGLE(GetTaskTraceID(*pending_task)), - TRACE_EVENT_FLAG_FLOW_IN); - } - - // Before running the task, store the task backtrace with the chain of - // PostTasks that resulted in this call and deliberately alias it to ensure - // it is on the stack if the task crashes. Be careful not to assume that the - // variable itself will have the expected value when displayed by the - // optimizer in an optimized build. Look at a memory dump of the stack. - static constexpr int kStackTaskTraceSnapshotSize = - std::tuple_sizetask_backtrace)>::value + 3; - std::array task_backtrace; - - // Store a marker to locate |task_backtrace| content easily on a memory - // dump. - task_backtrace.front() = reinterpret_cast(0xefefefefefefefef); - task_backtrace.back() = reinterpret_cast(0xfefefefefefefefe); - - task_backtrace[1] = pending_task->posted_from.program_counter(); - std::copy(pending_task->task_backtrace.begin(), - pending_task->task_backtrace.end(), task_backtrace.begin() + 2); - debug::Alias(&task_backtrace); - - ThreadLocalPointer* tls_for_current_pending_task = - GetTLSForCurrentPendingTask(); - const PendingTask* previous_pending_task = - tls_for_current_pending_task->Get(); - tls_for_current_pending_task->Set(pending_task); - - if (g_task_annotator_observer) - g_task_annotator_observer->BeforeRunTask(pending_task); - std::move(pending_task->task).Run(); - - tls_for_current_pending_task->Set(previous_pending_task); -} - -uint64_t TaskAnnotator::GetTaskTraceID(const PendingTask& task) const { - return (static_cast(task.sequence_num) << 32) | - ((static_cast(reinterpret_cast(this)) << 32) >> - 32); -} - -// static -void TaskAnnotator::RegisterObserverForTesting(ObserverForTesting* observer) { - DCHECK(!g_task_annotator_observer); - g_task_annotator_observer = observer; -} - -// static -void TaskAnnotator::ClearObserverForTesting() { - g_task_annotator_observer = nullptr; -} - -} // namespace debug -} // namespace base diff --git a/debug/task_annotator.h b/debug/task_annotator.h deleted file mode 100644 index fedca7d59..000000000 --- a/debug/task_annotator.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_DEBUG_TASK_ANNOTATOR_H_ -#define BASE_DEBUG_TASK_ANNOTATOR_H_ - -#include - -#include "base/base_export.h" -#include "base/macros.h" - -namespace base { -struct PendingTask; -namespace debug { - -// Implements common debug annotations for posted tasks. This includes data -// such as task origins, queueing durations and memory usage. -class BASE_EXPORT TaskAnnotator { - public: - class ObserverForTesting { - public: - // Invoked just before RunTask() in the scope in which the task is about to - // be executed. - virtual void BeforeRunTask(const PendingTask* pending_task) = 0; - }; - - TaskAnnotator(); - ~TaskAnnotator(); - - // Called to indicate that a task is about to be queued to run in the future, - // giving one last chance for this TaskAnnotator to add metadata to - // |pending_task| before it is moved into the queue. |queue_function| is used - // as the trace flow event name. |queue_function| can be null if the caller - // doesn't want trace flow events logged to toplevel.flow. - void WillQueueTask(const char* queue_function, PendingTask* pending_task); - - // Run a previously queued task. |queue_function| should match what was - // passed into |DidQueueTask| for this task. - void RunTask(const char* queue_function, PendingTask* pending_task); - - // Creates a process-wide unique ID to represent this task in trace events. - // This will be mangled with a Process ID hash to reduce the likelyhood of - // colliding with TaskAnnotator pointers on other processes. Callers may use - // this when generating their own flow events (i.e. when passing - // |queue_function == nullptr| in above methods). - uint64_t GetTaskTraceID(const PendingTask& task) const; - - private: - friend class TaskAnnotatorBacktraceIntegrationTest; - - // Registers an ObserverForTesting that will be invoked by all TaskAnnotators' - // RunTask(). This registration and the implementation of BeforeRunTask() are - // responsible to ensure thread-safety. - static void RegisterObserverForTesting(ObserverForTesting* observer); - static void ClearObserverForTesting(); - - DISALLOW_COPY_AND_ASSIGN(TaskAnnotator); -}; - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_TASK_ANNOTATOR_H_ diff --git a/debug/task_annotator_unittest.cc b/debug/task_annotator_unittest.cc deleted file mode 100644 index 2f07bbd15..000000000 --- a/debug/task_annotator_unittest.cc +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/debug/task_annotator.h" - -#include -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/pending_task.h" -#include "base/run_loop.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/waitable_event.h" -#include "base/task_scheduler/post_task.h" -#include "base/test/scoped_task_environment.h" -#include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace debug { -namespace { - -void TestTask(int* result) { - *result = 123; -} - -} // namespace - -TEST(TaskAnnotatorTest, QueueAndRunTask) { - int result = 0; - PendingTask pending_task(FROM_HERE, BindOnce(&TestTask, &result)); - - TaskAnnotator annotator; - annotator.WillQueueTask("TaskAnnotatorTest::Queue", &pending_task); - EXPECT_EQ(0, result); - annotator.RunTask("TaskAnnotatorTest::Queue", &pending_task); - EXPECT_EQ(123, result); -} - -// Test task annotator integration in base APIs and ensuing support for -// backtraces. Tasks posted across multiple threads in this test fixture should -// be synchronized as BeforeRunTask() and VerifyTraceAndPost() assume tasks are -// observed in lock steps, one at a time. -class TaskAnnotatorBacktraceIntegrationTest - : public ::testing::Test, - public TaskAnnotator::ObserverForTesting { - public: - using ExpectedTrace = std::vector; - - TaskAnnotatorBacktraceIntegrationTest() = default; - - ~TaskAnnotatorBacktraceIntegrationTest() override = default; - - // TaskAnnotator::ObserverForTesting: - void BeforeRunTask(const PendingTask* pending_task) override { - AutoLock auto_lock(on_before_run_task_lock_); - last_posted_from_ = pending_task->posted_from; - last_task_backtrace_ = pending_task->task_backtrace; - } - - void SetUp() override { TaskAnnotator::RegisterObserverForTesting(this); } - - void TearDown() override { TaskAnnotator::ClearObserverForTesting(); } - - void VerifyTraceAndPost(const scoped_refptr& task_runner, - const Location& posted_from, - const Location& next_from_here, - const ExpectedTrace& expected_trace, - OnceClosure task) { - SCOPED_TRACE(StringPrintf("Callback Depth: %zu", expected_trace.size())); - - EXPECT_EQ(posted_from, last_posted_from_); - for (size_t i = 0; i < last_task_backtrace_.size(); i++) { - SCOPED_TRACE(StringPrintf("Trace frame: %zu", i)); - if (i < expected_trace.size()) - EXPECT_EQ(expected_trace[i], last_task_backtrace_[i]); - else - EXPECT_EQ(nullptr, last_task_backtrace_[i]); - } - - task_runner->PostTask(next_from_here, std::move(task)); - } - - // Same as VerifyTraceAndPost() with the exception that it also posts a task - // that will prevent |task| from running until |wait_before_next_task| is - // signaled. - void VerifyTraceAndPostWithBlocker( - const scoped_refptr& task_runner, - const Location& posted_from, - const Location& next_from_here, - const ExpectedTrace& expected_trace, - OnceClosure task, - WaitableEvent* wait_before_next_task) { - DCHECK(wait_before_next_task); - - // Need to lock to ensure the upcoming VerifyTraceAndPost() runs before the - // BeforeRunTask() hook for the posted WaitableEvent::Wait(). Otherwise the - // upcoming VerifyTraceAndPost() will race to read the state saved in the - // BeforeRunTask() hook preceding the current task. - AutoLock auto_lock(on_before_run_task_lock_); - task_runner->PostTask( - FROM_HERE, - BindOnce(&WaitableEvent::Wait, Unretained(wait_before_next_task))); - VerifyTraceAndPost(task_runner, posted_from, next_from_here, expected_trace, - std::move(task)); - } - - protected: - static void RunTwo(OnceClosure c1, OnceClosure c2) { - std::move(c1).Run(); - std::move(c2).Run(); - } - - private: - // While calls to VerifyTraceAndPost() are strictly ordered in tests below - // (and hence non-racy), some helper methods (e.g. Wait/Signal) do racily call - // into BeforeRunTask(). This Lock ensures these unobserved writes are not - // racing. Locking isn't required on read per the VerifyTraceAndPost() - // themselves being ordered. - Lock on_before_run_task_lock_; - - Location last_posted_from_ = {}; - std::array last_task_backtrace_ = {}; - - DISALLOW_COPY_AND_ASSIGN(TaskAnnotatorBacktraceIntegrationTest); -}; - -// Ensure the task backtrace populates correctly. -TEST_F(TaskAnnotatorBacktraceIntegrationTest, SingleThreadedSimple) { - MessageLoop loop; - const Location location0 = FROM_HERE; - const Location location1 = FROM_HERE; - const Location location2 = FROM_HERE; - const Location location3 = FROM_HERE; - const Location location4 = FROM_HERE; - const Location location5 = FROM_HERE; - - RunLoop run_loop; - - // Task 5 has tasks 4/3/2/1 as parents (task 0 isn't visible as only the - // last 4 parents are kept). - OnceClosure task5 = BindOnce( - &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location5, FROM_HERE, - ExpectedTrace({location4.program_counter(), location3.program_counter(), - location2.program_counter(), location1.program_counter()}), - run_loop.QuitClosure()); - - // Task i=4/3/2/1/0 have tasks [0,i) as parents. - OnceClosure task4 = BindOnce( - &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location4, location5, - ExpectedTrace({location3.program_counter(), location2.program_counter(), - location1.program_counter(), location0.program_counter()}), - std::move(task5)); - OnceClosure task3 = BindOnce( - &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location3, location4, - ExpectedTrace({location2.program_counter(), location1.program_counter(), - location0.program_counter()}), - std::move(task4)); - OnceClosure task2 = BindOnce( - &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location2, location3, - ExpectedTrace({location1.program_counter(), location0.program_counter()}), - std::move(task3)); - OnceClosure task1 = - BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location1, location2, - ExpectedTrace({location0.program_counter()}), std::move(task2)); - OnceClosure task0 = - BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location0, location1, - ExpectedTrace({}), std::move(task1)); - - loop.task_runner()->PostTask(location0, std::move(task0)); - - run_loop.Run(); -} - -// Ensure it works when posting tasks across multiple threads managed by //base. -TEST_F(TaskAnnotatorBacktraceIntegrationTest, MultipleThreads) { - test::ScopedTaskEnvironment scoped_task_environment; - - // Use diverse task runners (a MessageLoop on the main thread, a TaskScheduler - // based SequencedTaskRunner, and a TaskScheduler based - // SingleThreadTaskRunner) to verify that TaskAnnotator can capture backtraces - // for PostTasks back-and-forth between these. - auto main_thread_a = ThreadTaskRunnerHandle::Get(); - auto task_runner_b = CreateSingleThreadTaskRunnerWithTraits({}); - auto task_runner_c = CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::WithBaseSyncPrimitives()}); - - const Location& location_a0 = FROM_HERE; - const Location& location_a1 = FROM_HERE; - const Location& location_a2 = FROM_HERE; - const Location& location_a3 = FROM_HERE; - - const Location& location_b0 = FROM_HERE; - const Location& location_b1 = FROM_HERE; - - const Location& location_c0 = FROM_HERE; - - RunLoop run_loop; - - // All tasks below happen in lock step by nature of being posted by the - // previous one (plus the synchronous nature of RunTwo()) with the exception - // of the follow-up local task to |task_b0_local|. This WaitableEvent ensures - // it completes before |task_c0| runs to avoid racy invocations of - // BeforeRunTask()+VerifyTraceAndPost(). - WaitableEvent lock_step(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - - // Here is the execution order generated below: - // A: TA0 -> TA1 \ TA2 - // B: TB0L \ + TB0F \ Signal \ / - // ---------\--/ \ / - // \ \ / - // C: Wait........ TC0 / - - // On task runner c, post a task back to main thread that verifies its trace - // and terminates after one more self-post. - OnceClosure task_a2 = BindOnce( - &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), main_thread_a, location_a2, location_a3, - ExpectedTrace( - {location_c0.program_counter(), location_b0.program_counter(), - location_a1.program_counter(), location_a0.program_counter()}), - run_loop.QuitClosure()); - OnceClosure task_c0 = - BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), main_thread_a, location_c0, location_a2, - ExpectedTrace({location_b0.program_counter(), - location_a1.program_counter(), - location_a0.program_counter()}), - std::move(task_a2)); - - // On task runner b run two tasks that conceptually come from the same - // location (managed via RunTwo().) One will post back to task runner b and - // another will post to task runner c to test spawning multiple tasks on - // different message loops. The task posted to task runner c will not get - // location b1 whereas the one posted back to task runner b will. - OnceClosure task_b0_fork = BindOnce( - &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPostWithBlocker, - Unretained(this), task_runner_c, location_b0, location_c0, - ExpectedTrace( - {location_a1.program_counter(), location_a0.program_counter()}), - std::move(task_c0), &lock_step); - OnceClosure task_b0_local = - BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), task_runner_b, location_b0, location_b1, - ExpectedTrace({location_a1.program_counter(), - location_a0.program_counter()}), - BindOnce(&WaitableEvent::Signal, Unretained(&lock_step))); - - OnceClosure task_a1 = - BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), task_runner_b, location_a1, location_b0, - ExpectedTrace({location_a0.program_counter()}), - BindOnce(&TaskAnnotatorBacktraceIntegrationTest::RunTwo, - std::move(task_b0_local), std::move(task_b0_fork))); - OnceClosure task_a0 = - BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), main_thread_a, location_a0, location_a1, - ExpectedTrace({}), std::move(task_a1)); - - main_thread_a->PostTask(location_a0, std::move(task_a0)); - - run_loop.Run(); -} - -// Ensure nesting doesn't break the chain. -TEST_F(TaskAnnotatorBacktraceIntegrationTest, SingleThreadedNested) { - MessageLoop loop; - const Location location0 = FROM_HERE; - const Location location1 = FROM_HERE; - const Location location2 = FROM_HERE; - const Location location3 = FROM_HERE; - const Location location4 = FROM_HERE; - const Location location5 = FROM_HERE; - - RunLoop run_loop; - - // Task execution below looks like this, w.r.t. to RunLoop depths: - // 1 : T0 \ + NRL1 \ ---------> T4 -> T5 - // 2 : ---------> T1 \ -> NRL2 \ ----> T2 -> T3 / + Quit / - // 3 : ---------> DN / - - // NRL1 tests that tasks that occur at a different nesting depth than their - // parent have a sane backtrace nonetheless (both ways). - - // NRL2 tests that posting T2 right after exiting the RunLoop (from the same - // task) results in NRL2 being its parent (and not the DoNothing() task that - // just ran -- which would have been the case if the "current task" wasn't - // restored properly when returning from a task within a task). - - // In other words, this is regression test for a bug in the previous - // implementation. In the current implementation, replacing - // tls_for_current_pending_task->Set(previous_pending_task); - // by - // tls_for_current_pending_task->Set(nullptr); - // at the end of TaskAnnotator::RunTask() makes this test fail. - - RunLoop nested_run_loop1(RunLoop::Type::kNestableTasksAllowed); - - // Expectations are the same as in SingleThreadedSimple test despite the - // nested loop starting between tasks 0 and 1 and stopping between tasks 3 and - // 4. - OnceClosure task5 = BindOnce( - &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location5, FROM_HERE, - ExpectedTrace({location4.program_counter(), location3.program_counter(), - location2.program_counter(), location1.program_counter()}), - run_loop.QuitClosure()); - OnceClosure task4 = BindOnce( - &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location4, location5, - ExpectedTrace({location3.program_counter(), location2.program_counter(), - location1.program_counter(), location0.program_counter()}), - std::move(task5)); - OnceClosure task3 = BindOnce( - &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location3, location4, - ExpectedTrace({location2.program_counter(), location1.program_counter(), - location0.program_counter()}), - std::move(task4)); - - OnceClosure run_task_3_then_quit_nested_loop1 = - BindOnce(&TaskAnnotatorBacktraceIntegrationTest::RunTwo, std::move(task3), - nested_run_loop1.QuitClosure()); - - OnceClosure task2 = BindOnce( - &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location2, location3, - ExpectedTrace({location1.program_counter(), location0.program_counter()}), - std::move(run_task_3_then_quit_nested_loop1)); - - // Task 1 is custom. It enters another nested RunLoop, has it do work and exit - // before posting the next task. This confirms that |task1| is restored as the - // current task before posting |task2| after returning from the nested loop. - RunLoop nested_run_loop2(RunLoop::Type::kNestableTasksAllowed); - OnceClosure task1 = BindOnce( - [](RunLoop* nested_run_loop, const Location& location2, - OnceClosure task2) { - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing()); - nested_run_loop->RunUntilIdle(); - ThreadTaskRunnerHandle::Get()->PostTask(location2, std::move(task2)); - }, - Unretained(&nested_run_loop2), location2, std::move(task2)); - - OnceClosure task0 = - BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), loop.task_runner(), location0, location1, - ExpectedTrace({}), std::move(task1)); - - loop.task_runner()->PostTask(location0, std::move(task0)); - loop.task_runner()->PostTask( - FROM_HERE, BindOnce(&RunLoop::Run, Unretained(&nested_run_loop1))); - - run_loop.Run(); -} - -} // namespace debug -} // namespace base diff --git a/debug/thread_heap_usage_tracker.cc b/debug/thread_heap_usage_tracker.cc deleted file mode 100644 index 6d00b1ccb..000000000 --- a/debug/thread_heap_usage_tracker.cc +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2016 The Chromium 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/thread_heap_usage_tracker.h" - -#include -#include -#include -#include -#include - -#include "base/allocator/allocator_shim.h" -#include "base/allocator/buildflags.h" -#include "base/logging.h" -#include "base/no_destructor.h" -#include "base/threading/thread_local_storage.h" -#include "build/build_config.h" - -#if defined(OS_MACOSX) || defined(OS_IOS) -#include -#else -#include -#endif - -namespace base { -namespace debug { - -namespace { - -using base::allocator::AllocatorDispatch; - -const uintptr_t kSentinelMask = std::numeric_limits::max() - 1; -ThreadHeapUsage* const kInitializationSentinel = - reinterpret_cast(kSentinelMask); -ThreadHeapUsage* const kTeardownSentinel = - reinterpret_cast(kSentinelMask | 1); - -ThreadLocalStorage::Slot& ThreadAllocationUsage() { - static NoDestructor thread_allocator_usage( - [](void* thread_heap_usage) { - // This destructor will be called twice. Once to destroy the actual - // ThreadHeapUsage instance and a second time, immediately after, for - // the sentinel. Re-setting the TLS slow (below) does re-initialize the - // TLS slot. The ThreadLocalStorage code is designed to deal with this - // use case and will re-call the destructor with the kTeardownSentinel - // as arg. - if (thread_heap_usage == kTeardownSentinel) - return; - DCHECK_NE(thread_heap_usage, kInitializationSentinel); - - // Deleting the ThreadHeapUsage TLS object will re-enter the shim and - // hit RecordFree() (see below). The sentinel prevents RecordFree() from - // re-creating another ThreadHeapUsage object. - ThreadAllocationUsage().Set(kTeardownSentinel); - delete static_cast(thread_heap_usage); - }); - return *thread_allocator_usage; -} - -bool g_heap_tracking_enabled = false; - -// Forward declared as it needs to delegate memory allocation to the next -// lower shim. -ThreadHeapUsage* GetOrCreateThreadUsage(); - -size_t GetAllocSizeEstimate(const AllocatorDispatch* next, - void* ptr, - void* context) { - if (ptr == nullptr) - return 0U; - - return next->get_size_estimate_function(next, ptr, context); -} - -void RecordAlloc(const AllocatorDispatch* next, - void* ptr, - size_t size, - void* context) { - ThreadHeapUsage* usage = GetOrCreateThreadUsage(); - if (usage == nullptr) - return; - - usage->alloc_ops++; - size_t estimate = GetAllocSizeEstimate(next, ptr, context); - if (size && estimate) { - // Only keep track of the net number of bytes allocated in the scope if the - // size estimate function returns sane values, e.g. non-zero. - usage->alloc_bytes += estimate; - usage->alloc_overhead_bytes += estimate - size; - - // Record the max outstanding number of bytes, but only if the difference - // is net positive (e.g. more bytes allocated than freed in the scope). - if (usage->alloc_bytes > usage->free_bytes) { - uint64_t allocated_bytes = usage->alloc_bytes - usage->free_bytes; - if (allocated_bytes > usage->max_allocated_bytes) - usage->max_allocated_bytes = allocated_bytes; - } - } else { - usage->alloc_bytes += size; - } -} - -void RecordFree(const AllocatorDispatch* next, void* ptr, void* context) { - ThreadHeapUsage* usage = GetOrCreateThreadUsage(); - if (usage == nullptr) - return; - - size_t estimate = GetAllocSizeEstimate(next, ptr, context); - usage->free_ops++; - usage->free_bytes += estimate; -} - -void* AllocFn(const AllocatorDispatch* self, size_t size, void* context) { - void* ret = self->next->alloc_function(self->next, size, context); - if (ret != nullptr) - RecordAlloc(self->next, ret, size, context); - - return ret; -} - -void* AllocZeroInitializedFn(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context) { - void* ret = - self->next->alloc_zero_initialized_function(self->next, n, size, context); - if (ret != nullptr) - RecordAlloc(self->next, ret, size, context); - - return ret; -} - -void* AllocAlignedFn(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - void* ret = - self->next->alloc_aligned_function(self->next, alignment, size, context); - if (ret != nullptr) - RecordAlloc(self->next, ret, size, context); - - return ret; -} - -void* ReallocFn(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - if (address != nullptr) - RecordFree(self->next, address, context); - - void* ret = self->next->realloc_function(self->next, address, size, context); - if (ret != nullptr && size != 0) - RecordAlloc(self->next, ret, size, context); - - return ret; -} - -void FreeFn(const AllocatorDispatch* self, void* address, void* context) { - if (address != nullptr) - RecordFree(self->next, address, context); - self->next->free_function(self->next, address, context); -} - -size_t GetSizeEstimateFn(const AllocatorDispatch* self, - void* address, - void* context) { - return self->next->get_size_estimate_function(self->next, address, context); -} - -unsigned BatchMallocFn(const AllocatorDispatch* self, - size_t size, - void** results, - unsigned num_requested, - void* context) { - unsigned count = self->next->batch_malloc_function(self->next, size, results, - num_requested, context); - for (unsigned i = 0; i < count; ++i) { - RecordAlloc(self->next, results[i], size, context); - } - return count; -} - -void BatchFreeFn(const AllocatorDispatch* self, - void** to_be_freed, - unsigned num_to_be_freed, - void* context) { - for (unsigned i = 0; i < num_to_be_freed; ++i) { - if (to_be_freed[i] != nullptr) { - RecordFree(self->next, to_be_freed[i], context); - } - } - self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed, - context); -} - -void FreeDefiniteSizeFn(const AllocatorDispatch* self, - void* ptr, - size_t size, - void* context) { - if (ptr != nullptr) - RecordFree(self->next, ptr, context); - self->next->free_definite_size_function(self->next, ptr, size, context); -} - -// The allocator dispatch used to intercept heap operations. -AllocatorDispatch allocator_dispatch = {&AllocFn, - &AllocZeroInitializedFn, - &AllocAlignedFn, - &ReallocFn, - &FreeFn, - &GetSizeEstimateFn, - &BatchMallocFn, - &BatchFreeFn, - &FreeDefiniteSizeFn, - nullptr}; - -ThreadHeapUsage* GetOrCreateThreadUsage() { - auto tls_ptr = reinterpret_cast(ThreadAllocationUsage().Get()); - if ((tls_ptr & kSentinelMask) == kSentinelMask) - return nullptr; // Re-entrancy case. - - auto* allocator_usage = reinterpret_cast(tls_ptr); - if (allocator_usage == nullptr) { - // Prevent reentrancy due to the allocation below. - ThreadAllocationUsage().Set(kInitializationSentinel); - - allocator_usage = new ThreadHeapUsage(); - static_assert(std::is_pod::value, - "AllocatorDispatch must be POD"); - memset(allocator_usage, 0, sizeof(*allocator_usage)); - ThreadAllocationUsage().Set(allocator_usage); - } - - return allocator_usage; -} - -} // namespace - -ThreadHeapUsageTracker::ThreadHeapUsageTracker() : thread_usage_(nullptr) { - static_assert(std::is_pod::value, "Must be POD."); -} - -ThreadHeapUsageTracker::~ThreadHeapUsageTracker() { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (thread_usage_ != nullptr) { - // If this tracker wasn't stopped, make it inclusive so that the - // usage isn't lost. - Stop(false); - } -} - -void ThreadHeapUsageTracker::Start() { - DCHECK(thread_checker_.CalledOnValidThread()); - - thread_usage_ = GetOrCreateThreadUsage(); - usage_ = *thread_usage_; - - // Reset the stats for our current scope. - // The per-thread usage instance now tracks this scope's usage, while this - // instance persists the outer scope's usage stats. On destruction, this - // instance will restore the outer scope's usage stats with this scope's - // usage added. - memset(thread_usage_, 0, sizeof(*thread_usage_)); -} - -void ThreadHeapUsageTracker::Stop(bool usage_is_exclusive) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK_NE(nullptr, thread_usage_); - - ThreadHeapUsage current = *thread_usage_; - if (usage_is_exclusive) { - // Restore the outer scope. - *thread_usage_ = usage_; - } else { - // Update the outer scope with the accrued inner usage. - if (thread_usage_->max_allocated_bytes) { - uint64_t outer_net_alloc_bytes = usage_.alloc_bytes - usage_.free_bytes; - - thread_usage_->max_allocated_bytes = - std::max(usage_.max_allocated_bytes, - outer_net_alloc_bytes + thread_usage_->max_allocated_bytes); - } - - thread_usage_->alloc_ops += usage_.alloc_ops; - thread_usage_->alloc_bytes += usage_.alloc_bytes; - thread_usage_->alloc_overhead_bytes += usage_.alloc_overhead_bytes; - thread_usage_->free_ops += usage_.free_ops; - thread_usage_->free_bytes += usage_.free_bytes; - } - - thread_usage_ = nullptr; - usage_ = current; -} - -ThreadHeapUsage ThreadHeapUsageTracker::GetUsageSnapshot() { - ThreadHeapUsage* usage = GetOrCreateThreadUsage(); - DCHECK_NE(nullptr, usage); - return *usage; -} - -void ThreadHeapUsageTracker::EnableHeapTracking() { - EnsureTLSInitialized(); - - CHECK_EQ(false, g_heap_tracking_enabled) << "No double-enabling."; - g_heap_tracking_enabled = true; -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - base::allocator::InsertAllocatorDispatch(&allocator_dispatch); -#else - CHECK(false) << "Can't enable heap tracking without the shim."; -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) -} - -bool ThreadHeapUsageTracker::IsHeapTrackingEnabled() { - return g_heap_tracking_enabled; -} - -void ThreadHeapUsageTracker::DisableHeapTrackingForTesting() { -#if BUILDFLAG(USE_ALLOCATOR_SHIM) - base::allocator::RemoveAllocatorDispatchForTesting(&allocator_dispatch); -#else - CHECK(false) << "Can't disable heap tracking without the shim."; -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) - DCHECK_EQ(true, g_heap_tracking_enabled) << "Heap tracking not enabled."; - g_heap_tracking_enabled = false; -} - -base::allocator::AllocatorDispatch* -ThreadHeapUsageTracker::GetDispatchForTesting() { - return &allocator_dispatch; -} - -void ThreadHeapUsageTracker::EnsureTLSInitialized() { - ignore_result(ThreadAllocationUsage()); -} - -} // namespace debug -} // namespace base diff --git a/debug/thread_heap_usage_tracker.h b/debug/thread_heap_usage_tracker.h deleted file mode 100644 index eb03b3f8b..000000000 --- a/debug/thread_heap_usage_tracker.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2016 The Chromium 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_THREAD_HEAP_USAGE_TRACKER_H_ -#define BASE_DEBUG_THREAD_HEAP_USAGE_TRACKER_H_ - -#include - -#include "base/allocator/buildflags.h" -#include "base/base_export.h" -#include "base/threading/thread_checker.h" - -namespace base { -namespace allocator { -struct AllocatorDispatch; -} // namespace allocator - -namespace debug { - -// Used to store the heap allocator usage in a scope. -struct ThreadHeapUsage { - // The cumulative number of allocation operations. - uint64_t alloc_ops; - - // The cumulative number of allocated bytes. Where available, this is - // inclusive heap padding and estimated or actual heap overhead. - uint64_t alloc_bytes; - - // Where available, cumulative number of heap padding and overhead bytes. - uint64_t alloc_overhead_bytes; - - // The cumulative number of free operations. - uint64_t free_ops; - - // The cumulative number of bytes freed. - // Only recorded if the underlying heap shim can return the size of an - // allocation. - uint64_t free_bytes; - - // The maximal value of |alloc_bytes| - |free_bytes| seen for this thread. - // Only recorded if the underlying heap shim supports returning the size of - // an allocation. - uint64_t max_allocated_bytes; -}; - -// By keeping a tally on heap operations, it's possible to track: -// - the number of alloc/free operations, where a realloc is zero or one -// of each, depending on the input parameters (see man realloc). -// - the number of bytes allocated/freed. -// - the number of estimated bytes of heap overhead used. -// - the high-watermark amount of bytes allocated in the scope. -// This in turn allows measuring the memory usage and memory usage churn over -// a scope. Scopes must be cleanly nested, and each scope must be -// destroyed on the thread where it's created. -// -// Note that this depends on the capabilities of the underlying heap shim. If -// that shim can not yield a size estimate for an allocation, it's not possible -// to keep track of overhead, freed bytes and the allocation high water mark. -class BASE_EXPORT ThreadHeapUsageTracker { - public: - ThreadHeapUsageTracker(); - ~ThreadHeapUsageTracker(); - - // Start tracking heap usage on this thread. - // This may only be called on the thread where the instance is created. - // Note IsHeapTrackingEnabled() must be true. - void Start(); - - // Stop tracking heap usage on this thread and store the usage tallied. - // If |usage_is_exclusive| is true, the usage tallied won't be added to the - // outer scope's usage. If |usage_is_exclusive| is false, the usage tallied - // in this scope will also tally to any outer scope. - // This may only be called on the thread where the instance is created. - void Stop(bool usage_is_exclusive); - - // After Stop() returns the usage tallied from Start() to Stop(). - const ThreadHeapUsage& usage() const { return usage_; } - - // Returns this thread's heap usage from the start of the innermost - // enclosing ThreadHeapUsageTracker instance, if any. - static ThreadHeapUsage GetUsageSnapshot(); - - // Enables the heap intercept. May only be called once, and only if the heap - // shim is available, e.g. if BUILDFLAG(USE_ALLOCATOR_SHIM) is - // true. - static void EnableHeapTracking(); - - // Returns true iff heap tracking is enabled. - static bool IsHeapTrackingEnabled(); - - protected: - // Exposed for testing only - note that it's safe to re-EnableHeapTracking() - // after calling this function in tests. - static void DisableHeapTrackingForTesting(); - - // Exposed for testing only. - static void EnsureTLSInitialized(); - - // Exposed to allow testing the shim without inserting it in the allocator - // shim chain. - static base::allocator::AllocatorDispatch* GetDispatchForTesting(); - - private: - ThreadChecker thread_checker_; - - // The heap usage at Start(), or the difference from Start() to Stop(). - ThreadHeapUsage usage_; - - // This thread's heap usage, non-null from Start() to Stop(). - ThreadHeapUsage* thread_usage_; -}; - -} // namespace debug -} // namespace base - -#endif // BASE_DEBUG_THREAD_HEAP_USAGE_TRACKER_H_ \ No newline at end of file diff --git a/debug/thread_heap_usage_tracker_unittest.cc b/debug/thread_heap_usage_tracker_unittest.cc deleted file mode 100644 index b99576cba..000000000 --- a/debug/thread_heap_usage_tracker_unittest.cc +++ /dev/null @@ -1,607 +0,0 @@ -// Copyright 2016 The Chromium 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/thread_heap_usage_tracker.h" - -#include - -#include "base/allocator/allocator_shim.h" -#include "base/allocator/buildflags.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_MACOSX) -#include "base/allocator/allocator_interception_mac.h" -#endif - -namespace base { -namespace debug { - -namespace { - -class TestingThreadHeapUsageTracker : public ThreadHeapUsageTracker { - public: - using ThreadHeapUsageTracker::DisableHeapTrackingForTesting; - using ThreadHeapUsageTracker::EnsureTLSInitialized; - using ThreadHeapUsageTracker::GetDispatchForTesting; -}; - -// A fixture class that allows testing the AllocatorDispatch associated with -// the ThreadHeapUsageTracker class in isolation against a mocked -// underlying -// heap implementation. -class ThreadHeapUsageTrackerTest : public testing::Test { - public: - using AllocatorDispatch = base::allocator::AllocatorDispatch; - - static const size_t kAllocationPadding; - enum SizeFunctionKind { - EXACT_SIZE_FUNCTION, - PADDING_SIZE_FUNCTION, - ZERO_SIZE_FUNCTION, - }; - - ThreadHeapUsageTrackerTest() : size_function_kind_(EXACT_SIZE_FUNCTION) { - EXPECT_EQ(nullptr, g_self); - g_self = this; - } - - ~ThreadHeapUsageTrackerTest() override { - EXPECT_EQ(this, g_self); - g_self = nullptr; - } - - void set_size_function_kind(SizeFunctionKind kind) { - size_function_kind_ = kind; - } - - void SetUp() override { - TestingThreadHeapUsageTracker::EnsureTLSInitialized(); - - dispatch_under_test_ = - TestingThreadHeapUsageTracker::GetDispatchForTesting(); - ASSERT_EQ(nullptr, dispatch_under_test_->next); - - dispatch_under_test_->next = &g_mock_dispatch; - } - - void TearDown() override { - ASSERT_EQ(&g_mock_dispatch, dispatch_under_test_->next); - - dispatch_under_test_->next = nullptr; - } - - void* MockMalloc(size_t size) { - return dispatch_under_test_->alloc_function(dispatch_under_test_, size, - nullptr); - } - - void* MockCalloc(size_t n, size_t size) { - return dispatch_under_test_->alloc_zero_initialized_function( - dispatch_under_test_, n, size, nullptr); - } - - void* MockAllocAligned(size_t alignment, size_t size) { - return dispatch_under_test_->alloc_aligned_function( - dispatch_under_test_, alignment, size, nullptr); - } - - void* MockRealloc(void* address, size_t size) { - return dispatch_under_test_->realloc_function(dispatch_under_test_, address, - size, nullptr); - } - - void MockFree(void* address) { - dispatch_under_test_->free_function(dispatch_under_test_, address, nullptr); - } - - size_t MockGetSizeEstimate(void* address) { - return dispatch_under_test_->get_size_estimate_function( - dispatch_under_test_, address, nullptr); - } - - private: - void RecordAlloc(void* address, size_t size) { - if (address != nullptr) - allocation_size_map_[address] = size; - } - - void DeleteAlloc(void* address) { - if (address != nullptr) - EXPECT_EQ(1U, allocation_size_map_.erase(address)); - } - - size_t GetSizeEstimate(void* address) { - auto it = allocation_size_map_.find(address); - if (it == allocation_size_map_.end()) - return 0; - - size_t ret = it->second; - switch (size_function_kind_) { - case EXACT_SIZE_FUNCTION: - break; - case PADDING_SIZE_FUNCTION: - ret += kAllocationPadding; - break; - case ZERO_SIZE_FUNCTION: - ret = 0; - break; - } - - return ret; - } - - static void* OnAllocFn(const AllocatorDispatch* self, - size_t size, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - void* ret = malloc(size); - g_self->RecordAlloc(ret, size); - return ret; - } - - static void* OnAllocZeroInitializedFn(const AllocatorDispatch* self, - size_t n, - size_t size, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - void* ret = calloc(n, size); - g_self->RecordAlloc(ret, n * size); - return ret; - } - - static void* OnAllocAlignedFn(const AllocatorDispatch* self, - size_t alignment, - size_t size, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - // This is a cheat as it doesn't return aligned allocations. This has the - // advantage of working for all platforms for this test. - void* ret = malloc(size); - g_self->RecordAlloc(ret, size); - return ret; - } - - static void* OnReallocFn(const AllocatorDispatch* self, - void* address, - size_t size, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - g_self->DeleteAlloc(address); - void* ret = realloc(address, size); - g_self->RecordAlloc(ret, size); - return ret; - } - - static void OnFreeFn(const AllocatorDispatch* self, - void* address, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - g_self->DeleteAlloc(address); - free(address); - } - - static size_t OnGetSizeEstimateFn(const AllocatorDispatch* self, - void* address, - void* context) { - EXPECT_EQ(&g_mock_dispatch, self); - - return g_self->GetSizeEstimate(address); - } - - using AllocationSizeMap = std::map; - - SizeFunctionKind size_function_kind_; - AllocationSizeMap allocation_size_map_; - AllocatorDispatch* dispatch_under_test_; - - static base::allocator::AllocatorDispatch g_mock_dispatch; - static ThreadHeapUsageTrackerTest* g_self; -}; - -const size_t ThreadHeapUsageTrackerTest::kAllocationPadding = 23; - -ThreadHeapUsageTrackerTest* ThreadHeapUsageTrackerTest::g_self = nullptr; - -base::allocator::AllocatorDispatch ThreadHeapUsageTrackerTest::g_mock_dispatch = - { - &ThreadHeapUsageTrackerTest::OnAllocFn, // alloc_function - &ThreadHeapUsageTrackerTest:: - OnAllocZeroInitializedFn, // alloc_zero_initialized_function - &ThreadHeapUsageTrackerTest:: - OnAllocAlignedFn, // alloc_aligned_function - &ThreadHeapUsageTrackerTest::OnReallocFn, // realloc_function - &ThreadHeapUsageTrackerTest::OnFreeFn, // free_function - &ThreadHeapUsageTrackerTest:: - OnGetSizeEstimateFn, // get_size_estimate_function - nullptr, // batch_malloc - nullptr, // batch_free - nullptr, // free_definite_size_function - nullptr, // next -}; - -} // namespace - -TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithExactSizeFunction) { - set_size_function_kind(EXACT_SIZE_FUNCTION); - - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot(); - - EXPECT_EQ(0U, u1.alloc_ops); - EXPECT_EQ(0U, u1.alloc_bytes); - EXPECT_EQ(0U, u1.alloc_overhead_bytes); - EXPECT_EQ(0U, u1.free_ops); - EXPECT_EQ(0U, u1.free_bytes); - EXPECT_EQ(0U, u1.max_allocated_bytes); - - const size_t kAllocSize = 1029U; - void* ptr = MockMalloc(kAllocSize); - MockFree(ptr); - - usage_tracker.Stop(false); - ThreadHeapUsage u2 = usage_tracker.usage(); - - EXPECT_EQ(1U, u2.alloc_ops); - EXPECT_EQ(kAllocSize, u2.alloc_bytes); - EXPECT_EQ(0U, u2.alloc_overhead_bytes); - EXPECT_EQ(1U, u2.free_ops); - EXPECT_EQ(kAllocSize, u2.free_bytes); - EXPECT_EQ(kAllocSize, u2.max_allocated_bytes); -} - -TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithPaddingSizeFunction) { - set_size_function_kind(PADDING_SIZE_FUNCTION); - - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot(); - - EXPECT_EQ(0U, u1.alloc_ops); - EXPECT_EQ(0U, u1.alloc_bytes); - EXPECT_EQ(0U, u1.alloc_overhead_bytes); - EXPECT_EQ(0U, u1.free_ops); - EXPECT_EQ(0U, u1.free_bytes); - EXPECT_EQ(0U, u1.max_allocated_bytes); - - const size_t kAllocSize = 1029U; - void* ptr = MockMalloc(kAllocSize); - MockFree(ptr); - - usage_tracker.Stop(false); - ThreadHeapUsage u2 = usage_tracker.usage(); - - EXPECT_EQ(1U, u2.alloc_ops); - EXPECT_EQ(kAllocSize + kAllocationPadding, u2.alloc_bytes); - EXPECT_EQ(kAllocationPadding, u2.alloc_overhead_bytes); - EXPECT_EQ(1U, u2.free_ops); - EXPECT_EQ(kAllocSize + kAllocationPadding, u2.free_bytes); - EXPECT_EQ(kAllocSize + kAllocationPadding, u2.max_allocated_bytes); -} - -TEST_F(ThreadHeapUsageTrackerTest, SimpleUsageWithZeroSizeFunction) { - set_size_function_kind(ZERO_SIZE_FUNCTION); - - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(0U, u1.alloc_ops); - EXPECT_EQ(0U, u1.alloc_bytes); - EXPECT_EQ(0U, u1.alloc_overhead_bytes); - EXPECT_EQ(0U, u1.free_ops); - EXPECT_EQ(0U, u1.free_bytes); - EXPECT_EQ(0U, u1.max_allocated_bytes); - - const size_t kAllocSize = 1029U; - void* ptr = MockMalloc(kAllocSize); - MockFree(ptr); - - usage_tracker.Stop(false); - ThreadHeapUsage u2 = usage_tracker.usage(); - - // With a get-size function that returns zero, there's no way to get the size - // of an allocation that's being freed, hence the shim can't tally freed bytes - // nor the high-watermark allocated bytes. - EXPECT_EQ(1U, u2.alloc_ops); - EXPECT_EQ(kAllocSize, u2.alloc_bytes); - EXPECT_EQ(0U, u2.alloc_overhead_bytes); - EXPECT_EQ(1U, u2.free_ops); - EXPECT_EQ(0U, u2.free_bytes); - EXPECT_EQ(0U, u2.max_allocated_bytes); -} - -TEST_F(ThreadHeapUsageTrackerTest, ReallocCorrectlyTallied) { - const size_t kAllocSize = 237U; - - { - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - // Reallocating nullptr should count as a single alloc. - void* ptr = MockRealloc(nullptr, kAllocSize); - ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(1U, usage.alloc_ops); - EXPECT_EQ(kAllocSize, usage.alloc_bytes); - EXPECT_EQ(0U, usage.alloc_overhead_bytes); - EXPECT_EQ(0U, usage.free_ops); - EXPECT_EQ(0U, usage.free_bytes); - EXPECT_EQ(kAllocSize, usage.max_allocated_bytes); - - // Reallocating a valid pointer to a zero size should count as a single - // free. - ptr = MockRealloc(ptr, 0U); - - usage_tracker.Stop(false); - EXPECT_EQ(1U, usage_tracker.usage().alloc_ops); - EXPECT_EQ(kAllocSize, usage_tracker.usage().alloc_bytes); - EXPECT_EQ(0U, usage_tracker.usage().alloc_overhead_bytes); - EXPECT_EQ(1U, usage_tracker.usage().free_ops); - EXPECT_EQ(kAllocSize, usage_tracker.usage().free_bytes); - EXPECT_EQ(kAllocSize, usage_tracker.usage().max_allocated_bytes); - - // Realloc to zero size may or may not return a nullptr - make sure to - // free the zero-size alloc in the latter case. - if (ptr != nullptr) - MockFree(ptr); - } - - { - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - void* ptr = MockMalloc(kAllocSize); - ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(1U, usage.alloc_ops); - - // Now try reallocating a valid pointer to a larger size, this should count - // as one free and one alloc. - const size_t kLargerAllocSize = kAllocSize + 928U; - ptr = MockRealloc(ptr, kLargerAllocSize); - - usage_tracker.Stop(false); - EXPECT_EQ(2U, usage_tracker.usage().alloc_ops); - EXPECT_EQ(kAllocSize + kLargerAllocSize, usage_tracker.usage().alloc_bytes); - EXPECT_EQ(0U, usage_tracker.usage().alloc_overhead_bytes); - EXPECT_EQ(1U, usage_tracker.usage().free_ops); - EXPECT_EQ(kAllocSize, usage_tracker.usage().free_bytes); - EXPECT_EQ(kLargerAllocSize, usage_tracker.usage().max_allocated_bytes); - - MockFree(ptr); - } -} - -TEST_F(ThreadHeapUsageTrackerTest, NestedMaxWorks) { - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - const size_t kOuterAllocSize = 1029U; - void* ptr = MockMalloc(kOuterAllocSize); - MockFree(ptr); - - EXPECT_EQ(kOuterAllocSize, - ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes); - - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - const size_t kInnerAllocSize = 673U; - ptr = MockMalloc(kInnerAllocSize); - MockFree(ptr); - - inner_usage_tracker.Stop(false); - - EXPECT_EQ(kInnerAllocSize, inner_usage_tracker.usage().max_allocated_bytes); - } - - // The greater, outer allocation size should have been restored. - EXPECT_EQ(kOuterAllocSize, - ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes); - - const size_t kLargerInnerAllocSize = kOuterAllocSize + 673U; - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - ptr = MockMalloc(kLargerInnerAllocSize); - MockFree(ptr); - - inner_usage_tracker.Stop(false); - EXPECT_EQ(kLargerInnerAllocSize, - inner_usage_tracker.usage().max_allocated_bytes); - } - - // The greater, inner allocation size should have been preserved. - EXPECT_EQ(kLargerInnerAllocSize, - ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes); - - // Now try the case with an outstanding net alloc size when entering the - // inner scope. - void* outer_ptr = MockMalloc(kOuterAllocSize); - EXPECT_EQ(kLargerInnerAllocSize, - ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes); - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - ptr = MockMalloc(kLargerInnerAllocSize); - MockFree(ptr); - - inner_usage_tracker.Stop(false); - EXPECT_EQ(kLargerInnerAllocSize, - inner_usage_tracker.usage().max_allocated_bytes); - } - - // While the inner scope saw only the inner net outstanding allocation size, - // the outer scope saw both outstanding at the same time. - EXPECT_EQ(kOuterAllocSize + kLargerInnerAllocSize, - ThreadHeapUsageTracker::GetUsageSnapshot().max_allocated_bytes); - - MockFree(outer_ptr); - - // Test a net-negative scope. - ptr = MockMalloc(kLargerInnerAllocSize); - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - MockFree(ptr); - - const size_t kInnerAllocSize = 1; - ptr = MockMalloc(kInnerAllocSize); - - inner_usage_tracker.Stop(false); - // Since the scope is still net-negative, the max is clamped at zero. - EXPECT_EQ(0U, inner_usage_tracker.usage().max_allocated_bytes); - } - - MockFree(ptr); -} - -TEST_F(ThreadHeapUsageTrackerTest, NoStopImpliesInclusive) { - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - const size_t kOuterAllocSize = 1029U; - void* ptr = MockMalloc(kOuterAllocSize); - MockFree(ptr); - - ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(kOuterAllocSize, usage.max_allocated_bytes); - - const size_t kInnerLargerAllocSize = kOuterAllocSize + 673U; - - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - // Make a larger allocation than the outer scope. - ptr = MockMalloc(kInnerLargerAllocSize); - MockFree(ptr); - - // inner_usage_tracker goes out of scope without a Stop(). - } - - ThreadHeapUsage current = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(usage.alloc_ops + 1, current.alloc_ops); - EXPECT_EQ(usage.alloc_bytes + kInnerLargerAllocSize, current.alloc_bytes); - EXPECT_EQ(usage.free_ops + 1, current.free_ops); - EXPECT_EQ(usage.free_bytes + kInnerLargerAllocSize, current.free_bytes); - EXPECT_EQ(kInnerLargerAllocSize, current.max_allocated_bytes); -} - -TEST_F(ThreadHeapUsageTrackerTest, ExclusiveScopesWork) { - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - const size_t kOuterAllocSize = 1029U; - void* ptr = MockMalloc(kOuterAllocSize); - MockFree(ptr); - - ThreadHeapUsage usage = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(kOuterAllocSize, usage.max_allocated_bytes); - - { - ThreadHeapUsageTracker inner_usage_tracker; - inner_usage_tracker.Start(); - - // Make a larger allocation than the outer scope. - ptr = MockMalloc(kOuterAllocSize + 673U); - MockFree(ptr); - - // This tracker is exlusive, all activity should be private to this scope. - inner_usage_tracker.Stop(true); - } - - ThreadHeapUsage current = ThreadHeapUsageTracker::GetUsageSnapshot(); - EXPECT_EQ(usage.alloc_ops, current.alloc_ops); - EXPECT_EQ(usage.alloc_bytes, current.alloc_bytes); - EXPECT_EQ(usage.alloc_overhead_bytes, current.alloc_overhead_bytes); - EXPECT_EQ(usage.free_ops, current.free_ops); - EXPECT_EQ(usage.free_bytes, current.free_bytes); - EXPECT_EQ(usage.max_allocated_bytes, current.max_allocated_bytes); -} - -TEST_F(ThreadHeapUsageTrackerTest, AllShimFunctionsAreProvided) { - const size_t kAllocSize = 100; - void* alloc = MockMalloc(kAllocSize); - size_t estimate = MockGetSizeEstimate(alloc); - ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize); - MockFree(alloc); - - alloc = MockCalloc(kAllocSize, 1); - estimate = MockGetSizeEstimate(alloc); - ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize); - MockFree(alloc); - - alloc = MockAllocAligned(1, kAllocSize); - estimate = MockGetSizeEstimate(alloc); - ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize); - - alloc = MockRealloc(alloc, kAllocSize); - estimate = MockGetSizeEstimate(alloc); - ASSERT_TRUE(estimate == 0 || estimate >= kAllocSize); - MockFree(alloc); -} - -#if BUILDFLAG(USE_ALLOCATOR_SHIM) -class ThreadHeapUsageShimTest : public testing::Test { -#if defined(OS_MACOSX) - void SetUp() override { allocator::InitializeAllocatorShim(); } - void TearDown() override { allocator::UninterceptMallocZonesForTesting(); } -#endif -}; - -TEST_F(ThreadHeapUsageShimTest, HooksIntoMallocWhenShimAvailable) { - ASSERT_FALSE(ThreadHeapUsageTracker::IsHeapTrackingEnabled()); - - ThreadHeapUsageTracker::EnableHeapTracking(); - - ASSERT_TRUE(ThreadHeapUsageTracker::IsHeapTrackingEnabled()); - - const size_t kAllocSize = 9993; - // This test verifies that the scoped heap data is affected by malloc & - // free only when the shim is available. - ThreadHeapUsageTracker usage_tracker; - usage_tracker.Start(); - - ThreadHeapUsage u1 = ThreadHeapUsageTracker::GetUsageSnapshot(); - void* ptr = malloc(kAllocSize); - // Prevent the compiler from optimizing out the malloc/free pair. - ASSERT_NE(nullptr, ptr); - - ThreadHeapUsage u2 = ThreadHeapUsageTracker::GetUsageSnapshot(); - free(ptr); - - usage_tracker.Stop(false); - ThreadHeapUsage u3 = usage_tracker.usage(); - - // Verify that at least one allocation operation was recorded, and that free - // operations are at least monotonically growing. - EXPECT_LE(0U, u1.alloc_ops); - EXPECT_LE(u1.alloc_ops + 1, u2.alloc_ops); - EXPECT_LE(u1.alloc_ops + 1, u3.alloc_ops); - - // Verify that at least the bytes above were recorded. - EXPECT_LE(u1.alloc_bytes + kAllocSize, u2.alloc_bytes); - - // Verify that at least the one free operation above was recorded. - EXPECT_LE(u2.free_ops + 1, u3.free_ops); - - TestingThreadHeapUsageTracker::DisableHeapTrackingForTesting(); - - ASSERT_FALSE(ThreadHeapUsageTracker::IsHeapTrackingEnabled()); -} -#endif // BUILDFLAG(USE_ALLOCATOR_SHIM) - -} // namespace debug -} // namespace base diff --git a/deferred_sequenced_task_runner.cc b/deferred_sequenced_task_runner.cc deleted file mode 100644 index f88170c3d..000000000 --- a/deferred_sequenced_task_runner.cc +++ /dev/null @@ -1,129 +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/deferred_sequenced_task_runner.h" - -#include - -#include "base/bind.h" -#include "base/logging.h" - -namespace base { - -DeferredSequencedTaskRunner::DeferredTask::DeferredTask() - : is_non_nestable(false) { -} - -DeferredSequencedTaskRunner::DeferredTask::DeferredTask(DeferredTask&& other) = - default; - -DeferredSequencedTaskRunner::DeferredTask::~DeferredTask() = default; - -DeferredSequencedTaskRunner::DeferredTask& -DeferredSequencedTaskRunner::DeferredTask::operator=(DeferredTask&& other) = - default; - -DeferredSequencedTaskRunner::DeferredSequencedTaskRunner( - scoped_refptr target_task_runner) - : DeferredSequencedTaskRunner() { - DCHECK(target_task_runner); - target_task_runner_ = std::move(target_task_runner); -} - -DeferredSequencedTaskRunner::DeferredSequencedTaskRunner() - : created_thread_id_(PlatformThread::CurrentId()) {} - -bool DeferredSequencedTaskRunner::PostDelayedTask(const Location& from_here, - OnceClosure task, - TimeDelta delay) { - AutoLock lock(lock_); - if (started_) { - DCHECK(deferred_tasks_queue_.empty()); - return target_task_runner_->PostDelayedTask(from_here, std::move(task), - delay); - } - - QueueDeferredTask(from_here, std::move(task), delay, - false /* is_non_nestable */); - return true; -} - -bool DeferredSequencedTaskRunner::RunsTasksInCurrentSequence() const { - AutoLock lock(lock_); - if (target_task_runner_) - return target_task_runner_->RunsTasksInCurrentSequence(); - - return created_thread_id_ == PlatformThread::CurrentId(); -} - -bool DeferredSequencedTaskRunner::PostNonNestableDelayedTask( - const Location& from_here, - OnceClosure task, - TimeDelta delay) { - AutoLock lock(lock_); - if (started_) { - DCHECK(deferred_tasks_queue_.empty()); - return target_task_runner_->PostNonNestableDelayedTask( - from_here, std::move(task), delay); - } - QueueDeferredTask(from_here, std::move(task), delay, - true /* is_non_nestable */); - return true; -} - -void DeferredSequencedTaskRunner::Start() { - AutoLock lock(lock_); - StartImpl(); -} - -void DeferredSequencedTaskRunner::StartWithTaskRunner( - scoped_refptr target_task_runner) { - AutoLock lock(lock_); - DCHECK(!target_task_runner_); - DCHECK(target_task_runner); - target_task_runner_ = std::move(target_task_runner); - StartImpl(); -} - -DeferredSequencedTaskRunner::~DeferredSequencedTaskRunner() = default; - -void DeferredSequencedTaskRunner::QueueDeferredTask(const Location& from_here, - OnceClosure task, - TimeDelta delay, - bool is_non_nestable) { - lock_.AssertAcquired(); - - // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167 - // for details. - CHECK(task); - - DeferredTask deferred_task; - deferred_task.posted_from = from_here; - deferred_task.task = std::move(task); - deferred_task.delay = delay; - deferred_task.is_non_nestable = is_non_nestable; - deferred_tasks_queue_.push_back(std::move(deferred_task)); -} - -void DeferredSequencedTaskRunner::StartImpl() { - lock_.AssertAcquired(); // Callers should have grabbed the lock. - DCHECK(!started_); - started_ = true; - DCHECK(target_task_runner_); - for (std::vector::iterator i = deferred_tasks_queue_.begin(); - i != deferred_tasks_queue_.end(); - ++i) { - DeferredTask& task = *i; - if (task.is_non_nestable) { - target_task_runner_->PostNonNestableDelayedTask( - task.posted_from, std::move(task.task), task.delay); - } else { - target_task_runner_->PostDelayedTask(task.posted_from, - std::move(task.task), task.delay); - } - } - deferred_tasks_queue_.clear(); -} - -} // namespace base diff --git a/deferred_sequenced_task_runner.h b/deferred_sequenced_task_runner.h deleted file mode 100644 index 2805f479e..000000000 --- a/deferred_sequenced_task_runner.h +++ /dev/null @@ -1,97 +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_DEFERRED_SEQUENCED_TASK_RUNNER_H_ -#define BASE_DEFERRED_SEQUENCED_TASK_RUNNER_H_ - -#include - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/sequenced_task_runner.h" -#include "base/synchronization/lock.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" - -namespace base { - -// A DeferredSequencedTaskRunner is a subclass of SequencedTaskRunner that -// queues up all requests until the first call to Start() is issued. -// DeferredSequencedTaskRunner may be created in two ways: -// . with an explicit SequencedTaskRunner that the events are flushed to -// . without a SequencedTaskRunner. In this configuration the -// SequencedTaskRunner is supplied in StartWithTaskRunner(). -class BASE_EXPORT DeferredSequencedTaskRunner : public SequencedTaskRunner { - public: - explicit DeferredSequencedTaskRunner( - scoped_refptr target_runner); - - // Use this constructor when you don't have the target SequencedTaskRunner. - // When using this call StartWithTaskRunner(). - DeferredSequencedTaskRunner(); - - // TaskRunner implementation - bool PostDelayedTask(const Location& from_here, - OnceClosure task, - TimeDelta delay) override; - bool RunsTasksInCurrentSequence() const override; - - // SequencedTaskRunner implementation - bool PostNonNestableDelayedTask(const Location& from_here, - OnceClosure task, - TimeDelta delay) override; - - // Start the execution - posts all queued tasks to the target executor. The - // deferred tasks are posted with their initial delay, meaning that the task - // execution delay is actually measured from Start. - // Fails when called a second time. - void Start(); - - // Same as Start(), but must be used with the no-arg constructor. - void StartWithTaskRunner( - scoped_refptr target_task_runner); - - private: - struct DeferredTask { - DeferredTask(); - DeferredTask(DeferredTask&& other); - ~DeferredTask(); - DeferredTask& operator=(DeferredTask&& other); - - Location posted_from; - OnceClosure task; - // The delay this task was initially posted with. - TimeDelta delay; - bool is_non_nestable; - }; - - ~DeferredSequencedTaskRunner() override; - - // Both variants of Start() call into this. - void StartImpl(); - - // Creates a |Task| object and adds it to |deferred_tasks_queue_|. - void QueueDeferredTask(const Location& from_here, - OnceClosure task, - TimeDelta delay, - bool is_non_nestable); - - // // Protects |started_| and |deferred_tasks_queue_|. - mutable Lock lock_; - - const PlatformThreadId created_thread_id_; - - bool started_ = false; - scoped_refptr target_task_runner_; - std::vector deferred_tasks_queue_; - - DISALLOW_COPY_AND_ASSIGN(DeferredSequencedTaskRunner); -}; - -} // namespace base - -#endif // BASE_DEFERRED_SEQUENCED_TASK_RUNNER_H_ diff --git a/deferred_sequenced_task_runner_unittest.cc b/deferred_sequenced_task_runner_unittest.cc deleted file mode 100644 index 5cb220fdf..000000000 --- a/deferred_sequenced_task_runner_unittest.cc +++ /dev/null @@ -1,214 +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/deferred_sequenced_task_runner.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback_forward.h" -#include "base/location.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -class DeferredSequencedTaskRunnerTest : public testing::Test { - public: - class ExecuteTaskOnDestructor : public RefCounted { - public: - ExecuteTaskOnDestructor( - DeferredSequencedTaskRunnerTest* executor, - int task_id) - : executor_(executor), - task_id_(task_id) { - } - private: - friend class RefCounted; - virtual ~ExecuteTaskOnDestructor() { executor_->ExecuteTask(task_id_); } - DeferredSequencedTaskRunnerTest* executor_; - int task_id_; - }; - - void ExecuteTask(int task_id) { - AutoLock lock(lock_); - executed_task_ids_.push_back(task_id); - } - - void PostExecuteTask(int task_id) { - runner_->PostTask(FROM_HERE, - BindOnce(&DeferredSequencedTaskRunnerTest::ExecuteTask, - Unretained(this), task_id)); - } - - void StartRunner() { - runner_->Start(); - } - - void DoNothing(ExecuteTaskOnDestructor* object) { - } - - protected: - DeferredSequencedTaskRunnerTest() - : loop_(), - runner_(new DeferredSequencedTaskRunner(loop_.task_runner())) {} - - MessageLoop loop_; - scoped_refptr runner_; - mutable Lock lock_; - std::vector executed_task_ids_; -}; - -TEST_F(DeferredSequencedTaskRunnerTest, Stopped) { - PostExecuteTask(1); - RunLoop().RunUntilIdle(); - EXPECT_THAT(executed_task_ids_, testing::ElementsAre()); -} - -TEST_F(DeferredSequencedTaskRunnerTest, Start) { - StartRunner(); - PostExecuteTask(1); - RunLoop().RunUntilIdle(); - EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1)); -} - -TEST_F(DeferredSequencedTaskRunnerTest, StartWithMultipleElements) { - StartRunner(); - for (int i = 1; i < 5; ++i) - PostExecuteTask(i); - - RunLoop().RunUntilIdle(); - EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2, 3, 4)); -} - -TEST_F(DeferredSequencedTaskRunnerTest, DeferredStart) { - PostExecuteTask(1); - RunLoop().RunUntilIdle(); - EXPECT_THAT(executed_task_ids_, testing::ElementsAre()); - - StartRunner(); - RunLoop().RunUntilIdle(); - EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1)); - - PostExecuteTask(2); - RunLoop().RunUntilIdle(); - EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2)); -} - -TEST_F(DeferredSequencedTaskRunnerTest, DeferredStartWithMultipleElements) { - for (int i = 1; i < 5; ++i) - PostExecuteTask(i); - RunLoop().RunUntilIdle(); - EXPECT_THAT(executed_task_ids_, testing::ElementsAre()); - - StartRunner(); - for (int i = 5; i < 9; ++i) - PostExecuteTask(i); - RunLoop().RunUntilIdle(); - EXPECT_THAT(executed_task_ids_, testing::ElementsAre(1, 2, 3, 4, 5, 6, 7, 8)); -} - -TEST_F(DeferredSequencedTaskRunnerTest, DeferredStartWithMultipleThreads) { - { - Thread thread1("DeferredSequencedTaskRunnerTestThread1"); - Thread thread2("DeferredSequencedTaskRunnerTestThread2"); - thread1.Start(); - thread2.Start(); - for (int i = 0; i < 5; ++i) { - thread1.task_runner()->PostTask( - FROM_HERE, BindOnce(&DeferredSequencedTaskRunnerTest::PostExecuteTask, - Unretained(this), 2 * i)); - thread2.task_runner()->PostTask( - FROM_HERE, BindOnce(&DeferredSequencedTaskRunnerTest::PostExecuteTask, - Unretained(this), 2 * i + 1)); - if (i == 2) { - thread1.task_runner()->PostTask( - FROM_HERE, BindOnce(&DeferredSequencedTaskRunnerTest::StartRunner, - Unretained(this))); - } - } - } - - RunLoop().RunUntilIdle(); - EXPECT_THAT(executed_task_ids_, - testing::WhenSorted(testing::ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9))); -} - -TEST_F(DeferredSequencedTaskRunnerTest, ObjectDestructionOrder) { - { - Thread thread("DeferredSequencedTaskRunnerTestThread"); - thread.Start(); - runner_ = new DeferredSequencedTaskRunner(thread.task_runner()); - for (int i = 0; i < 5; ++i) { - { - // Use a block to ensure that no reference to |short_lived_object| - // is kept on the main thread after it is posted to |runner_|. - scoped_refptr short_lived_object = - new ExecuteTaskOnDestructor(this, 2 * i); - runner_->PostTask( - FROM_HERE, - BindOnce(&DeferredSequencedTaskRunnerTest::DoNothing, - Unretained(this), RetainedRef(short_lived_object))); - } - // |short_lived_object| with id |2 * i| should be destroyed before the - // task |2 * i + 1| is executed. - PostExecuteTask(2 * i + 1); - } - StartRunner(); - } - - // All |short_lived_object| with id |2 * i| are destroyed before the task - // |2 * i + 1| is executed. - EXPECT_THAT(executed_task_ids_, - testing::ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); -} - -void GetRunsTasksInCurrentSequence(bool* result, - scoped_refptr runner, - OnceClosure quit) { - *result = runner->RunsTasksInCurrentSequence(); - std::move(quit).Run(); -} - -TEST_F(DeferredSequencedTaskRunnerTest, RunsTasksInCurrentSequence) { - scoped_refptr runner = - MakeRefCounted(); - EXPECT_TRUE(runner->RunsTasksInCurrentSequence()); - - Thread thread1("DeferredSequencedTaskRunnerTestThread1"); - thread1.Start(); - bool runs_task_in_current_thread = true; - base::RunLoop run_loop; - thread1.task_runner()->PostTask( - FROM_HERE, - BindOnce(&GetRunsTasksInCurrentSequence, &runs_task_in_current_thread, - runner, run_loop.QuitClosure())); - run_loop.Run(); - EXPECT_FALSE(runs_task_in_current_thread); -} - -TEST_F(DeferredSequencedTaskRunnerTest, StartWithTaskRunner) { - scoped_refptr runner = - MakeRefCounted(); - bool run_called = false; - base::RunLoop run_loop; - runner->PostTask(FROM_HERE, - BindOnce( - [](bool* run_called, base::Closure quit_closure) { - *run_called = true; - std::move(quit_closure).Run(); - }, - &run_called, run_loop.QuitClosure())); - runner->StartWithTaskRunner(loop_.task_runner()); - run_loop.Run(); - EXPECT_TRUE(run_called); -} - -} // namespace -} // namespace base diff --git a/environment.cc b/environment.cc deleted file mode 100644 index cdea53c8c..000000000 --- a/environment.cc +++ /dev/null @@ -1,238 +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/environment.h" - -#include - -#include - -#include "base/memory/ptr_util.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#endif - -namespace base { - -namespace { - -class EnvironmentImpl : public Environment { - public: - bool GetVar(StringPiece variable_name, std::string* result) override { - if (GetVarImpl(variable_name, result)) - return true; - - // Some commonly used variable names are uppercase while others - // are lowercase, which is inconsistent. Let's try to be helpful - // and look for a variable name with the reverse case. - // I.e. HTTP_PROXY may be http_proxy for some users/systems. - char first_char = variable_name[0]; - std::string alternate_case_var; - if (IsAsciiLower(first_char)) - alternate_case_var = ToUpperASCII(variable_name); - else if (IsAsciiUpper(first_char)) - alternate_case_var = ToLowerASCII(variable_name); - else - return false; - return GetVarImpl(alternate_case_var, result); - } - - bool SetVar(StringPiece variable_name, - const std::string& new_value) override { - return SetVarImpl(variable_name, new_value); - } - - bool UnSetVar(StringPiece variable_name) override { - return UnSetVarImpl(variable_name); - } - - private: - bool GetVarImpl(StringPiece variable_name, std::string* result) { -#if defined(OS_WIN) - DWORD value_length = - ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), nullptr, 0); - if (value_length == 0) - return false; - if (result) { - std::unique_ptr value(new wchar_t[value_length]); - ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), value.get(), - value_length); - *result = WideToUTF8(value.get()); - } - return true; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - const char* env_value = getenv(variable_name.data()); - if (!env_value) - return false; - // Note that the variable may be defined but empty. - if (result) - *result = env_value; - return true; -#endif - } - - bool SetVarImpl(StringPiece variable_name, const std::string& new_value) { -#if defined(OS_WIN) - // On success, a nonzero value is returned. - return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), - UTF8ToWide(new_value).c_str()); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // On success, zero is returned. - return !setenv(variable_name.data(), new_value.c_str(), 1); -#endif - } - - bool UnSetVarImpl(StringPiece variable_name) { -#if defined(OS_WIN) - // On success, a nonzero value is returned. - return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), nullptr); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // On success, zero is returned. - return !unsetenv(variable_name.data()); -#endif - } -}; - -// Parses a null-terminated input string of an environment block. The key is -// placed into the given string, and the total length of the line, including -// the terminating null, is returned. -size_t ParseEnvLine(const NativeEnvironmentString::value_type* input, - NativeEnvironmentString* key) { - // Skip to the equals or end of the string, this is the key. - size_t cur = 0; - while (input[cur] && input[cur] != '=') - cur++; - *key = NativeEnvironmentString(&input[0], cur); - - // Now just skip to the end of the string. - while (input[cur]) - cur++; - return cur + 1; -} - -} // namespace - -namespace env_vars { - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -// On Posix systems, this variable contains the location of the user's home -// directory. (e.g, /home/username/). -const char kHome[] = "HOME"; -#endif - -} // namespace env_vars - -Environment::~Environment() = default; - -// static -std::unique_ptr Environment::Create() { - return std::make_unique(); -} - -bool Environment::HasVar(StringPiece variable_name) { - return GetVar(variable_name, nullptr); -} - -#if defined(OS_WIN) - -string16 AlterEnvironment(const wchar_t* env, - const EnvironmentMap& changes) { - string16 result; - - // First copy all unmodified values to the output. - size_t cur_env = 0; - string16 key; - while (env[cur_env]) { - const wchar_t* line = &env[cur_env]; - size_t line_length = ParseEnvLine(line, &key); - - // Keep only values not specified in the change vector. - EnvironmentMap::const_iterator found_change = changes.find(key); - if (found_change == changes.end()) - result.append(line, line_length); - - cur_env += line_length; - } - - // Now append all modified and new values. - for (EnvironmentMap::const_iterator i = changes.begin(); - i != changes.end(); ++i) { - if (!i->second.empty()) { - result.append(i->first); - result.push_back('='); - result.append(i->second); - result.push_back(0); - } - } - - // An additional null marks the end of the list. We always need a double-null - // in case nothing was added above. - if (result.empty()) - result.push_back(0); - result.push_back(0); - return result; -} - -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - -std::unique_ptr AlterEnvironment(const char* const* const env, - const EnvironmentMap& changes) { - std::string value_storage; // Holds concatenated null-terminated strings. - std::vector result_indices; // Line indices into value_storage. - - // First build up all of the unchanged environment strings. These are - // null-terminated of the form "key=value". - std::string key; - for (size_t i = 0; env[i]; i++) { - size_t line_length = ParseEnvLine(env[i], &key); - - // Keep only values not specified in the change vector. - EnvironmentMap::const_iterator found_change = changes.find(key); - if (found_change == changes.end()) { - result_indices.push_back(value_storage.size()); - value_storage.append(env[i], line_length); - } - } - - // Now append all modified and new values. - for (EnvironmentMap::const_iterator i = changes.begin(); - i != changes.end(); ++i) { - if (!i->second.empty()) { - result_indices.push_back(value_storage.size()); - value_storage.append(i->first); - value_storage.push_back('='); - value_storage.append(i->second); - value_storage.push_back(0); - } - } - - size_t pointer_count_required = - result_indices.size() + 1 + // Null-terminated array of pointers. - (value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer. - std::unique_ptr result(new char*[pointer_count_required]); - - // The string storage goes after the array of pointers. - char* storage_data = reinterpret_cast( - &result.get()[result_indices.size() + 1]); - if (!value_storage.empty()) - memcpy(storage_data, value_storage.data(), value_storage.size()); - - // Fill array of pointers at the beginning of the result. - for (size_t i = 0; i < result_indices.size(); i++) - result[i] = &storage_data[result_indices[i]]; - result[result_indices.size()] = 0; // Null terminator. - - return result; -} - -#endif // OS_WIN - -} // namespace base diff --git a/environment.h b/environment.h deleted file mode 100644 index e842ab087..000000000 --- a/environment.h +++ /dev/null @@ -1,90 +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_ENVIRONMENT_H_ -#define BASE_ENVIRONMENT_H_ - -#include -#include -#include - -#include "base/base_export.h" -#include "base/strings/string16.h" -#include "base/strings/string_piece.h" -#include "build/build_config.h" - -namespace base { - -namespace env_vars { - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -BASE_EXPORT extern const char kHome[]; -#endif - -} // namespace env_vars - -class BASE_EXPORT Environment { - public: - virtual ~Environment(); - - // Returns the appropriate platform-specific instance. - static std::unique_ptr Create(); - - // Gets an environment variable's value and stores it in |result|. - // Returns false if the key is unset. - virtual bool GetVar(StringPiece variable_name, std::string* result) = 0; - - // Syntactic sugar for GetVar(variable_name, nullptr); - virtual bool HasVar(StringPiece variable_name); - - // Returns true on success, otherwise returns false. - virtual bool SetVar(StringPiece variable_name, - const std::string& new_value) = 0; - - // Returns true on success, otherwise returns false. - virtual bool UnSetVar(StringPiece variable_name) = 0; -}; - - -#if defined(OS_WIN) - -typedef string16 NativeEnvironmentString; -typedef std::map - EnvironmentMap; - -// Returns a modified environment vector constructed from the given environment -// and the list of changes given in |changes|. Each key in the environment is -// matched against the first element of the pairs. In the event of a match, the -// value is replaced by the second of the pair, unless the second is empty, in -// which case the key-value is removed. -// -// This Windows version takes and returns a Windows-style environment block -// which is a concatenated list of null-terminated 16-bit strings. The end is -// marked by a double-null terminator. The size of the returned string will -// include the terminators. -BASE_EXPORT string16 AlterEnvironment(const wchar_t* env, - const EnvironmentMap& changes); - -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - -typedef std::string NativeEnvironmentString; -typedef std::map - EnvironmentMap; - -// See general comments for the Windows version above. -// -// This Posix version takes and returns a Posix-style environment block, which -// is a null-terminated list of pointers to null-terminated strings. The -// returned array will have appended to it the storage for the array itself so -// there is only one pointer to manage, but this means that you can't copy the -// array without keeping the original around. -BASE_EXPORT std::unique_ptr AlterEnvironment( - const char* const* env, - const EnvironmentMap& changes); - -#endif - -} // namespace base - -#endif // BASE_ENVIRONMENT_H_ diff --git a/environment_unittest.cc b/environment_unittest.cc deleted file mode 100644 index 23aec5118..000000000 --- a/environment_unittest.cc +++ /dev/null @@ -1,171 +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/environment.h" - -#include - -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -typedef PlatformTest EnvironmentTest; - -namespace base { - -namespace { - -constexpr char kValidEnvironmentVariable[] = "PATH"; - -} // namespace - -TEST_F(EnvironmentTest, GetVar) { - std::unique_ptr env(Environment::Create()); - std::string env_value; - EXPECT_TRUE(env->GetVar(kValidEnvironmentVariable, &env_value)); - EXPECT_NE(env_value, ""); -} - -TEST_F(EnvironmentTest, GetVarReverse) { - std::unique_ptr env(Environment::Create()); - const char kFooUpper[] = "FOO"; - const char kFooLower[] = "foo"; - - // Set a variable in UPPER case. - EXPECT_TRUE(env->SetVar(kFooUpper, kFooLower)); - - // And then try to get this variable passing the lower case. - std::string env_value; - EXPECT_TRUE(env->GetVar(kFooLower, &env_value)); - - EXPECT_STREQ(env_value.c_str(), kFooLower); - - EXPECT_TRUE(env->UnSetVar(kFooUpper)); - - const char kBar[] = "bar"; - // Now do the opposite, set the variable in the lower case. - EXPECT_TRUE(env->SetVar(kFooLower, kBar)); - - // And then try to get this variable passing the UPPER case. - EXPECT_TRUE(env->GetVar(kFooUpper, &env_value)); - - EXPECT_STREQ(env_value.c_str(), kBar); - - EXPECT_TRUE(env->UnSetVar(kFooLower)); -} - -TEST_F(EnvironmentTest, HasVar) { - std::unique_ptr env(Environment::Create()); - EXPECT_TRUE(env->HasVar(kValidEnvironmentVariable)); -} - -TEST_F(EnvironmentTest, SetVar) { - std::unique_ptr env(Environment::Create()); - - const char kFooUpper[] = "FOO"; - const char kFooLower[] = "foo"; - EXPECT_TRUE(env->SetVar(kFooUpper, kFooLower)); - - // Now verify that the environment has the new variable. - EXPECT_TRUE(env->HasVar(kFooUpper)); - - std::string var_value; - EXPECT_TRUE(env->GetVar(kFooUpper, &var_value)); - EXPECT_EQ(var_value, kFooLower); -} - -TEST_F(EnvironmentTest, UnSetVar) { - std::unique_ptr env(Environment::Create()); - - const char kFooUpper[] = "FOO"; - const char kFooLower[] = "foo"; - // First set some environment variable. - EXPECT_TRUE(env->SetVar(kFooUpper, kFooLower)); - - // Now verify that the environment has the new variable. - EXPECT_TRUE(env->HasVar(kFooUpper)); - - // Finally verify that the environment variable was erased. - EXPECT_TRUE(env->UnSetVar(kFooUpper)); - - // And check that the variable has been unset. - EXPECT_FALSE(env->HasVar(kFooUpper)); -} - -#if defined(OS_WIN) - -TEST_F(EnvironmentTest, AlterEnvironment) { - const wchar_t empty[] = L"\0"; - const wchar_t a2[] = L"A=2\0"; - EnvironmentMap changes; - string16 e; - - e = AlterEnvironment(empty, changes); - EXPECT_EQ(0, e[0]); - - changes[L"A"] = L"1"; - e = AlterEnvironment(empty, changes); - EXPECT_EQ(string16(L"A=1\0\0", 5), e); - - changes.clear(); - changes[L"A"] = string16(); - e = AlterEnvironment(empty, changes); - EXPECT_EQ(string16(L"\0\0", 2), e); - - changes.clear(); - e = AlterEnvironment(a2, changes); - EXPECT_EQ(string16(L"A=2\0\0", 5), e); - - changes.clear(); - changes[L"A"] = L"1"; - e = AlterEnvironment(a2, changes); - EXPECT_EQ(string16(L"A=1\0\0", 5), e); - - changes.clear(); - changes[L"A"] = string16(); - e = AlterEnvironment(a2, changes); - EXPECT_EQ(string16(L"\0\0", 2), e); -} - -#else - -TEST_F(EnvironmentTest, AlterEnvironment) { - const char* const empty[] = {nullptr}; - const char* const a2[] = {"A=2", nullptr}; - EnvironmentMap changes; - std::unique_ptr e; - - e = AlterEnvironment(empty, changes); - EXPECT_TRUE(e[0] == nullptr); - - changes["A"] = "1"; - e = AlterEnvironment(empty, changes); - EXPECT_EQ(std::string("A=1"), e[0]); - EXPECT_TRUE(e[1] == nullptr); - - changes.clear(); - changes["A"] = std::string(); - e = AlterEnvironment(empty, changes); - EXPECT_TRUE(e[0] == nullptr); - - changes.clear(); - e = AlterEnvironment(a2, changes); - EXPECT_EQ(std::string("A=2"), e[0]); - EXPECT_TRUE(e[1] == nullptr); - - changes.clear(); - changes["A"] = "1"; - e = AlterEnvironment(a2, changes); - EXPECT_EQ(std::string("A=1"), e[0]); - EXPECT_TRUE(e[1] == nullptr); - - changes.clear(); - changes["A"] = std::string(); - e = AlterEnvironment(a2, changes); - EXPECT_TRUE(e[0] == nullptr); -} - -#endif - -} // namespace base diff --git a/feature_list.cc b/feature_list.cc deleted file mode 100644 index 1610eecbc..000000000 --- a/feature_list.cc +++ /dev/null @@ -1,438 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/feature_list.h" - -#include - -#include -#include - -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/field_trial.h" -#include "base/pickle.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" - -namespace base { - -namespace { - -// Pointer to the FeatureList instance singleton that was set via -// FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to -// have more control over initialization timing. Leaky. -FeatureList* g_feature_list_instance = nullptr; - -// Tracks whether the FeatureList instance was initialized via an accessor. -bool g_initialized_from_accessor = false; - -// An allocator entry for a feature in shared memory. The FeatureEntry is -// followed by a base::Pickle object that contains the feature and trial name. -struct FeatureEntry { - // SHA1(FeatureEntry): Increment this if structure changes! - static constexpr uint32_t kPersistentTypeId = 0x06567CA6 + 1; - - // Expected size for 32/64-bit check. - static constexpr size_t kExpectedInstanceSize = 8; - - // Specifies whether a feature override enables or disables the feature. Same - // values as the OverrideState enum in feature_list.h - uint32_t override_state; - - // Size of the pickled structure, NOT the total size of this entry. - uint32_t pickle_size; - - // Reads the feature and trial name from the pickle. Calling this is only - // valid on an initialized entry that's in shared memory. - bool GetFeatureAndTrialName(StringPiece* feature_name, - StringPiece* trial_name) const { - const char* src = - reinterpret_cast(this) + sizeof(FeatureEntry); - - Pickle pickle(src, pickle_size); - PickleIterator pickle_iter(pickle); - - if (!pickle_iter.ReadStringPiece(feature_name)) - return false; - - // Return true because we are not guaranteed to have a trial name anyways. - auto sink = pickle_iter.ReadStringPiece(trial_name); - ALLOW_UNUSED_LOCAL(sink); - return true; - } -}; - -// Some characters are not allowed to appear in feature names or the associated -// field trial names, as they are used as special characters for command-line -// serialization. This function checks that the strings are ASCII (since they -// are used in command-line API functions that require ASCII) and whether there -// are any reserved characters present, returning true if the string is valid. -// Only called in DCHECKs. -bool IsValidFeatureOrFieldTrialName(const std::string& name) { - return IsStringASCII(name) && name.find_first_of(",<*") == std::string::npos; -} - -} // namespace - -#if DCHECK_IS_CONFIGURABLE -const Feature kDCheckIsFatalFeature{"DcheckIsFatal", - base::FEATURE_DISABLED_BY_DEFAULT}; -#endif // DCHECK_IS_CONFIGURABLE - -FeatureList::FeatureList() = default; - -FeatureList::~FeatureList() = default; - -void FeatureList::InitializeFromCommandLine( - const std::string& enable_features, - const std::string& disable_features) { - DCHECK(!initialized_); - - // Process disabled features first, so that disabled ones take precedence over - // enabled ones (since RegisterOverride() uses insert()). - RegisterOverridesFromCommandLine(disable_features, OVERRIDE_DISABLE_FEATURE); - RegisterOverridesFromCommandLine(enable_features, OVERRIDE_ENABLE_FEATURE); - - initialized_from_command_line_ = true; -} - -void FeatureList::InitializeFromSharedMemory( - PersistentMemoryAllocator* allocator) { - DCHECK(!initialized_); - - PersistentMemoryAllocator::Iterator iter(allocator); - const FeatureEntry* entry; - while ((entry = iter.GetNextOfObject()) != nullptr) { - OverrideState override_state = - static_cast(entry->override_state); - - StringPiece feature_name; - StringPiece trial_name; - if (!entry->GetFeatureAndTrialName(&feature_name, &trial_name)) - continue; - - FieldTrial* trial = FieldTrialList::Find(trial_name.as_string()); - RegisterOverride(feature_name, override_state, trial); - } -} - -bool FeatureList::IsFeatureOverriddenFromCommandLine( - const std::string& feature_name, - OverrideState state) const { - auto it = overrides_.find(feature_name); - return it != overrides_.end() && it->second.overridden_state == state && - !it->second.overridden_by_field_trial; -} - -void FeatureList::AssociateReportingFieldTrial( - const std::string& feature_name, - OverrideState for_overridden_state, - FieldTrial* field_trial) { - DCHECK( - IsFeatureOverriddenFromCommandLine(feature_name, for_overridden_state)); - - // Only one associated field trial is supported per feature. This is generally - // enforced server-side. - OverrideEntry* entry = &overrides_.find(feature_name)->second; - if (entry->field_trial) { - NOTREACHED() << "Feature " << feature_name - << " already has trial: " << entry->field_trial->trial_name() - << ", associating trial: " << field_trial->trial_name(); - return; - } - - entry->field_trial = field_trial; -} - -void FeatureList::RegisterFieldTrialOverride(const std::string& feature_name, - OverrideState override_state, - FieldTrial* field_trial) { - DCHECK(field_trial); - DCHECK(!ContainsKey(overrides_, feature_name) || - !overrides_.find(feature_name)->second.field_trial) - << "Feature " << feature_name - << " has conflicting field trial overrides: " - << overrides_.find(feature_name)->second.field_trial->trial_name() - << " / " << field_trial->trial_name(); - - RegisterOverride(feature_name, override_state, field_trial); -} - -void FeatureList::AddFeaturesToAllocator(PersistentMemoryAllocator* allocator) { - DCHECK(initialized_); - - for (const auto& override : overrides_) { - Pickle pickle; - pickle.WriteString(override.first); - if (override.second.field_trial) - pickle.WriteString(override.second.field_trial->trial_name()); - - size_t total_size = sizeof(FeatureEntry) + pickle.size(); - FeatureEntry* entry = allocator->New(total_size); - if (!entry) - return; - - entry->override_state = override.second.overridden_state; - entry->pickle_size = pickle.size(); - - char* dst = reinterpret_cast(entry) + sizeof(FeatureEntry); - memcpy(dst, pickle.data(), pickle.size()); - - allocator->MakeIterable(entry); - } -} - -void FeatureList::GetFeatureOverrides(std::string* enable_overrides, - std::string* disable_overrides) { - GetFeatureOverridesImpl(enable_overrides, disable_overrides, false); -} - -void FeatureList::GetCommandLineFeatureOverrides( - std::string* enable_overrides, - std::string* disable_overrides) { - GetFeatureOverridesImpl(enable_overrides, disable_overrides, true); -} - -// static -bool FeatureList::IsEnabled(const Feature& feature) { - if (!g_feature_list_instance) { - g_initialized_from_accessor = true; - return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; - } - return g_feature_list_instance->IsFeatureEnabled(feature); -} - -// static -FieldTrial* FeatureList::GetFieldTrial(const Feature& feature) { - if (!g_feature_list_instance) { - g_initialized_from_accessor = true; - return nullptr; - } - return g_feature_list_instance->GetAssociatedFieldTrial(feature); -} - -// static -std::vector FeatureList::SplitFeatureListString( - base::StringPiece input) { - return SplitStringPiece(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); -} - -// static -bool FeatureList::InitializeInstance(const std::string& enable_features, - const std::string& disable_features) { - // We want to initialize a new instance here to support command-line features - // in testing better. For example, we initialize a dummy instance in - // base/test/test_suite.cc, and override it in content/browser/ - // browser_main_loop.cc. - // On the other hand, we want to avoid re-initialization from command line. - // For example, we initialize an instance in chrome/browser/ - // chrome_browser_main.cc and do not override it in content/browser/ - // browser_main_loop.cc. - // If the singleton was previously initialized from within an accessor, we - // want to prevent callers from reinitializing the singleton and masking the - // accessor call(s) which likely returned incorrect information. - CHECK(!g_initialized_from_accessor); - bool instance_existed_before = false; - if (g_feature_list_instance) { - if (g_feature_list_instance->initialized_from_command_line_) - return false; - - delete g_feature_list_instance; - g_feature_list_instance = nullptr; - instance_existed_before = true; - } - - std::unique_ptr feature_list(new base::FeatureList); - feature_list->InitializeFromCommandLine(enable_features, disable_features); - base::FeatureList::SetInstance(std::move(feature_list)); - return !instance_existed_before; -} - -// static -FeatureList* FeatureList::GetInstance() { - return g_feature_list_instance; -} - -// static -void FeatureList::SetInstance(std::unique_ptr instance) { - DCHECK(!g_feature_list_instance); - instance->FinalizeInitialization(); - - // Note: Intentional leak of global singleton. - g_feature_list_instance = instance.release(); - -#if DCHECK_IS_CONFIGURABLE - // Update the behaviour of LOG_DCHECK to match the Feature configuration. - // DCHECK is also forced to be FATAL if we are running a death-test. - // TODO(asvitkine): If we find other use-cases that need integrating here - // then define a proper API/hook for the purpose. - if (base::FeatureList::IsEnabled(kDCheckIsFatalFeature) || - base::CommandLine::ForCurrentProcess()->HasSwitch( - "gtest_internal_run_death_test")) { - logging::LOG_DCHECK = logging::LOG_FATAL; - } else { - logging::LOG_DCHECK = logging::LOG_INFO; - } -#endif // DCHECK_IS_CONFIGURABLE -} - -// static -std::unique_ptr FeatureList::ClearInstanceForTesting() { - FeatureList* old_instance = g_feature_list_instance; - g_feature_list_instance = nullptr; - g_initialized_from_accessor = false; - return base::WrapUnique(old_instance); -} - -// static -void FeatureList::RestoreInstanceForTesting( - std::unique_ptr instance) { - DCHECK(!g_feature_list_instance); - // Note: Intentional leak of global singleton. - g_feature_list_instance = instance.release(); -} - -void FeatureList::FinalizeInitialization() { - DCHECK(!initialized_); - initialized_ = true; -} - -bool FeatureList::IsFeatureEnabled(const Feature& feature) { - DCHECK(initialized_); - DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; - DCHECK(CheckFeatureIdentity(feature)) << feature.name; - - auto it = overrides_.find(feature.name); - if (it != overrides_.end()) { - const OverrideEntry& entry = it->second; - - // Activate the corresponding field trial, if necessary. - if (entry.field_trial) - entry.field_trial->group(); - - // TODO(asvitkine) Expand this section as more support is added. - - // If marked as OVERRIDE_USE_DEFAULT, simply return the default state below. - if (entry.overridden_state != OVERRIDE_USE_DEFAULT) - return entry.overridden_state == OVERRIDE_ENABLE_FEATURE; - } - // Otherwise, return the default state. - return feature.default_state == FEATURE_ENABLED_BY_DEFAULT; -} - -FieldTrial* FeatureList::GetAssociatedFieldTrial(const Feature& feature) { - DCHECK(initialized_); - DCHECK(IsValidFeatureOrFieldTrialName(feature.name)) << feature.name; - DCHECK(CheckFeatureIdentity(feature)) << feature.name; - - auto it = overrides_.find(feature.name); - if (it != overrides_.end()) { - const OverrideEntry& entry = it->second; - return entry.field_trial; - } - - return nullptr; -} - -void FeatureList::RegisterOverridesFromCommandLine( - const std::string& feature_list, - OverrideState overridden_state) { - for (const auto& value : SplitFeatureListString(feature_list)) { - StringPiece feature_name = value; - base::FieldTrial* trial = nullptr; - - // The entry may be of the form FeatureNametrial_name())) - << field_trial->trial_name(); - } - if (feature_name.starts_with("*")) { - feature_name = feature_name.substr(1); - overridden_state = OVERRIDE_USE_DEFAULT; - } - - // Note: The semantics of insert() is that it does not overwrite the entry if - // one already exists for the key. Thus, only the first override for a given - // feature name takes effect. - overrides_.insert(std::make_pair( - feature_name.as_string(), OverrideEntry(overridden_state, field_trial))); -} - -void FeatureList::GetFeatureOverridesImpl(std::string* enable_overrides, - std::string* disable_overrides, - bool command_line_only) { - DCHECK(initialized_); - - enable_overrides->clear(); - disable_overrides->clear(); - - // Note: Since |overrides_| is a std::map, iteration will be in alphabetical - // order. This is not guaranteed to users of this function, but is useful for - // tests to assume the order. - for (const auto& entry : overrides_) { - if (command_line_only && - (entry.second.field_trial != nullptr || - entry.second.overridden_state == OVERRIDE_USE_DEFAULT)) { - continue; - } - - std::string* target_list = nullptr; - switch (entry.second.overridden_state) { - case OVERRIDE_USE_DEFAULT: - case OVERRIDE_ENABLE_FEATURE: - target_list = enable_overrides; - break; - case OVERRIDE_DISABLE_FEATURE: - target_list = disable_overrides; - break; - } - - if (!target_list->empty()) - target_list->push_back(','); - if (entry.second.overridden_state == OVERRIDE_USE_DEFAULT) - target_list->push_back('*'); - target_list->append(entry.first); - if (entry.second.field_trial) { - target_list->push_back('<'); - target_list->append(entry.second.field_trial->trial_name()); - } - } -} - -bool FeatureList::CheckFeatureIdentity(const Feature& feature) { - AutoLock auto_lock(feature_identity_tracker_lock_); - - auto it = feature_identity_tracker_.find(feature.name); - if (it == feature_identity_tracker_.end()) { - // If it's not tracked yet, register it. - feature_identity_tracker_[feature.name] = &feature; - return true; - } - // Compare address of |feature| to the existing tracked entry. - return it->second == &feature; -} - -FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state, - FieldTrial* field_trial) - : overridden_state(overridden_state), - field_trial(field_trial), - overridden_by_field_trial(field_trial != nullptr) {} - -} // namespace base diff --git a/feature_list.h b/feature_list.h deleted file mode 100644 index 2237507a4..000000000 --- a/feature_list.h +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FEATURE_LIST_H_ -#define BASE_FEATURE_LIST_H_ - -#include -#include -#include -#include - -#include "base/base_export.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/strings/string_piece.h" -#include "base/synchronization/lock.h" - -namespace base { - -class FieldTrial; - -// Specifies whether a given feature is enabled or disabled by default. -enum FeatureState { - FEATURE_DISABLED_BY_DEFAULT, - FEATURE_ENABLED_BY_DEFAULT, -}; - -// The Feature struct is used to define the default state for a feature. See -// comment below for more details. There must only ever be one struct instance -// for a given feature name - generally defined as a constant global variable or -// file static. It should never be used as a constexpr as it breaks -// pointer-based identity lookup. -struct BASE_EXPORT Feature { - // The name of the feature. This should be unique to each feature and is used - // for enabling/disabling features via command line flags and experiments. - // It is strongly recommended to use CamelCase style for feature names, e.g. - // "MyGreatFeature". - const char* const name; - - // The default state (i.e. enabled or disabled) for this feature. - const FeatureState default_state; -}; - -#if DCHECK_IS_CONFIGURABLE -// DCHECKs have been built-in, and are configurable at run-time to be fatal, or -// not, via a DcheckIsFatal feature. We define the Feature here since it is -// checked in FeatureList::SetInstance(). See https://crbug.com/596231. -extern BASE_EXPORT const Feature kDCheckIsFatalFeature; -#endif // DCHECK_IS_CONFIGURABLE - -// The FeatureList class is used to determine whether a given feature is on or -// off. It provides an authoritative answer, taking into account command-line -// overrides and experimental control. -// -// The basic use case is for any feature that can be toggled (e.g. through -// command-line or an experiment) to have a defined Feature struct, e.g.: -// -// const base::Feature kMyGreatFeature { -// "MyGreatFeature", base::FEATURE_ENABLED_BY_DEFAULT -// }; -// -// Then, client code that wishes to query the state of the feature would check: -// -// if (base::FeatureList::IsEnabled(kMyGreatFeature)) { -// // Feature code goes here. -// } -// -// Behind the scenes, the above call would take into account any command-line -// flags to enable or disable the feature, any experiments that may control it -// and finally its default state (in that order of priority), to determine -// whether the feature is on. -// -// Features can be explicitly forced on or off by specifying a list of comma- -// separated feature names via the following command-line flags: -// -// --enable-features=Feature5,Feature7 -// --disable-features=Feature1,Feature2,Feature3 -// -// To enable/disable features in a test, do NOT append --enable-features or -// --disable-features to the command-line directly. Instead, use -// ScopedFeatureList. See base/test/scoped_feature_list.h for details. -// -// After initialization (which should be done single-threaded), the FeatureList -// API is thread safe. -// -// Note: This class is a singleton, but does not use base/memory/singleton.h in -// order to have control over its initialization sequence. Specifically, the -// intended use is to create an instance of this class and fully initialize it, -// before setting it as the singleton for a process, via SetInstance(). -class BASE_EXPORT FeatureList { - public: - FeatureList(); - ~FeatureList(); - - // Initializes feature overrides via command-line flags |enable_features| and - // |disable_features|, each of which is a comma-separated list of features to - // enable or disable, respectively. If a feature appears on both lists, then - // it will be disabled. If a list entry has the format "FeatureName SplitFeatureListString( - base::StringPiece input); - - // Initializes and sets an instance of FeatureList with feature overrides via - // command-line flags |enable_features| and |disable_features| if one has not - // already been set from command-line flags. Returns true if an instance did - // not previously exist. See InitializeFromCommandLine() for more details - // about |enable_features| and |disable_features| parameters. - static bool InitializeInstance(const std::string& enable_features, - const std::string& disable_features); - - // Returns the singleton instance of FeatureList. Will return null until an - // instance is registered via SetInstance(). - static FeatureList* GetInstance(); - - // Registers the given |instance| to be the singleton feature list for this - // process. This should only be called once and |instance| must not be null. - // Note: If you are considering using this for the purposes of testing, take - // a look at using base/test/scoped_feature_list.h instead. - static void SetInstance(std::unique_ptr instance); - - // Clears the previously-registered singleton instance for tests and returns - // the old instance. - // Note: Most tests should never call this directly. Instead consider using - // base::test::ScopedFeatureList. - static std::unique_ptr ClearInstanceForTesting(); - - // Sets a given (initialized) |instance| to be the singleton feature list, - // for testing. Existing instance must be null. This is primarily intended - // to support base::test::ScopedFeatureList helper class. - static void RestoreInstanceForTesting(std::unique_ptr instance); - - private: - FRIEND_TEST_ALL_PREFIXES(FeatureListTest, CheckFeatureIdentity); - FRIEND_TEST_ALL_PREFIXES(FeatureListTest, - StoreAndRetrieveFeaturesFromSharedMemory); - FRIEND_TEST_ALL_PREFIXES(FeatureListTest, - StoreAndRetrieveAssociatedFeaturesFromSharedMemory); - - struct OverrideEntry { - // The overridden enable (on/off) state of the feature. - const OverrideState overridden_state; - - // An optional associated field trial, which will be activated when the - // state of the feature is queried for the first time. Weak pointer to the - // FieldTrial object that is owned by the FieldTrialList singleton. - base::FieldTrial* field_trial; - - // Specifies whether the feature's state is overridden by |field_trial|. - // If it's not, and |field_trial| is not null, it means it is simply an - // associated field trial for reporting purposes (and |overridden_state| - // came from the command-line). - const bool overridden_by_field_trial; - - // TODO(asvitkine): Expand this as more support is added. - - // Constructs an OverrideEntry for the given |overridden_state|. If - // |field_trial| is not null, it implies that |overridden_state| comes from - // the trial, so |overridden_by_field_trial| will be set to true. - OverrideEntry(OverrideState overridden_state, FieldTrial* field_trial); - }; - - // Finalizes the initialization state of the FeatureList, so that no further - // overrides can be registered. This is called by SetInstance() on the - // singleton feature list that is being registered. - void FinalizeInitialization(); - - // Returns whether the given |feature| is enabled. This is invoked by the - // public FeatureList::IsEnabled() static function on the global singleton. - // Requires the FeatureList to have already been fully initialized. - bool IsFeatureEnabled(const Feature& feature); - - // Returns the field trial associated with the given |feature|. This is - // invoked by the public FeatureList::GetFieldTrial() static function on the - // global singleton. Requires the FeatureList to have already been fully - // initialized. - base::FieldTrial* GetAssociatedFieldTrial(const Feature& feature); - - // For each feature name in comma-separated list of strings |feature_list|, - // registers an override with the specified |overridden_state|. Also, will - // associate an optional named field trial if the entry is of the format - // "FeatureName overrides_; - - // Locked map that keeps track of seen features, to ensure a single feature is - // only defined once. This verification is only done in builds with DCHECKs - // enabled. - Lock feature_identity_tracker_lock_; - std::map feature_identity_tracker_; - - // Whether this object has been fully initialized. This gets set to true as a - // result of FinalizeInitialization(). - bool initialized_ = false; - - // Whether this object has been initialized from command line. - bool initialized_from_command_line_ = false; - - DISALLOW_COPY_AND_ASSIGN(FeatureList); -}; - -} // namespace base - -#endif // BASE_FEATURE_LIST_H_ diff --git a/feature_list_unittest.cc b/feature_list_unittest.cc deleted file mode 100644 index 164997a72..000000000 --- a/feature_list_unittest.cc +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/feature_list.h" - -#include - -#include -#include - -#include "base/format_macros.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/field_trial.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -constexpr char kFeatureOnByDefaultName[] = "OnByDefault"; -struct Feature kFeatureOnByDefault { - kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT -}; - -constexpr char kFeatureOffByDefaultName[] = "OffByDefault"; -struct Feature kFeatureOffByDefault { - kFeatureOffByDefaultName, FEATURE_DISABLED_BY_DEFAULT -}; - -std::string SortFeatureListString(const std::string& feature_list) { - std::vector features = - FeatureList::SplitFeatureListString(feature_list); - std::sort(features.begin(), features.end()); - return JoinString(features, ","); -} - -} // namespace - -class FeatureListTest : public testing::Test { - public: - FeatureListTest() : feature_list_(nullptr) { - RegisterFeatureListInstance(WrapUnique(new FeatureList)); - } - ~FeatureListTest() override { ClearFeatureListInstance(); } - - void RegisterFeatureListInstance(std::unique_ptr feature_list) { - FeatureList::ClearInstanceForTesting(); - feature_list_ = feature_list.get(); - FeatureList::SetInstance(std::move(feature_list)); - } - void ClearFeatureListInstance() { - FeatureList::ClearInstanceForTesting(); - feature_list_ = nullptr; - } - - FeatureList* feature_list() { return feature_list_; } - - private: - // Weak. Owned by the FeatureList::SetInstance(). - FeatureList* feature_list_; - - DISALLOW_COPY_AND_ASSIGN(FeatureListTest); -}; - -TEST_F(FeatureListTest, DefaultStates) { - EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); - EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); -} - -TEST_F(FeatureListTest, InitializeFromCommandLine) { - struct { - const char* enable_features; - const char* disable_features; - bool expected_feature_on_state; - bool expected_feature_off_state; - } test_cases[] = { - {"", "", true, false}, - {"OffByDefault", "", true, true}, - {"OffByDefault", "OnByDefault", false, true}, - {"OnByDefault,OffByDefault", "", true, true}, - {"", "OnByDefault,OffByDefault", false, false}, - // In the case an entry is both, disable takes precedence. - {"OnByDefault", "OnByDefault,OffByDefault", false, false}, - }; - - for (size_t i = 0; i < arraysize(test_cases); ++i) { - const auto& test_case = test_cases[i]; - SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i, - test_case.enable_features, - test_case.disable_features)); - - ClearFeatureListInstance(); - std::unique_ptr feature_list(new FeatureList); - feature_list->InitializeFromCommandLine(test_case.enable_features, - test_case.disable_features); - RegisterFeatureListInstance(std::move(feature_list)); - - EXPECT_EQ(test_case.expected_feature_on_state, - FeatureList::IsEnabled(kFeatureOnByDefault)) - << i; - EXPECT_EQ(test_case.expected_feature_off_state, - FeatureList::IsEnabled(kFeatureOffByDefault)) - << i; - } -} - -TEST_F(FeatureListTest, CheckFeatureIdentity) { - // Tests that CheckFeatureIdentity() correctly detects when two different - // structs with the same feature name are passed to it. - - // Call it twice for each feature at the top of the file, since the first call - // makes it remember the entry and the second call will verify it. - EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault)); - EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault)); - EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault)); - EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault)); - - // Now, call it with a distinct struct for |kFeatureOnByDefaultName|, which - // should return false. - struct Feature kFeatureOnByDefault2 { - kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT - }; - EXPECT_FALSE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault2)); -} - -TEST_F(FeatureListTest, FieldTrialOverrides) { - struct { - FeatureList::OverrideState trial1_state; - FeatureList::OverrideState trial2_state; - } test_cases[] = { - {FeatureList::OVERRIDE_DISABLE_FEATURE, - FeatureList::OVERRIDE_DISABLE_FEATURE}, - {FeatureList::OVERRIDE_DISABLE_FEATURE, - FeatureList::OVERRIDE_ENABLE_FEATURE}, - {FeatureList::OVERRIDE_ENABLE_FEATURE, - FeatureList::OVERRIDE_DISABLE_FEATURE}, - {FeatureList::OVERRIDE_ENABLE_FEATURE, - FeatureList::OVERRIDE_ENABLE_FEATURE}, - }; - - FieldTrial::ActiveGroup active_group; - for (size_t i = 0; i < arraysize(test_cases); ++i) { - const auto& test_case = test_cases[i]; - SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i)); - - ClearFeatureListInstance(); - - FieldTrialList field_trial_list(nullptr); - std::unique_ptr feature_list(new FeatureList); - - FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A"); - FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B"); - feature_list->RegisterFieldTrialOverride(kFeatureOnByDefaultName, - test_case.trial1_state, trial1); - feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName, - test_case.trial2_state, trial2); - RegisterFeatureListInstance(std::move(feature_list)); - - // Initially, neither trial should be active. - EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name())); - EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name())); - - const bool expected_enabled_1 = - (test_case.trial1_state == FeatureList::OVERRIDE_ENABLE_FEATURE); - EXPECT_EQ(expected_enabled_1, FeatureList::IsEnabled(kFeatureOnByDefault)); - // The above should have activated |trial1|. - EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name())); - EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name())); - - const bool expected_enabled_2 = - (test_case.trial2_state == FeatureList::OVERRIDE_ENABLE_FEATURE); - EXPECT_EQ(expected_enabled_2, FeatureList::IsEnabled(kFeatureOffByDefault)); - // The above should have activated |trial2|. - EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name())); - EXPECT_TRUE(FieldTrialList::IsTrialActive(trial2->trial_name())); - } -} - -TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) { - FieldTrialList field_trial_list(nullptr); - std::unique_ptr feature_list(new FeatureList); - - FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A"); - FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B"); - feature_list->RegisterFieldTrialOverride( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1); - feature_list->RegisterFieldTrialOverride( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2); - RegisterFeatureListInstance(std::move(feature_list)); - - // Initially, neither trial should be active. - EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name())); - EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name())); - - // Check the feature enabled state is its default. - EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); - // The above should have activated |trial1|. - EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name())); - EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name())); - - // Check the feature enabled state is its default. - EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); - // The above should have activated |trial2|. - EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name())); - EXPECT_TRUE(FieldTrialList::IsTrialActive(trial2->trial_name())); -} - -TEST_F(FeatureListTest, CommandLineTakesPrecedenceOverFieldTrial) { - ClearFeatureListInstance(); - - FieldTrialList field_trial_list(nullptr); - std::unique_ptr feature_list(new FeatureList); - - // The feature is explicitly enabled on the command-line. - feature_list->InitializeFromCommandLine(kFeatureOffByDefaultName, ""); - - // But the FieldTrial would set the feature to disabled. - FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A"); - feature_list->RegisterFieldTrialOverride( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, trial); - RegisterFeatureListInstance(std::move(feature_list)); - - EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name())); - // Command-line should take precedence. - EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault)); - // Since the feature is on due to the command-line, and not as a result of the - // field trial, the field trial should not be activated (since the Associate* - // API wasn't used.) - EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name())); -} - -TEST_F(FeatureListTest, IsFeatureOverriddenFromCommandLine) { - ClearFeatureListInstance(); - - FieldTrialList field_trial_list(nullptr); - std::unique_ptr feature_list(new FeatureList); - - // No features are overridden from the command line yet - EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); - EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); - EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); - EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); - - // Now, enable |kFeatureOffByDefaultName| via the command-line. - feature_list->InitializeFromCommandLine(kFeatureOffByDefaultName, ""); - - // It should now be overridden for the enabled group. - EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); - EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); - - // Register a field trial to associate with the feature and ensure that the - // results are still the same. - feature_list->AssociateReportingFieldTrial( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE, - FieldTrialList::CreateFieldTrial("Trial1", "A")); - EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); - EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); - - // Now, register a field trial to override |kFeatureOnByDefaultName| state - // and check that the function still returns false for that feature. - feature_list->RegisterFieldTrialOverride( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, - FieldTrialList::CreateFieldTrial("Trial2", "A")); - EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); - EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); - RegisterFeatureListInstance(std::move(feature_list)); - - // Check the expected feature states for good measure. - EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault)); - EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault)); -} - -TEST_F(FeatureListTest, AssociateReportingFieldTrial) { - struct { - const char* enable_features; - const char* disable_features; - bool expected_enable_trial_created; - bool expected_disable_trial_created; - } test_cases[] = { - // If no enable/disable flags are specified, no trials should be created. - {"", "", false, false}, - // Enabling the feature should result in the enable trial created. - {kFeatureOffByDefaultName, "", true, false}, - // Disabling the feature should result in the disable trial created. - {"", kFeatureOffByDefaultName, false, true}, - }; - - const char kTrialName[] = "ForcingTrial"; - const char kForcedOnGroupName[] = "ForcedOn"; - const char kForcedOffGroupName[] = "ForcedOff"; - - for (size_t i = 0; i < arraysize(test_cases); ++i) { - const auto& test_case = test_cases[i]; - SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i, - test_case.enable_features, - test_case.disable_features)); - - ClearFeatureListInstance(); - - FieldTrialList field_trial_list(nullptr); - std::unique_ptr feature_list(new FeatureList); - feature_list->InitializeFromCommandLine(test_case.enable_features, - test_case.disable_features); - - FieldTrial* enable_trial = nullptr; - if (feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)) { - enable_trial = base::FieldTrialList::CreateFieldTrial(kTrialName, - kForcedOnGroupName); - feature_list->AssociateReportingFieldTrial( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE, - enable_trial); - } - FieldTrial* disable_trial = nullptr; - if (feature_list->IsFeatureOverriddenFromCommandLine( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)) { - disable_trial = base::FieldTrialList::CreateFieldTrial( - kTrialName, kForcedOffGroupName); - feature_list->AssociateReportingFieldTrial( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, - disable_trial); - } - EXPECT_EQ(test_case.expected_enable_trial_created, enable_trial != nullptr); - EXPECT_EQ(test_case.expected_disable_trial_created, - disable_trial != nullptr); - RegisterFeatureListInstance(std::move(feature_list)); - - EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); - if (disable_trial) { - EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); - EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName)); - EXPECT_EQ(kForcedOffGroupName, disable_trial->group_name()); - } else if (enable_trial) { - EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault)); - EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName)); - EXPECT_EQ(kForcedOnGroupName, enable_trial->group_name()); - } - } -} - -TEST_F(FeatureListTest, GetFeatureOverrides) { - ClearFeatureListInstance(); - FieldTrialList field_trial_list(nullptr); - std::unique_ptr feature_list(new FeatureList); - feature_list->InitializeFromCommandLine("A,X", "D"); - - FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group"); - feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName, - FeatureList::OVERRIDE_ENABLE_FEATURE, - trial); - - RegisterFeatureListInstance(std::move(feature_list)); - - std::string enable_features; - std::string disable_features; - FeatureList::GetInstance()->GetFeatureOverrides(&enable_features, - &disable_features); - EXPECT_EQ("A,OffByDefaultGetCommandLineFeatureOverrides(&enable_features, - &disable_features); - EXPECT_EQ("A,X", SortFeatureListString(enable_features)); - EXPECT_EQ("D", SortFeatureListString(disable_features)); -} - -TEST_F(FeatureListTest, GetFeatureOverrides_UseDefault) { - ClearFeatureListInstance(); - FieldTrialList field_trial_list(nullptr); - std::unique_ptr feature_list(new FeatureList); - feature_list->InitializeFromCommandLine("A,X", "D"); - - FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group"); - feature_list->RegisterFieldTrialOverride( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial); - - RegisterFeatureListInstance(std::move(feature_list)); - - std::string enable_features; - std::string disable_features; - FeatureList::GetInstance()->GetFeatureOverrides(&enable_features, - &disable_features); - EXPECT_EQ("*OffByDefault feature_list(new FeatureList); - feature_list->RegisterFieldTrialOverride( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial); - RegisterFeatureListInstance(std::move(feature_list)); - - EXPECT_EQ(trial, FeatureList::GetFieldTrial(kFeatureOnByDefault)); - EXPECT_EQ(nullptr, FeatureList::GetFieldTrial(kFeatureOffByDefault)); -} - -TEST_F(FeatureListTest, InitializeFromCommandLine_WithFieldTrials) { - ClearFeatureListInstance(); - FieldTrialList field_trial_list(nullptr); - FieldTrialList::CreateFieldTrial("Trial", "Group"); - std::unique_ptr feature_list(new FeatureList); - feature_list->InitializeFromCommandLine("A,OffByDefault feature_list(new FeatureList); - feature_list->InitializeFromCommandLine( - "A,*OffByDefault feature_list(new base::FeatureList); - FeatureList::SetInstance(std::move(feature_list)); - EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); - EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); - - // Initialize from command line if we haven't yet. - FeatureList::InitializeInstance("", kFeatureOnByDefaultName); - EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault)); - EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); - - // Do not initialize from commandline if we have already. - FeatureList::InitializeInstance(kFeatureOffByDefaultName, ""); - EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault)); - EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); -} - -TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) { - ClearFeatureListInstance(); - // This test case simulates the calling pattern found in code which does not - // explicitly initialize the features list. - // All IsEnabled() calls should return the default value in this scenario. - EXPECT_EQ(nullptr, FeatureList::GetInstance()); - EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); - EXPECT_EQ(nullptr, FeatureList::GetInstance()); - EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); -} - -TEST_F(FeatureListTest, StoreAndRetrieveFeaturesFromSharedMemory) { - std::unique_ptr feature_list(new base::FeatureList); - - // Create some overrides. - feature_list->RegisterOverride(kFeatureOffByDefaultName, - FeatureList::OVERRIDE_ENABLE_FEATURE, nullptr); - feature_list->RegisterOverride( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, nullptr); - feature_list->FinalizeInitialization(); - - // Create an allocator and store the overrides. - std::unique_ptr shm(new SharedMemory()); - shm->CreateAndMapAnonymous(4 << 10); - SharedPersistentMemoryAllocator allocator(std::move(shm), 1, "", false); - feature_list->AddFeaturesToAllocator(&allocator); - - std::unique_ptr feature_list2(new base::FeatureList); - - // Check that the new feature list is empty. - EXPECT_FALSE(feature_list2->IsFeatureOverriddenFromCommandLine( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); - EXPECT_FALSE(feature_list2->IsFeatureOverriddenFromCommandLine( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); - - feature_list2->InitializeFromSharedMemory(&allocator); - // Check that the new feature list now has 2 overrides. - EXPECT_TRUE(feature_list2->IsFeatureOverriddenFromCommandLine( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); - EXPECT_TRUE(feature_list2->IsFeatureOverriddenFromCommandLine( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); -} - -TEST_F(FeatureListTest, StoreAndRetrieveAssociatedFeaturesFromSharedMemory) { - FieldTrialList field_trial_list(nullptr); - std::unique_ptr feature_list(new base::FeatureList); - - // Create some overrides. - FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A"); - FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B"); - feature_list->RegisterFieldTrialOverride( - kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1); - feature_list->RegisterFieldTrialOverride( - kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2); - feature_list->FinalizeInitialization(); - - // Create an allocator and store the overrides. - std::unique_ptr shm(new SharedMemory()); - shm->CreateAndMapAnonymous(4 << 10); - SharedPersistentMemoryAllocator allocator(std::move(shm), 1, "", false); - feature_list->AddFeaturesToAllocator(&allocator); - - std::unique_ptr feature_list2(new base::FeatureList); - feature_list2->InitializeFromSharedMemory(&allocator); - feature_list2->FinalizeInitialization(); - - // Check that the field trials are still associated. - FieldTrial* associated_trial1 = - feature_list2->GetAssociatedFieldTrial(kFeatureOnByDefault); - FieldTrial* associated_trial2 = - feature_list2->GetAssociatedFieldTrial(kFeatureOffByDefault); - EXPECT_EQ(associated_trial1, trial1); - EXPECT_EQ(associated_trial2, trial2); -} - -} // namespace base diff --git a/file_descriptor_posix.h b/file_descriptor_posix.h deleted file mode 100644 index 2a366116a..000000000 --- a/file_descriptor_posix.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FILE_DESCRIPTOR_POSIX_H_ -#define BASE_FILE_DESCRIPTOR_POSIX_H_ - -#include "base/files/file.h" -#include "base/files/scoped_file.h" - -namespace base { - -// ----------------------------------------------------------------------------- -// We introduct a special structure for file descriptors in order that we are -// able to use template specialisation to special-case their handling. -// -// IMPORTANT: This is primarily intended for use when sending file descriptors -// over IPC. Even if |auto_close| is true, base::FileDescriptor does NOT close() -// |fd| when going out of scope. Instead, a consumer of a base::FileDescriptor -// must invoke close() on |fd| if |auto_close| is true. -// -// In the case of IPC, the the IPC subsystem knows to close() |fd| after sending -// a message that contains a base::FileDescriptor if auto_close == true. On the -// other end, the receiver must make sure to close() |fd| after it has finished -// processing the IPC message. See the IPC::ParamTraits<> specialization in -// ipc/ipc_message_utils.h for all the details. -// ----------------------------------------------------------------------------- -struct FileDescriptor { - FileDescriptor() : fd(-1), auto_close(false) {} - - FileDescriptor(int ifd, bool iauto_close) : fd(ifd), auto_close(iauto_close) { - } - - FileDescriptor(File file) : fd(file.TakePlatformFile()), auto_close(true) {} - explicit FileDescriptor(ScopedFD fd) : fd(fd.release()), 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; - } - - int fd; - // If true, this file descriptor should be closed after it has been used. For - // example an IPC system might interpret this flag as indicating that the - // file descriptor it has been given should be closed after use. - bool auto_close; -}; - -} // namespace base - -#endif // BASE_FILE_DESCRIPTOR_POSIX_H_ diff --git a/file_descriptor_store.cc b/file_descriptor_store.cc deleted file mode 100644 index 71cf2b3f5..000000000 --- a/file_descriptor_store.cc +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2017 The Chromium 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/file_descriptor_store.h" - -#include - -#include "base/logging.h" - -namespace base { - -FileDescriptorStore::Descriptor::Descriptor(const std::string& key, - base::ScopedFD fd) - : key(key), - fd(std::move(fd)), - region(base::MemoryMappedFile::Region::kWholeFile) {} - -FileDescriptorStore::Descriptor::Descriptor( - const std::string& key, - base::ScopedFD fd, - base::MemoryMappedFile::Region region) - : key(key), fd(std::move(fd)), region(region) {} - -FileDescriptorStore::Descriptor::Descriptor( - FileDescriptorStore::Descriptor&& other) - : key(other.key), fd(std::move(other.fd)), region(other.region) {} - -FileDescriptorStore::Descriptor::~Descriptor() = default; - -// static -FileDescriptorStore& FileDescriptorStore::GetInstance() { - static FileDescriptorStore* store = new FileDescriptorStore; - return *store; -} - -base::ScopedFD FileDescriptorStore::TakeFD( - const std::string& key, - base::MemoryMappedFile::Region* region) { - base::ScopedFD fd = MaybeTakeFD(key, region); - if (!fd.is_valid()) - DLOG(DCHECK) << "Unknown global descriptor: " << key; - return fd; -} - -base::ScopedFD FileDescriptorStore::MaybeTakeFD( - const std::string& key, - base::MemoryMappedFile::Region* region) { - auto iter = descriptors_.find(key); - if (iter == descriptors_.end()) - return base::ScopedFD(); - *region = iter->second.region; - base::ScopedFD result = std::move(iter->second.fd); - descriptors_.erase(iter); - return result; -} - -void FileDescriptorStore::Set(const std::string& key, base::ScopedFD fd) { - Set(key, std::move(fd), base::MemoryMappedFile::Region::kWholeFile); -} - -void FileDescriptorStore::Set(const std::string& key, - base::ScopedFD fd, - base::MemoryMappedFile::Region region) { - Descriptor descriptor(key, std::move(fd), region); - descriptors_.insert(std::make_pair(key, std::move(descriptor))); -} - -FileDescriptorStore::FileDescriptorStore() = default; - -FileDescriptorStore::~FileDescriptorStore() = default; - -} // namespace base diff --git a/file_descriptor_store.h b/file_descriptor_store.h deleted file mode 100644 index b6bd07991..000000000 --- a/file_descriptor_store.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2017 The Chromium 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_FILE_DESCRIPTOR_STORE_H_ -#define BASE_FILE_DESCRIPTOR_STORE_H_ - -#include -#include - -#include "base/files/memory_mapped_file.h" -#include "base/files/scoped_file.h" -#include "base/macros.h" - -namespace base { - -// The file descriptor store is used to associate file descriptors with keys -// that must be unique. -// It is used to share file descriptors from a process to its child. -class BASE_EXPORT FileDescriptorStore { - public: - struct Descriptor { - Descriptor(const std::string& key, base::ScopedFD fd); - Descriptor(const std::string& key, - base::ScopedFD fd, - base::MemoryMappedFile::Region region); - Descriptor(Descriptor&& other); - ~Descriptor(); - - Descriptor& operator=(Descriptor&& other) = default; - - // Globally unique key. - std::string key; - // Actual FD. - base::ScopedFD fd; - // Optional region, defaults to kWholeFile. - base::MemoryMappedFile::Region region; - }; - using Mapping = std::map; - - // Returns the singleton instance of FileDescriptorStore. - static FileDescriptorStore& GetInstance(); - - // Gets a descriptor given a key and also populates |region|. - // It is a fatal error if the key is not known. - base::ScopedFD TakeFD(const std::string& key, - base::MemoryMappedFile::Region* region); - - // Gets a descriptor given a key. Returns an empty ScopedFD on error. - base::ScopedFD MaybeTakeFD(const std::string& key, - base::MemoryMappedFile::Region* region); - - // Sets the descriptor for the given |key|. This sets the region associated - // with |key| to kWholeFile. - void Set(const std::string& key, base::ScopedFD fd); - - // Sets the descriptor and |region| for the given |key|. - void Set(const std::string& key, - base::ScopedFD fd, - base::MemoryMappedFile::Region region); - - private: - FileDescriptorStore(); - ~FileDescriptorStore(); - - Mapping descriptors_; - - DISALLOW_COPY_AND_ASSIGN(FileDescriptorStore); -}; - -} // namespace base - -#endif // BASE_FILE_DESCRIPTOR_STORE_H_ diff --git a/file_version_info.h b/file_version_info.h deleted file mode 100644 index 3b9457cce..000000000 --- a/file_version_info.h +++ /dev/null @@ -1,73 +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_FILE_VERSION_INFO_H_ -#define BASE_FILE_VERSION_INFO_H_ - -#include - -#include "build/build_config.h" -#include "base/base_export.h" -#include "base/strings/string16.h" - -#if defined(OS_WIN) -#include -#endif - -namespace base { -class FilePath; -} - -// Provides an interface for accessing the version information for a file. This -// is the information you access when you select a file in the Windows Explorer, -// right-click select Properties, then click the Version tab, and on the Mac -// when you select a file in the Finder and do a Get Info. -// -// This list of properties is straight out of Win32's VerQueryValue -// and the Mac -// version returns values from the Info.plist as appropriate. TODO(avi): make -// this a less-obvious Windows-ism. - -class BASE_EXPORT FileVersionInfo { - public: - virtual ~FileVersionInfo() {} -#if defined(OS_WIN) || defined(OS_MACOSX) - // Creates a FileVersionInfo for the specified path. Returns NULL if something - // goes wrong (typically the file does not exit or cannot be opened). The - // returned object should be deleted when you are done with it. - static FileVersionInfo* CreateFileVersionInfo( - const base::FilePath& file_path); -#endif // OS_WIN || OS_MACOSX - -#if defined(OS_WIN) - // Creates a FileVersionInfo for the specified module. Returns NULL in case - // of error. The returned object should be deleted when you are done with it. - static FileVersionInfo* CreateFileVersionInfoForModule(HMODULE module); -#else - // Creates a FileVersionInfo for the current module. Returns NULL in case - // of error. The returned object should be deleted when you are done with it. - static FileVersionInfo* CreateFileVersionInfoForCurrentModule(); -#endif // OS_WIN - - // Accessors to the different version properties. - // Returns an empty string if the property is not found. - virtual base::string16 company_name() = 0; - virtual base::string16 company_short_name() = 0; - virtual base::string16 product_name() = 0; - virtual base::string16 product_short_name() = 0; - virtual base::string16 internal_name() = 0; - virtual base::string16 product_version() = 0; - virtual base::string16 private_build() = 0; - virtual base::string16 special_build() = 0; - virtual base::string16 comments() = 0; - virtual base::string16 original_filename() = 0; - virtual base::string16 file_description() = 0; - virtual base::string16 file_version() = 0; - virtual base::string16 legal_copyright() = 0; - virtual base::string16 legal_trademarks() = 0; - virtual base::string16 last_change() = 0; - virtual bool is_official_build() = 0; -}; - -#endif // BASE_FILE_VERSION_INFO_H_ diff --git a/file_version_info_mac.h b/file_version_info_mac.h deleted file mode 100644 index 9cc4b1074..000000000 --- a/file_version_info_mac.h +++ /dev/null @@ -1,51 +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_FILE_VERSION_INFO_MAC_H_ -#define BASE_FILE_VERSION_INFO_MAC_H_ - -#include -#include - -#include "base/file_version_info.h" -#include "base/mac/scoped_nsobject.h" -#include "base/macros.h" - -@class NSBundle; - -class FileVersionInfoMac : public FileVersionInfo { - public: - explicit FileVersionInfoMac(NSBundle *bundle); - ~FileVersionInfoMac() override; - - // Accessors to the different version properties. - // Returns an empty string if the property is not found. - base::string16 company_name() override; - base::string16 company_short_name() override; - base::string16 product_name() override; - base::string16 product_short_name() override; - base::string16 internal_name() override; - base::string16 product_version() override; - base::string16 private_build() override; - base::string16 special_build() override; - base::string16 comments() override; - base::string16 original_filename() override; - base::string16 file_description() override; - base::string16 file_version() override; - base::string16 legal_copyright() override; - base::string16 legal_trademarks() override; - base::string16 last_change() override; - bool is_official_build() override; - - private: - // Returns a base::string16 value for a property name. - // Returns the empty string if the property does not exist. - base::string16 GetString16Value(CFStringRef name); - - base::scoped_nsobject bundle_; - - DISALLOW_COPY_AND_ASSIGN(FileVersionInfoMac); -}; - -#endif // BASE_FILE_VERSION_INFO_MAC_H_ diff --git a/file_version_info_mac.mm b/file_version_info_mac.mm deleted file mode 100644 index ce4292441..000000000 --- a/file_version_info_mac.mm +++ /dev/null @@ -1,124 +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/file_version_info_mac.h" - -#import - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/mac/bundle_locations.h" -#include "base/mac/foundation_util.h" -#include "base/strings/sys_string_conversions.h" -#include "build/build_config.h" - -FileVersionInfoMac::FileVersionInfoMac(NSBundle *bundle) - : bundle_([bundle retain]) { -} - -FileVersionInfoMac::~FileVersionInfoMac() {} - -// static -FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForCurrentModule() { - return CreateFileVersionInfo(base::mac::FrameworkBundlePath()); -} - -// static -FileVersionInfo* FileVersionInfo::CreateFileVersionInfo( - const base::FilePath& file_path) { - NSString* path = base::SysUTF8ToNSString(file_path.value()); - NSBundle* bundle = [NSBundle bundleWithPath:path]; - return new FileVersionInfoMac(bundle); -} - -base::string16 FileVersionInfoMac::company_name() { - return base::string16(); -} - -base::string16 FileVersionInfoMac::company_short_name() { - return base::string16(); -} - -base::string16 FileVersionInfoMac::internal_name() { - return base::string16(); -} - -base::string16 FileVersionInfoMac::product_name() { - return GetString16Value(kCFBundleNameKey); -} - -base::string16 FileVersionInfoMac::product_short_name() { - return GetString16Value(kCFBundleNameKey); -} - -base::string16 FileVersionInfoMac::comments() { - return base::string16(); -} - -base::string16 FileVersionInfoMac::legal_copyright() { - return GetString16Value(CFSTR("CFBundleGetInfoString")); -} - -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, both have a policy-enfoced limit - // of three version components, so the full version is stored in a custom - // key (CrBundleVersion) falling back to CFBundleVersion if not present. -#if defined(OS_IOS) - base::string16 version(GetString16Value(CFSTR("CrBundleVersion"))); - if (version.length() > 0) - return version; - return GetString16Value(CFSTR("CFBundleVersion")); -#else - return GetString16Value(CFSTR("CFBundleShortVersionString")); -#endif // defined(OS_IOS) -} - -base::string16 FileVersionInfoMac::file_description() { - return base::string16(); -} - -base::string16 FileVersionInfoMac::legal_trademarks() { - return base::string16(); -} - -base::string16 FileVersionInfoMac::private_build() { - return base::string16(); -} - -base::string16 FileVersionInfoMac::file_version() { - return product_version(); -} - -base::string16 FileVersionInfoMac::original_filename() { - return GetString16Value(kCFBundleNameKey); -} - -base::string16 FileVersionInfoMac::special_build() { - return base::string16(); -} - -base::string16 FileVersionInfoMac::last_change() { - return GetString16Value(CFSTR("SCMRevision")); -} - -bool FileVersionInfoMac::is_official_build() { -#if defined (GOOGLE_CHROME_BUILD) - return true; -#else - return false; -#endif -} - -base::string16 FileVersionInfoMac::GetString16Value(CFStringRef name) { - if (bundle_) { - NSString *ns_name = base::mac::CFToNSCast(name); - NSString* value = [bundle_ objectForInfoDictionaryKey:ns_name]; - if (value) { - return base::SysNSStringToUTF16(value); - } - } - return base::string16(); -} diff --git a/file_version_info_win.cc b/file_version_info_win.cc deleted file mode 100644 index 4affd817a..000000000 --- a/file_version_info_win.cc +++ /dev/null @@ -1,218 +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/file_version_info_win.h" - -#include -#include - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/threading/thread_restrictions.h" -#include "base/win/resource_util.h" - -using base::FilePath; - -namespace { - -struct LanguageAndCodePage { - WORD language; - WORD code_page; -}; - -// Returns the \\VarFileInfo\\Translation value extracted from the -// VS_VERSION_INFO resource in |data|. -LanguageAndCodePage* GetTranslate(const void* data) { - LanguageAndCodePage* translate = nullptr; - UINT length; - if (::VerQueryValue(data, L"\\VarFileInfo\\Translation", - reinterpret_cast(&translate), &length)) { - return translate; - } - return nullptr; -} - -VS_FIXEDFILEINFO* GetVsFixedFileInfo(const void* data) { - VS_FIXEDFILEINFO* fixed_file_info = nullptr; - UINT length; - if (::VerQueryValue(data, L"\\", reinterpret_cast(&fixed_file_info), - &length)) { - return fixed_file_info; - } - return nullptr; -} - -} // namespace - -FileVersionInfoWin::~FileVersionInfoWin() = default; - -// static -FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForModule( - HMODULE module) { - void* data; - size_t version_info_length; - const bool has_version_resource = base::win::GetResourceFromModule( - module, VS_VERSION_INFO, RT_VERSION, &data, &version_info_length); - if (!has_version_resource) - return nullptr; - - const LanguageAndCodePage* translate = GetTranslate(data); - if (!translate) - return nullptr; - - return new FileVersionInfoWin(data, translate->language, - translate->code_page); -} - -// static -FileVersionInfo* FileVersionInfo::CreateFileVersionInfo( - const FilePath& file_path) { - base::AssertBlockingAllowed(); - - DWORD dummy; - const wchar_t* path = file_path.value().c_str(); - const DWORD length = ::GetFileVersionInfoSize(path, &dummy); - if (length == 0) - return nullptr; - - std::vector data(length, 0); - - if (!::GetFileVersionInfo(path, dummy, length, data.data())) - return nullptr; - - const LanguageAndCodePage* translate = GetTranslate(data.data()); - if (!translate) - return nullptr; - - return new FileVersionInfoWin(std::move(data), translate->language, - translate->code_page); -} - -base::string16 FileVersionInfoWin::company_name() { - return GetStringValue(L"CompanyName"); -} - -base::string16 FileVersionInfoWin::company_short_name() { - return GetStringValue(L"CompanyShortName"); -} - -base::string16 FileVersionInfoWin::internal_name() { - return GetStringValue(L"InternalName"); -} - -base::string16 FileVersionInfoWin::product_name() { - return GetStringValue(L"ProductName"); -} - -base::string16 FileVersionInfoWin::product_short_name() { - return GetStringValue(L"ProductShortName"); -} - -base::string16 FileVersionInfoWin::comments() { - return GetStringValue(L"Comments"); -} - -base::string16 FileVersionInfoWin::legal_copyright() { - return GetStringValue(L"LegalCopyright"); -} - -base::string16 FileVersionInfoWin::product_version() { - return GetStringValue(L"ProductVersion"); -} - -base::string16 FileVersionInfoWin::file_description() { - return GetStringValue(L"FileDescription"); -} - -base::string16 FileVersionInfoWin::legal_trademarks() { - return GetStringValue(L"LegalTrademarks"); -} - -base::string16 FileVersionInfoWin::private_build() { - return GetStringValue(L"PrivateBuild"); -} - -base::string16 FileVersionInfoWin::file_version() { - return GetStringValue(L"FileVersion"); -} - -base::string16 FileVersionInfoWin::original_filename() { - return GetStringValue(L"OriginalFilename"); -} - -base::string16 FileVersionInfoWin::special_build() { - return GetStringValue(L"SpecialBuild"); -} - -base::string16 FileVersionInfoWin::last_change() { - return GetStringValue(L"LastChange"); -} - -bool FileVersionInfoWin::is_official_build() { - return (GetStringValue(L"Official Build").compare(L"1") == 0); -} - -bool FileVersionInfoWin::GetValue(const wchar_t* name, - std::wstring* value_str) { - WORD lang_codepage[8]; - size_t i = 0; - // Use the language and codepage from the DLL. - lang_codepage[i++] = language_; - lang_codepage[i++] = code_page_; - // Use the default language and codepage from the DLL. - lang_codepage[i++] = ::GetUserDefaultLangID(); - lang_codepage[i++] = code_page_; - // Use the language from the DLL and Latin codepage (most common). - lang_codepage[i++] = language_; - lang_codepage[i++] = 1252; - // Use the default language and Latin codepage (most common). - lang_codepage[i++] = ::GetUserDefaultLangID(); - lang_codepage[i++] = 1252; - - i = 0; - while (i < arraysize(lang_codepage)) { - wchar_t sub_block[MAX_PATH]; - WORD language = lang_codepage[i++]; - WORD code_page = lang_codepage[i++]; - _snwprintf_s(sub_block, MAX_PATH, MAX_PATH, - L"\\StringFileInfo\\%04x%04x\\%ls", language, code_page, name); - LPVOID value = NULL; - uint32_t size; - BOOL r = ::VerQueryValue(data_, sub_block, &value, &size); - if (r && value) { - value_str->assign(static_cast(value)); - return true; - } - } - return false; -} - -std::wstring FileVersionInfoWin::GetStringValue(const wchar_t* name) { - std::wstring str; - if (GetValue(name, &str)) - return str; - else - return L""; -} - -FileVersionInfoWin::FileVersionInfoWin(std::vector&& data, - WORD language, - WORD code_page) - : owned_data_(std::move(data)), - data_(owned_data_.data()), - language_(language), - code_page_(code_page), - fixed_file_info_(GetVsFixedFileInfo(data_)) { - DCHECK(!owned_data_.empty()); -} - -FileVersionInfoWin::FileVersionInfoWin(void* data, - WORD language, - WORD code_page) - : data_(data), - language_(language), - code_page_(code_page), - fixed_file_info_(GetVsFixedFileInfo(data)) { - DCHECK(data_); -} diff --git a/file_version_info_win.h b/file_version_info_win.h deleted file mode 100644 index d91b67f67..000000000 --- a/file_version_info_win.h +++ /dev/null @@ -1,77 +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_FILE_VERSION_INFO_WIN_H_ -#define BASE_FILE_VERSION_INFO_WIN_H_ - -#include - -#include - -#include -#include -#include - -#include "base/base_export.h" -#include "base/file_version_info.h" -#include "base/macros.h" - -struct tagVS_FIXEDFILEINFO; -typedef tagVS_FIXEDFILEINFO VS_FIXEDFILEINFO; - -class BASE_EXPORT FileVersionInfoWin : public FileVersionInfo { - public: - ~FileVersionInfoWin() override; - - // Accessors to the different version properties. - // Returns an empty string if the property is not found. - base::string16 company_name() override; - base::string16 company_short_name() override; - base::string16 product_name() override; - base::string16 product_short_name() override; - base::string16 internal_name() override; - base::string16 product_version() override; - base::string16 private_build() override; - base::string16 special_build() override; - base::string16 comments() override; - base::string16 original_filename() override; - base::string16 file_description() override; - base::string16 file_version() override; - base::string16 legal_copyright() override; - base::string16 legal_trademarks() override; - base::string16 last_change() override; - bool is_official_build() override; - - // Lets you access other properties not covered above. - bool GetValue(const wchar_t* name, std::wstring* value); - - // Similar to GetValue but returns a wstring (empty string if the property - // does not exist). - std::wstring GetStringValue(const wchar_t* name); - - // Get the fixed file info if it exists. Otherwise NULL - const VS_FIXEDFILEINFO* fixed_file_info() const { return fixed_file_info_; } - - private: - friend FileVersionInfo; - - // |data| is a VS_VERSION_INFO resource. |language| and |code_page| are - // extracted from the \VarFileInfo\Translation value of |data|. - FileVersionInfoWin(std::vector&& data, - WORD language, - WORD code_page); - FileVersionInfoWin(void* data, WORD language, WORD code_page); - - const std::vector owned_data_; - const void* const data_; - const WORD language_; - const WORD code_page_; - - // This is a pointer into |data_| if it exists. Otherwise nullptr. - const VS_FIXEDFILEINFO* const fixed_file_info_; - - DISALLOW_COPY_AND_ASSIGN(FileVersionInfoWin); -}; - -#endif // BASE_FILE_VERSION_INFO_WIN_H_ diff --git a/file_version_info_win_unittest.cc b/file_version_info_win_unittest.cc deleted file mode 100644 index a4acc4ca3..000000000 --- a/file_version_info_win_unittest.cc +++ /dev/null @@ -1,175 +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/file_version_info_win.h" - -#include - -#include - -#include - -#include "base/file_version_info.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/path_service.h" -#include "base/scoped_native_library.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::FilePath; - -namespace { - -FilePath GetTestDataPath() { - FilePath path; - base::PathService::Get(base::DIR_SOURCE_ROOT, &path); - path = path.AppendASCII("base"); - path = path.AppendASCII("test"); - path = path.AppendASCII("data"); - path = path.AppendASCII("file_version_info_unittest"); - return path; -} - -class FileVersionInfoFactory { - public: - explicit FileVersionInfoFactory(const FilePath& path) : path_(path) {} - - std::unique_ptr Create() const { - return base::WrapUnique(FileVersionInfo::CreateFileVersionInfo(path_)); - } - - private: - const FilePath path_; - - DISALLOW_COPY_AND_ASSIGN(FileVersionInfoFactory); -}; - -class FileVersionInfoForModuleFactory { - public: - explicit FileVersionInfoForModuleFactory(const FilePath& path) - // Load the library with LOAD_LIBRARY_AS_IMAGE_RESOURCE since it shouldn't - // be executed. - : library_(::LoadLibraryEx(path.value().c_str(), - nullptr, - LOAD_LIBRARY_AS_IMAGE_RESOURCE)) { - EXPECT_TRUE(library_.is_valid()); - } - - std::unique_ptr Create() const { - return base::WrapUnique( - FileVersionInfo::CreateFileVersionInfoForModule(library_.get())); - } - - private: - const base::ScopedNativeLibrary library_; - - DISALLOW_COPY_AND_ASSIGN(FileVersionInfoForModuleFactory); -}; - -template -class FileVersionInfoTest : public testing::Test {}; - -using FileVersionInfoFactories = - ::testing::Types; - -} // namespace - -TYPED_TEST_CASE(FileVersionInfoTest, FileVersionInfoFactories); - -TYPED_TEST(FileVersionInfoTest, HardCodedProperties) { - const wchar_t kDLLName[] = {L"FileVersionInfoTest1.dll"}; - - const wchar_t* const kExpectedValues[15] = { - // FileVersionInfoTest.dll - L"Goooooogle", // company_name - L"Google", // company_short_name - L"This is the product name", // product_name - L"This is the product short name", // product_short_name - L"The Internal Name", // internal_name - L"4.3.2.1", // product_version - L"Private build property", // private_build - L"Special build property", // special_build - L"This is a particularly interesting comment", // comments - L"This is the original filename", // original_filename - L"This is my file description", // file_description - L"1.2.3.4", // file_version - L"This is the legal copyright", // legal_copyright - L"This is the legal trademarks", // legal_trademarks - L"This is the last change", // last_change - }; - - FilePath dll_path = GetTestDataPath(); - dll_path = dll_path.Append(kDLLName); - - TypeParam factory(dll_path); - std::unique_ptr version_info(factory.Create()); - ASSERT_TRUE(version_info); - - int j = 0; - EXPECT_EQ(kExpectedValues[j++], version_info->company_name()); - EXPECT_EQ(kExpectedValues[j++], version_info->company_short_name()); - EXPECT_EQ(kExpectedValues[j++], version_info->product_name()); - EXPECT_EQ(kExpectedValues[j++], version_info->product_short_name()); - EXPECT_EQ(kExpectedValues[j++], version_info->internal_name()); - EXPECT_EQ(kExpectedValues[j++], version_info->product_version()); - EXPECT_EQ(kExpectedValues[j++], version_info->private_build()); - EXPECT_EQ(kExpectedValues[j++], version_info->special_build()); - EXPECT_EQ(kExpectedValues[j++], version_info->comments()); - EXPECT_EQ(kExpectedValues[j++], version_info->original_filename()); - EXPECT_EQ(kExpectedValues[j++], version_info->file_description()); - EXPECT_EQ(kExpectedValues[j++], version_info->file_version()); - EXPECT_EQ(kExpectedValues[j++], version_info->legal_copyright()); - EXPECT_EQ(kExpectedValues[j++], version_info->legal_trademarks()); - EXPECT_EQ(kExpectedValues[j++], version_info->last_change()); -} - -TYPED_TEST(FileVersionInfoTest, IsOfficialBuild) { - constexpr struct { - const wchar_t* const dll_name; - const bool is_official_build; - } kTestItems[]{ - {L"FileVersionInfoTest1.dll", true}, {L"FileVersionInfoTest2.dll", false}, - }; - - for (const auto& test_item : kTestItems) { - const FilePath dll_path = GetTestDataPath().Append(test_item.dll_name); - - TypeParam factory(dll_path); - std::unique_ptr version_info(factory.Create()); - ASSERT_TRUE(version_info); - - EXPECT_EQ(test_item.is_official_build, version_info->is_official_build()); - } -} - -TYPED_TEST(FileVersionInfoTest, CustomProperties) { - FilePath dll_path = GetTestDataPath(); - dll_path = dll_path.AppendASCII("FileVersionInfoTest1.dll"); - - TypeParam factory(dll_path); - std::unique_ptr version_info(factory.Create()); - ASSERT_TRUE(version_info); - - // Test few existing properties. - std::wstring str; - FileVersionInfoWin* version_info_win = - static_cast(version_info.get()); - EXPECT_TRUE(version_info_win->GetValue(L"Custom prop 1", &str)); - EXPECT_EQ(L"Un", str); - EXPECT_EQ(L"Un", version_info_win->GetStringValue(L"Custom prop 1")); - - EXPECT_TRUE(version_info_win->GetValue(L"Custom prop 2", &str)); - EXPECT_EQ(L"Deux", str); - EXPECT_EQ(L"Deux", version_info_win->GetStringValue(L"Custom prop 2")); - - EXPECT_TRUE(version_info_win->GetValue(L"Custom prop 3", &str)); - EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043", str); - EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043", - version_info_win->GetStringValue(L"Custom prop 3")); - - // Test an non-existing property. - EXPECT_FALSE(version_info_win->GetValue(L"Unknown property", &str)); - EXPECT_EQ(L"", version_info_win->GetStringValue(L"Unknown property")); -} diff --git a/files/dir_reader_fallback.h b/files/dir_reader_fallback.h deleted file mode 100644 index d44c2279e..000000000 --- a/files/dir_reader_fallback.h +++ /dev/null @@ -1,35 +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_FILES_DIR_READER_FALLBACK_H_ -#define BASE_FILES_DIR_READER_FALLBACK_H_ - -namespace base { - -class DirReaderFallback { - public: - // Open a directory. If |IsValid| is true, then |Next| can be called to start - // the iteration at the beginning of the directory. - explicit DirReaderFallback(const char* directory_path) {} - - // After construction, IsValid returns true iff the directory was - // successfully opened. - bool IsValid() const { return false; } - - // Move to the next entry returning false if the iteration is complete. - bool Next() { return false; } - - // Return the name of the current directory entry. - const char* name() { return nullptr;} - - // Return the file descriptor which is being used. - int fd() const { return -1; } - - // Returns true if this is a no-op fallback class (for testing). - static bool IsFallback() { return true; } -}; - -} // namespace base - -#endif // BASE_FILES_DIR_READER_FALLBACK_H_ diff --git a/files/dir_reader_linux.h b/files/dir_reader_linux.h deleted file mode 100644 index 259bcfede..000000000 --- a/files/dir_reader_linux.h +++ /dev/null @@ -1,101 +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_FILES_DIR_READER_LINUX_H_ -#define BASE_FILES_DIR_READER_LINUX_H_ - -#include -#include -#include -#include -#include -#include - -#include "base/logging.h" -#include "base/macros.h" -#include "base/posix/eintr_wrapper.h" - -// See the comments in dir_reader_posix.h about this. - -namespace base { - -struct linux_dirent { - uint64_t d_ino; - int64_t d_off; - unsigned short d_reclen; - unsigned char d_type; - char d_name[0]; -}; - -class DirReaderLinux { - public: - explicit DirReaderLinux(const char* directory_path) - : fd_(open(directory_path, O_RDONLY | O_DIRECTORY)), - offset_(0), - size_(0) { - memset(buf_, 0, sizeof(buf_)); - } - - ~DirReaderLinux() { - if (fd_ >= 0) { - if (IGNORE_EINTR(close(fd_))) - RAW_LOG(ERROR, "Failed to close directory handle"); - } - } - - bool IsValid() const { - return fd_ >= 0; - } - - // Move to the next entry returning false if the iteration is complete. - bool Next() { - if (size_) { - linux_dirent* dirent = reinterpret_cast(&buf_[offset_]); - offset_ += dirent->d_reclen; - } - - if (offset_ != size_) - return true; - - const int r = syscall(__NR_getdents64, fd_, buf_, sizeof(buf_)); - if (r == 0) - return false; - if (r == -1) { - DPLOG(FATAL) << "getdents64 returned an error: " << errno; - return false; - } - size_ = r; - offset_ = 0; - return true; - } - - const char* name() const { - if (!size_) - return nullptr; - - const linux_dirent* dirent = - reinterpret_cast(&buf_[offset_]); - return dirent->d_name; - } - - int fd() const { - return fd_; - } - - static bool IsFallback() { - return false; - } - - private: - const int fd_; - alignas(linux_dirent) unsigned char buf_[512]; - size_t offset_; - size_t size_; - - DISALLOW_COPY_AND_ASSIGN(DirReaderLinux); -}; - -} // namespace base - -#endif // BASE_FILES_DIR_READER_LINUX_H_ diff --git a/files/dir_reader_posix.h b/files/dir_reader_posix.h deleted file mode 100644 index 15fc744f6..000000000 --- a/files/dir_reader_posix.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FILES_DIR_READER_POSIX_H_ -#define BASE_FILES_DIR_READER_POSIX_H_ - -#include "build/build_config.h" - -// This header provides a class, DirReaderPosix, which allows one to open and -// read from directories without allocating memory. For the interface, see -// the generic fallback in dir_reader_fallback.h. - -// Mac note: OS X has getdirentries, but it only works if we restrict Chrome to -// 32-bit inodes. There is a getdirentries64 syscall in 10.6, but it's not -// wrapped and the direct syscall interface is unstable. Using an unstable API -// seems worse than falling back to enumerating all file descriptors so we will -// probably never implement this on the Mac. - -#if defined(OS_LINUX) || defined(OS_ANDROID) -#include "base/files/dir_reader_linux.h" -#else -#include "base/files/dir_reader_fallback.h" -#endif - -namespace base { - -#if defined(OS_LINUX) || defined(OS_ANDROID) -typedef DirReaderLinux DirReaderPosix; -#else -typedef DirReaderFallback DirReaderPosix; -#endif - -} // namespace base - -#endif // BASE_FILES_DIR_READER_POSIX_H_ diff --git a/files/dir_reader_posix_unittest.cc b/files/dir_reader_posix_unittest.cc deleted file mode 100644 index 1954cb2f0..000000000 --- a/files/dir_reader_posix_unittest.cc +++ /dev/null @@ -1,95 +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/files/dir_reader_posix.h" - -#include -#include -#include -#include -#include -#include - -#include "base/files/scoped_temp_dir.h" -#include "base/logging.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_ANDROID) -#include "base/os_compat_android.h" -#endif - -namespace base { - -TEST(DirReaderPosixUnittest, Read) { - static const unsigned kNumFiles = 100; - - if (DirReaderPosix::IsFallback()) - return; - - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - const char* dir = temp_dir.GetPath().value().c_str(); - ASSERT_TRUE(dir); - - char wdbuf[PATH_MAX]; - PCHECK(getcwd(wdbuf, PATH_MAX)); - - PCHECK(chdir(dir) == 0); - - for (unsigned i = 0; i < kNumFiles; i++) { - char buf[16]; - snprintf(buf, sizeof(buf), "%d", i); - const int fd = open(buf, O_CREAT | O_RDONLY | O_EXCL, 0600); - PCHECK(fd >= 0); - PCHECK(close(fd) == 0); - } - - std::set seen; - - DirReaderPosix reader(dir); - EXPECT_TRUE(reader.IsValid()); - - if (!reader.IsValid()) - return; - - bool seen_dot = false, seen_dotdot = false; - - for (; reader.Next(); ) { - if (strcmp(reader.name(), ".") == 0) { - seen_dot = true; - continue; - } - if (strcmp(reader.name(), "..") == 0) { - seen_dotdot = true; - continue; - } - - SCOPED_TRACE(testing::Message() << "reader.name(): " << reader.name()); - - char *endptr; - const unsigned long value = strtoul(reader.name(), &endptr, 10); - - EXPECT_FALSE(*endptr); - EXPECT_LT(value, kNumFiles); - EXPECT_EQ(0u, seen.count(value)); - seen.insert(value); - } - - for (unsigned i = 0; i < kNumFiles; i++) { - char buf[16]; - snprintf(buf, sizeof(buf), "%d", i); - PCHECK(unlink(buf) == 0); - } - - PCHECK(rmdir(dir) == 0); - - PCHECK(chdir(wdbuf) == 0); - - EXPECT_TRUE(seen_dot); - EXPECT_TRUE(seen_dotdot); - EXPECT_EQ(kNumFiles, seen.size()); -} - -} // namespace base diff --git a/files/file.cc b/files/file.cc deleted file mode 100644 index e8934b1cd..000000000 --- a/files/file.cc +++ /dev/null @@ -1,143 +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/files/file.h" -#include "base/files/file_path.h" -#include "base/files/file_tracing.h" -#include "base/metrics/histogram.h" -#include "base/timer/elapsed_timer.h" -#include "build/build_config.h" - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#endif - -namespace base { - -File::Info::Info() - : size(0), - is_directory(false), - is_symbolic_link(false) { -} - -File::Info::~Info() = default; - -File::File() - : error_details_(FILE_ERROR_FAILED), - created_(false), - async_(false) { -} - -#if !defined(OS_NACL) -File::File(const FilePath& path, uint32_t flags) - : error_details_(FILE_OK), created_(false), async_(false) { - Initialize(path, flags); -} -#endif - -File::File(PlatformFile platform_file) : File(platform_file, false) {} - -File::File(PlatformFile platform_file, bool async) - : file_(platform_file), - error_details_(FILE_OK), - created_(false), - async_(async) { -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - DCHECK_GE(platform_file, -1); -#endif -} - -File::File(Error error_details) - : error_details_(error_details), - created_(false), - async_(false) { -} - -File::File(File&& other) - : file_(other.TakePlatformFile()), - tracing_path_(other.tracing_path_), - error_details_(other.error_details()), - created_(other.created()), - async_(other.async_) {} - -File::~File() { - // Go through the AssertIOAllowed logic. - Close(); -} - -File& File::operator=(File&& other) { - Close(); - SetPlatformFile(other.TakePlatformFile()); - tracing_path_ = other.tracing_path_; - error_details_ = other.error_details(); - created_ = other.created(); - async_ = other.async_; - return *this; -} - -#if !defined(OS_NACL) -void File::Initialize(const FilePath& path, uint32_t flags) { - if (path.ReferencesParent()) { -#if defined(OS_WIN) - ::SetLastError(ERROR_ACCESS_DENIED); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - errno = EACCES; -#else -#error Unsupported platform -#endif - error_details_ = FILE_ERROR_ACCESS_DENIED; - return; - } - if (FileTracing::IsCategoryEnabled()) - tracing_path_ = path; - SCOPED_FILE_TRACE("Initialize"); - DoInitialize(path, 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/files/file.h b/files/file.h deleted file mode 100644 index 907757c7f..000000000 --- a/files/file.h +++ /dev/null @@ -1,382 +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_FILES_FILE_H_ -#define BASE_FILES_FILE_H_ - -#include - -#include - -#include "base/base_export.h" -#include "base/files/file_path.h" -#include "base/files/file_tracing.h" -#include "base/files/platform_file.h" -#include "base/files/scoped_file.h" -#include "base/macros.h" -#include "base/time/time.h" -#include "build/build_config.h" - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#endif - -namespace base { - -#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \ - defined(OS_ANDROID) && __ANDROID_API__ < 21 -typedef struct stat stat_wrapper_t; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -typedef struct stat64 stat_wrapper_t; -#endif - -// Thin wrapper around an OS-level file. -// Note that this class does not provide any support for asynchronous IO, other -// than the ability to create asynchronous handles on Windows. -// -// Note about const: this class does not attempt to determine if the underlying -// file system object is affected by a particular method in order to consider -// that method const or not. Only methods that deal with member variables in an -// obvious non-modifying way are marked as const. Any method that forward calls -// to the OS is not considered const, even if there is no apparent change to -// member variables. -class BASE_EXPORT File { - public: - // FLAG_(OPEN|CREATE).* are mutually exclusive. You should specify exactly one - // of the five (possibly combining with other flags) when opening or creating - // a file. - // FLAG_(WRITE|APPEND) are mutually exclusive. This is so that APPEND behavior - // will be consistent with O_APPEND on POSIX. - // FLAG_EXCLUSIVE_(READ|WRITE) only grant exclusive access to the file on - // creation on POSIX; for existing files, consider using Lock(). - enum Flags { - FLAG_OPEN = 1 << 0, // Opens a file, only if it exists. - FLAG_CREATE = 1 << 1, // Creates a new file, only if it does not - // already exist. - FLAG_OPEN_ALWAYS = 1 << 2, // May create a new file. - FLAG_CREATE_ALWAYS = 1 << 3, // May overwrite an old file. - FLAG_OPEN_TRUNCATED = 1 << 4, // Opens a file and truncates it, only if it - // exists. - FLAG_READ = 1 << 5, - FLAG_WRITE = 1 << 6, - FLAG_APPEND = 1 << 7, - FLAG_EXCLUSIVE_READ = 1 << 8, // EXCLUSIVE is opposite of Windows SHARE. - FLAG_EXCLUSIVE_WRITE = 1 << 9, - FLAG_ASYNC = 1 << 10, - FLAG_TEMPORARY = 1 << 11, // Used on Windows only. - FLAG_HIDDEN = 1 << 12, // Used on Windows only. - FLAG_DELETE_ON_CLOSE = 1 << 13, - FLAG_WRITE_ATTRIBUTES = 1 << 14, // Used on Windows only. - FLAG_SHARE_DELETE = 1 << 15, // Used on Windows only. - FLAG_TERMINAL_DEVICE = 1 << 16, // Serial port flags. - FLAG_BACKUP_SEMANTICS = 1 << 17, // Used on Windows only. - FLAG_EXECUTE = 1 << 18, // Used on Windows only. - FLAG_SEQUENTIAL_SCAN = 1 << 19, // Used on Windows only. - FLAG_CAN_DELETE_ON_CLOSE = 1 << 20, // Requests permission to delete a file - // via DeleteOnClose() (Windows only). - // See DeleteOnClose() for details. - }; - - // This enum has been recorded in multiple histograms using PlatformFileError - // enum. If the order of the fields needs to change, please ensure that those - // histograms are obsolete or have been moved to a different enum. - // - // FILE_ERROR_ACCESS_DENIED is returned when a call fails because of a - // filesystem restriction. FILE_ERROR_SECURITY is returned when a browser - // policy doesn't allow the operation to be executed. - enum Error { - FILE_OK = 0, - FILE_ERROR_FAILED = -1, - FILE_ERROR_IN_USE = -2, - FILE_ERROR_EXISTS = -3, - FILE_ERROR_NOT_FOUND = -4, - FILE_ERROR_ACCESS_DENIED = -5, - FILE_ERROR_TOO_MANY_OPENED = -6, - FILE_ERROR_NO_MEMORY = -7, - FILE_ERROR_NO_SPACE = -8, - FILE_ERROR_NOT_A_DIRECTORY = -9, - FILE_ERROR_INVALID_OPERATION = -10, - FILE_ERROR_SECURITY = -11, - FILE_ERROR_ABORT = -12, - FILE_ERROR_NOT_A_FILE = -13, - FILE_ERROR_NOT_EMPTY = -14, - FILE_ERROR_INVALID_URL = -15, - FILE_ERROR_IO = -16, - // Put new entries here and increment FILE_ERROR_MAX. - FILE_ERROR_MAX = -17 - }; - - // This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux. - enum Whence { - FROM_BEGIN = 0, - FROM_CURRENT = 1, - FROM_END = 2 - }; - - // Used to hold information about a given file. - // If you add more fields to this structure (platform-specific fields are OK), - // make sure to update all functions that use it in file_util_{win|posix}.cc, - // too, and the ParamTraits implementation in - // ipc/ipc_message_utils.cc. - struct BASE_EXPORT Info { - Info(); - ~Info(); -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - // 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_t size; - - // True if the file corresponds to a directory. - bool is_directory; - - // True if the file corresponds to a symbolic link. For Windows currently - // not supported and thus always false. - bool is_symbolic_link; - - // The last modified time of a file. - Time last_modified; - - // The last accessed time of a file. - Time last_accessed; - - // The creation time of a file. - Time creation_time; - }; - - File(); - - // Creates or opens the given file. This will fail with 'access denied' if the - // |path| contains path traversal ('..') components. - File(const FilePath& path, uint32_t flags); - - // Takes ownership of |platform_file| and sets async to false. - explicit File(PlatformFile platform_file); - - // Takes ownership of |platform_file| and sets async to the given value. - // This constructor exists because on Windows you can't check if platform_file - // is async or not. - File(PlatformFile platform_file, bool async); - - // Creates an object with a specific error_details code. - explicit File(Error error_details); - - File(File&& other); - - ~File(); - - File& operator=(File&& other); - - // Creates or opens the given file. - void Initialize(const FilePath& path, uint32_t flags); - - // Returns |true| if the handle / fd wrapped by this object is valid. This - // method doesn't interact with the file system (and is safe to be called from - // ThreadRestrictions::SetIOAllowed(false) threads). - bool IsValid() const; - - // Returns true if a new file was created (or an old one truncated to zero - // length to simulate a new file, which can happen with - // FLAG_CREATE_ALWAYS), and false otherwise. - bool created() const { return created_; } - - // 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(path, flags); - // if (!file.IsValid()) - // return; - Error error_details() const { return error_details_; } - - PlatformFile GetPlatformFile() const; - PlatformFile TakePlatformFile(); - - // Destroying this object closes the file automatically. - void Close(); - - // Changes current position in the file to an |offset| relative to an origin - // defined by |whence|. Returns the resultant current position in the file - // (relative to the start) or -1 in case of error. - int64_t Seek(Whence whence, int64_t offset); - - // Reads the given number of bytes (or until EOF is reached) starting with the - // given offset. Returns the number of bytes read, or -1 on error. Note that - // this function makes a best effort to read all data on all platforms, so it - // is not intended for stream oriented files but instead for cases when the - // normal expectation is that actually |size| bytes are read unless there is - // an error. - int Read(int64_t offset, char* data, int size); - - // Same as above but without seek. - int ReadAtCurrentPos(char* data, int size); - - // Reads the given number of bytes (or until EOF is reached) starting with the - // given offset, but does not make any effort to read all data on all - // platforms. Returns the number of bytes read, or -1 on error. - int ReadNoBestEffort(int64_t offset, char* data, int size); - - // Same as above but without seek. - int ReadAtCurrentPosNoBestEffort(char* data, int size); - - // Writes the given buffer into the file at the given offset, overwritting any - // data that was previously there. Returns the number of bytes written, or -1 - // on error. Note that this function makes a best effort to write all data on - // all platforms. |data| can be nullptr when |size| is 0. - // Ignores the offset and writes to the end of the file if the file was opened - // with FLAG_APPEND. - int Write(int64_t offset, const char* data, int size); - - // Save as above but without seek. - int WriteAtCurrentPos(const char* data, int size); - - // Save as above but does not make any effort to write all data on all - // 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_t 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 SetLength(int64_t length); - - // Instructs the filesystem to flush the file to disk. (POSIX: fsync, Windows: - // FlushFileBuffers). - // Calling Flush() does not guarantee file integrity and thus is not a valid - // substitute for file integrity checks and recovery codepaths for malformed - // files. It can also be *really* slow, so avoid blocking on Flush(), - // especially please don't block shutdown on Flush(). - // Latency percentiles of Flush() across all platforms as of July 2016: - // 50 % > 5 ms - // 10 % > 58 ms - // 1 % > 357 ms - // 0.1 % > 1.8 seconds - // 0.01 % > 7.6 seconds - bool Flush(); - - // Updates the file times. - bool SetTimes(Time last_access_time, Time last_modified_time); - - // Returns some basic information for the given file. - bool GetInfo(Info* info); - -#if !defined(OS_FUCHSIA) // Fuchsia's POSIX API does not support file locking. - - // Attempts to take an exclusive write lock on the file. Returns immediately - // (i.e. does not wait for another process to unlock the file). If the lock - // was obtained, the result will be FILE_OK. A lock only guarantees - // that other processes may not also take a lock on the same file with the - // same API - it may still be opened, renamed, unlinked, etc. - // - // Common semantics: - // * Locks are held by processes, but not inherited by child processes. - // * Locks are released by the OS on file close or process termination. - // * Locks are reliable only on local filesystems. - // * Duplicated file handles may also write to locked files. - // Windows-specific semantics: - // * Locks are mandatory for read/write APIs, advisory for mapping APIs. - // * Within a process, locking the same file (by the same or new handle) - // will fail. - // POSIX-specific semantics: - // * Locks are advisory only. - // * Within a process, locking the same file (by the same or new handle) - // will succeed. - // * Closing any descriptor on a given file releases the lock. - Error Lock(); - - // Unlock a file previously locked. - Error Unlock(); - -#endif // !defined(OS_FUCHSIA) - - // Returns a new object referencing this file for use within the current - // process. Handling of FLAG_DELETE_ON_CLOSE varies by OS. On POSIX, the File - // object that was created or initialized with this flag will have unlinked - // the underlying file when it was created or opened. On Windows, the - // underlying file is deleted when the last handle to it is closed. - File Duplicate() const; - - bool async() const { return async_; } - -#if defined(OS_WIN) - // Sets or clears the DeleteFile disposition on the file. Returns true if - // the disposition was set or cleared, as indicated by |delete_on_close|. - // - // Microsoft Windows deletes a file only when the DeleteFile disposition is - // set on a file when the last handle to the last underlying kernel File - // object is closed. This disposition is be set by: - // - Calling the Win32 DeleteFile function with the path to a file. - // - Opening/creating a file with FLAG_DELETE_ON_CLOSE and then closing all - // handles to that File object. - // - Opening/creating a file with FLAG_CAN_DELETE_ON_CLOSE and subsequently - // calling DeleteOnClose(true). - // - // In all cases, all pre-existing handles to the file must have been opened - // with FLAG_SHARE_DELETE. Once the disposition has been set by any of the - // above means, no new File objects can be created for the file. - // - // So: - // - Use FLAG_SHARE_DELETE when creating/opening a file to allow another - // entity on the system to cause it to be deleted when it is closed. (Note: - // another entity can delete the file the moment after it is closed, so not - // using this permission doesn't provide any protections.) - // - Use FLAG_DELETE_ON_CLOSE for any file that is to be deleted after use. - // The OS will ensure it is deleted even in the face of process termination. - // Note that it's possible for deletion to be cancelled via another File - // object referencing the same file using DeleteOnClose(false) to clear the - // DeleteFile disposition after the original File is closed. - // - Use FLAG_CAN_DELETE_ON_CLOSE in conjunction with DeleteOnClose() to alter - // the DeleteFile disposition on an open handle. This fine-grained control - // allows for marking a file for deletion during processing so that it is - // deleted in the event of untimely process termination, and then clearing - // this state once the file is suitable for persistence. - bool DeleteOnClose(bool delete_on_close); -#endif - -#if defined(OS_WIN) - static Error OSErrorToFileError(DWORD last_error); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - static Error OSErrorToFileError(int saved_errno); -#endif - - // Gets the last global error (errno or GetLastError()) and converts it to the - // closest base::File::Error equivalent via OSErrorToFileError(). The returned - // value is only trustworthy immediately after another base::File method - // fails. base::File never resets the global error to zero. - static Error GetLastFileError(); - - // Converts an error value to a human-readable form. Used for logging. - static std::string ErrorToString(Error error); - - private: - friend class FileTracing::ScopedTrace; - - // Creates or opens the given file. Only called if |path| has no - // traversal ('..') components. - void DoInitialize(const FilePath& path, uint32_t flags); - - void SetPlatformFile(PlatformFile file); - - ScopedPlatformFile file_; - - // A path to use for tracing purposes. Set if file tracing is enabled during - // |Initialize()|. - FilePath tracing_path_; - - // Object tied to the lifetime of |this| that enables/disables tracing. - FileTracing::ScopedEnabler trace_enabler_; - - Error error_details_; - bool created_; - bool async_; - - DISALLOW_COPY_AND_ASSIGN(File); -}; - -} // namespace base - -#endif // BASE_FILES_FILE_H_ - diff --git a/files/file_descriptor_watcher_posix.cc b/files/file_descriptor_watcher_posix.cc deleted file mode 100644 index b26bf6c5d..000000000 --- a/files/file_descriptor_watcher_posix.cc +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2016 The Chromium 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_descriptor_watcher_posix.h" - -#include "base/bind.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop_current.h" -#include "base/message_loop/message_pump_for_io.h" -#include "base/sequenced_task_runner.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "base/threading/thread_checker.h" -#include "base/threading/thread_local.h" - -namespace base { - -namespace { - -// MessageLoopForIO used to watch file descriptors for which callbacks are -// registered from a given thread. -LazyInstance>::Leaky - tls_message_loop_for_io = LAZY_INSTANCE_INITIALIZER; - -} // namespace - -FileDescriptorWatcher::Controller::~Controller() { - DCHECK(sequence_checker_.CalledOnValidSequence()); - - // Delete |watcher_| on the MessageLoopForIO. - // - // If the MessageLoopForIO is deleted before Watcher::StartWatching() runs, - // |watcher_| is leaked. If the MessageLoopForIO is deleted after - // Watcher::StartWatching() runs but before the DeleteSoon task runs, - // |watcher_| is deleted from Watcher::WillDestroyCurrentMessageLoop(). - message_loop_for_io_task_runner_->DeleteSoon(FROM_HERE, watcher_.release()); - - // Since WeakPtrs are invalidated by the destructor, RunCallback() won't be - // invoked after this returns. -} - -class FileDescriptorWatcher::Controller::Watcher - : public MessagePumpForIO::FdWatcher, - public MessageLoopCurrent::DestructionObserver { - public: - Watcher(WeakPtr controller, MessagePumpForIO::Mode mode, int fd); - ~Watcher() override; - - void StartWatching(); - - private: - friend class FileDescriptorWatcher; - - // MessagePumpForIO::FdWatcher: - void OnFileCanReadWithoutBlocking(int fd) override; - void OnFileCanWriteWithoutBlocking(int fd) override; - - // MessageLoopCurrent::DestructionObserver: - void WillDestroyCurrentMessageLoop() override; - - // The MessageLoopForIO's watch handle (stops the watch when destroyed). - MessagePumpForIO::FdWatchController fd_watch_controller_; - - // Runs tasks on the sequence on which this was instantiated (i.e. the - // sequence on which the callback must run). - const scoped_refptr callback_task_runner_ = - SequencedTaskRunnerHandle::Get(); - - // The Controller that created this Watcher. - WeakPtr controller_; - - // Whether this Watcher is notified when |fd_| becomes readable or writable - // without blocking. - const MessagePumpForIO::Mode mode_; - - // The watched file descriptor. - const int fd_; - - // Except for the constructor, every method of this class must run on the same - // MessageLoopForIO thread. - ThreadChecker thread_checker_; - - // Whether this Watcher was registered as a DestructionObserver on the - // MessageLoopForIO thread. - bool registered_as_destruction_observer_ = false; - - DISALLOW_COPY_AND_ASSIGN(Watcher); -}; - -FileDescriptorWatcher::Controller::Watcher::Watcher( - WeakPtr controller, - MessagePumpForIO::Mode mode, - int fd) - : fd_watch_controller_(FROM_HERE), - controller_(controller), - mode_(mode), - fd_(fd) { - DCHECK(callback_task_runner_); - thread_checker_.DetachFromThread(); -} - -FileDescriptorWatcher::Controller::Watcher::~Watcher() { - DCHECK(thread_checker_.CalledOnValidThread()); - MessageLoopCurrentForIO::Get()->RemoveDestructionObserver(this); -} - -void FileDescriptorWatcher::Controller::Watcher::StartWatching() { - DCHECK(thread_checker_.CalledOnValidThread()); - - if (!MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - fd_, false, mode_, &fd_watch_controller_, this)) { - // TODO(wez): Ideally we would [D]CHECK here, or propagate the failure back - // to the caller, but there is no guarantee that they haven't already - // closed |fd_| on another thread, so the best we can do is Debug-log. - DLOG(ERROR) << "Failed to watch fd=" << fd_; - } - - if (!registered_as_destruction_observer_) { - MessageLoopCurrentForIO::Get()->AddDestructionObserver(this); - registered_as_destruction_observer_ = true; - } -} - -void FileDescriptorWatcher::Controller::Watcher::OnFileCanReadWithoutBlocking( - int fd) { - DCHECK_EQ(fd_, fd); - DCHECK_EQ(MessagePumpForIO::WATCH_READ, mode_); - DCHECK(thread_checker_.CalledOnValidThread()); - - // Run the callback on the sequence on which the watch was initiated. - callback_task_runner_->PostTask( - FROM_HERE, BindOnce(&Controller::RunCallback, controller_)); -} - -void FileDescriptorWatcher::Controller::Watcher::OnFileCanWriteWithoutBlocking( - int fd) { - DCHECK_EQ(fd_, fd); - DCHECK_EQ(MessagePumpForIO::WATCH_WRITE, mode_); - DCHECK(thread_checker_.CalledOnValidThread()); - - // Run the callback on the sequence on which the watch was initiated. - callback_task_runner_->PostTask( - FROM_HERE, BindOnce(&Controller::RunCallback, controller_)); -} - -void FileDescriptorWatcher::Controller::Watcher:: - WillDestroyCurrentMessageLoop() { - DCHECK(thread_checker_.CalledOnValidThread()); - - // A Watcher is owned by a Controller. When the Controller is deleted, it - // transfers ownership of the Watcher to a delete task posted to the - // MessageLoopForIO. If the MessageLoopForIO is deleted before the delete task - // runs, the following line takes care of deleting the Watcher. - delete this; -} - -FileDescriptorWatcher::Controller::Controller(MessagePumpForIO::Mode mode, - int fd, - const Closure& callback) - : callback_(callback), - message_loop_for_io_task_runner_( - tls_message_loop_for_io.Get().Get()->task_runner()), - weak_factory_(this) { - DCHECK(!callback_.is_null()); - DCHECK(message_loop_for_io_task_runner_); - watcher_ = std::make_unique(weak_factory_.GetWeakPtr(), mode, fd); - StartWatching(); -} - -void FileDescriptorWatcher::Controller::StartWatching() { - DCHECK(sequence_checker_.CalledOnValidSequence()); - // It is safe to use Unretained() below because |watcher_| can only be deleted - // by a delete task posted to |message_loop_for_io_task_runner_| by this - // Controller's destructor. Since this delete task hasn't been posted yet, it - // can't run before the task posted below. - message_loop_for_io_task_runner_->PostTask( - FROM_HERE, BindOnce(&Watcher::StartWatching, Unretained(watcher_.get()))); -} - -void FileDescriptorWatcher::Controller::RunCallback() { - DCHECK(sequence_checker_.CalledOnValidSequence()); - - WeakPtr weak_this = weak_factory_.GetWeakPtr(); - - callback_.Run(); - - // If |this| wasn't deleted, re-enable the watch. - if (weak_this) - StartWatching(); -} - -FileDescriptorWatcher::FileDescriptorWatcher( - MessageLoopForIO* message_loop_for_io) { - DCHECK(message_loop_for_io); - DCHECK(!tls_message_loop_for_io.Get().Get()); - tls_message_loop_for_io.Get().Set(message_loop_for_io); -} - -FileDescriptorWatcher::~FileDescriptorWatcher() { - tls_message_loop_for_io.Get().Set(nullptr); -} - -std::unique_ptr -FileDescriptorWatcher::WatchReadable(int fd, const Closure& callback) { - return WrapUnique(new Controller(MessagePumpForIO::WATCH_READ, fd, callback)); -} - -std::unique_ptr -FileDescriptorWatcher::WatchWritable(int fd, const Closure& callback) { - return WrapUnique( - new Controller(MessagePumpForIO::WATCH_WRITE, fd, callback)); -} - -} // namespace base diff --git a/files/file_descriptor_watcher_posix.h b/files/file_descriptor_watcher_posix.h deleted file mode 100644 index aa4457904..000000000 --- a/files/file_descriptor_watcher_posix.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2016 The Chromium 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_DESCRIPTOR_WATCHER_POSIX_H_ -#define BASE_FILES_FILE_DESCRIPTOR_WATCHER_POSIX_H_ - -#include - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_pump_for_io.h" -#include "base/sequence_checker.h" - -namespace base { - -class SingleThreadTaskRunner; - -// The FileDescriptorWatcher API allows callbacks to be invoked when file -// descriptors are readable or writable without blocking. -// -// To enable this API in unit tests, use a ScopedTaskEnvironment with -// MainThreadType::IO. -// -// Note: Prefer FileDescriptorWatcher to MessageLoopForIO::WatchFileDescriptor() -// for non-critical IO. FileDescriptorWatcher works on threads/sequences without -// MessagePumps but involves going through the task queue after being notified -// by the OS (a desirablable property for non-critical IO that shouldn't preempt -// the main queue). -class BASE_EXPORT FileDescriptorWatcher { - public: - // Instantiated and returned by WatchReadable() or WatchWritable(). The - // constructor registers a callback to be invoked when a file descriptor is - // readable or writable without blocking and the destructor unregisters it. - class Controller { - public: - // Unregisters the callback registered by the constructor. - ~Controller(); - - private: - friend class FileDescriptorWatcher; - class Watcher; - - // Registers |callback| to be invoked when |fd| is readable or writable - // without blocking (depending on |mode|). - Controller(MessagePumpForIO::Mode mode, int fd, const Closure& callback); - - // Starts watching the file descriptor. - void StartWatching(); - - // Runs |callback_|. - void RunCallback(); - - // The callback to run when the watched file descriptor is readable or - // writable without blocking. - Closure callback_; - - // TaskRunner associated with the MessageLoopForIO that watches the file - // descriptor. - const scoped_refptr - message_loop_for_io_task_runner_; - - // Notified by the MessageLoopForIO associated with - // |message_loop_for_io_task_runner_| when the watched file descriptor is - // readable or writable without blocking. Posts a task to run RunCallback() - // on the sequence on which the Controller was instantiated. When the - // Controller is deleted, ownership of |watcher_| is transfered to a delete - // task posted to the MessageLoopForIO. This ensures that |watcher_| isn't - // deleted while it is being used by the MessageLoopForIO. - std::unique_ptr watcher_; - - // Validates that the Controller is used on the sequence on which it was - // instantiated. - SequenceChecker sequence_checker_; - - WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(Controller); - }; - - // Registers |message_loop_for_io| to watch file descriptors for which - // callbacks are registered from the current thread via WatchReadable() or - // WatchWritable(). |message_loop_for_io| may run on another thread. The - // constructed FileDescriptorWatcher must not outlive |message_loop_for_io|. - FileDescriptorWatcher(MessageLoopForIO* message_loop_for_io); - ~FileDescriptorWatcher(); - - // Registers |callback| to be posted on the current sequence when |fd| is - // readable or writable without blocking. |callback| is unregistered when the - // returned Controller is deleted (deletion must happen on the current - // sequence). To call these methods, a FileDescriptorWatcher must have been - // instantiated on the current thread and SequencedTaskRunnerHandle::IsSet() - // must return true (these conditions are met at least on all TaskScheduler - // threads as well as on threads backed by a MessageLoopForIO). - static std::unique_ptr WatchReadable(int fd, - const Closure& callback); - static std::unique_ptr WatchWritable(int fd, - const Closure& callback); - - private: - DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher); -}; - -} // namespace base - -#endif // BASE_FILES_FILE_DESCRIPTOR_WATCHER_POSIX_H_ diff --git a/files/file_descriptor_watcher_posix_unittest.cc b/files/file_descriptor_watcher_posix_unittest.cc deleted file mode 100644 index 4ed044b97..000000000 --- a/files/file_descriptor_watcher_posix_unittest.cc +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2016 The Chromium 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_descriptor_watcher_posix.h" - -#include - -#include - -#include "base/bind.h" -#include "base/files/file_util.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" -#include "base/posix/eintr_wrapper.h" -#include "base/run_loop.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread.h" -#include "base/threading/thread_checker_impl.h" -#include "build/build_config.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -class Mock { - public: - Mock() = default; - - MOCK_METHOD0(ReadableCallback, void()); - MOCK_METHOD0(WritableCallback, void()); - - private: - DISALLOW_COPY_AND_ASSIGN(Mock); -}; - -enum class FileDescriptorWatcherTestType { - MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD, - MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD, -}; - -class FileDescriptorWatcherTest - : public testing::TestWithParam { - public: - FileDescriptorWatcherTest() - : message_loop_(GetParam() == FileDescriptorWatcherTestType:: - MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD - ? new MessageLoopForIO - : new MessageLoop), - other_thread_("FileDescriptorWatcherTest_OtherThread") {} - ~FileDescriptorWatcherTest() override = default; - - void SetUp() override { - ASSERT_EQ(0, pipe(pipe_fds_)); - - MessageLoop* message_loop_for_io; - if (GetParam() == - FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD) { - Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - ASSERT_TRUE(other_thread_.StartWithOptions(options)); - message_loop_for_io = other_thread_.message_loop(); - } else { - message_loop_for_io = message_loop_.get(); - } - - ASSERT_TRUE(message_loop_for_io->IsType(MessageLoop::TYPE_IO)); - file_descriptor_watcher_ = std::make_unique( - static_cast(message_loop_for_io)); - } - - void TearDown() override { - if (GetParam() == - FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD && - message_loop_) { - // Allow the delete task posted by the Controller's destructor to run. - base::RunLoop().RunUntilIdle(); - } - - // Ensure that OtherThread is done processing before closing fds. - other_thread_.Stop(); - - EXPECT_EQ(0, IGNORE_EINTR(close(pipe_fds_[0]))); - EXPECT_EQ(0, IGNORE_EINTR(close(pipe_fds_[1]))); - } - - protected: - int read_file_descriptor() const { return pipe_fds_[0]; } - int write_file_descriptor() const { return pipe_fds_[1]; } - - // Waits for a short delay and run pending tasks. - void WaitAndRunPendingTasks() { - PlatformThread::Sleep(TestTimeouts::tiny_timeout()); - RunLoop().RunUntilIdle(); - } - - // Registers ReadableCallback() to be called on |mock_| when - // read_file_descriptor() is readable without blocking. - std::unique_ptr WatchReadable() { - std::unique_ptr controller = - FileDescriptorWatcher::WatchReadable( - read_file_descriptor(), - Bind(&Mock::ReadableCallback, Unretained(&mock_))); - EXPECT_TRUE(controller); - - // Unless read_file_descriptor() was readable before the callback was - // registered, this shouldn't do anything. - WaitAndRunPendingTasks(); - - return controller; - } - - // Registers WritableCallback() to be called on |mock_| when - // write_file_descriptor() is writable without blocking. - std::unique_ptr WatchWritable() { - std::unique_ptr controller = - FileDescriptorWatcher::WatchWritable( - write_file_descriptor(), - Bind(&Mock::WritableCallback, Unretained(&mock_))); - EXPECT_TRUE(controller); - return controller; - } - - void WriteByte() { - constexpr char kByte = '!'; - ASSERT_TRUE( - WriteFileDescriptor(write_file_descriptor(), &kByte, sizeof(kByte))); - } - - void ReadByte() { - // This is always called as part of the WatchReadable() callback, which - // should run on the main thread. - EXPECT_TRUE(thread_checker_.CalledOnValidThread()); - - char buffer; - ASSERT_TRUE(ReadFromFD(read_file_descriptor(), &buffer, sizeof(buffer))); - } - - // Mock on wich callbacks are invoked. - testing::StrictMock mock_; - - // MessageLoop bound to the main thread. - std::unique_ptr message_loop_; - - // Thread running a MessageLoopForIO. Used when the test type is - // MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD. - Thread other_thread_; - - private: - // Determines which MessageLoopForIO is used to watch file descriptors for - // which callbacks are registered on the main thread. - std::unique_ptr file_descriptor_watcher_; - - // Watched file descriptors. - int pipe_fds_[2]; - - // Used to verify that callbacks run on the thread on which they are - // registered. - ThreadCheckerImpl thread_checker_; - - DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcherTest); -}; - -} // namespace - -TEST_P(FileDescriptorWatcherTest, WatchWritable) { - auto controller = WatchWritable(); - - // The write end of a newly created pipe is immediately writable. - RunLoop run_loop; - EXPECT_CALL(mock_, WritableCallback()) - .WillOnce(testing::Invoke(&run_loop, &RunLoop::Quit)); - run_loop.Run(); -} - -TEST_P(FileDescriptorWatcherTest, WatchReadableOneByte) { - auto controller = WatchReadable(); - - // Write 1 byte to the pipe, making it readable without blocking. Expect one - // call to ReadableCallback() which will read 1 byte from the pipe. - WriteByte(); - RunLoop run_loop; - EXPECT_CALL(mock_, ReadableCallback()) - .WillOnce(testing::Invoke([this, &run_loop]() { - ReadByte(); - run_loop.Quit(); - })); - run_loop.Run(); - testing::Mock::VerifyAndClear(&mock_); - - // No more call to ReadableCallback() is expected. - WaitAndRunPendingTasks(); -} - -TEST_P(FileDescriptorWatcherTest, WatchReadableTwoBytes) { - auto controller = WatchReadable(); - - // Write 2 bytes to the pipe. Expect two calls to ReadableCallback() which - // will each read 1 byte from the pipe. - WriteByte(); - WriteByte(); - RunLoop run_loop; - EXPECT_CALL(mock_, ReadableCallback()) - .WillOnce(testing::Invoke([this]() { ReadByte(); })) - .WillOnce(testing::Invoke([this, &run_loop]() { - ReadByte(); - run_loop.Quit(); - })); - run_loop.Run(); - testing::Mock::VerifyAndClear(&mock_); - - // No more call to ReadableCallback() is expected. - WaitAndRunPendingTasks(); -} - -TEST_P(FileDescriptorWatcherTest, WatchReadableByteWrittenFromCallback) { - auto controller = WatchReadable(); - - // Write 1 byte to the pipe. Expect one call to ReadableCallback() from which - // 1 byte is read and 1 byte is written to the pipe. Then, expect another call - // to ReadableCallback() from which the remaining byte is read from the pipe. - WriteByte(); - RunLoop run_loop; - EXPECT_CALL(mock_, ReadableCallback()) - .WillOnce(testing::Invoke([this]() { - ReadByte(); - WriteByte(); - })) - .WillOnce(testing::Invoke([this, &run_loop]() { - ReadByte(); - run_loop.Quit(); - })); - run_loop.Run(); - testing::Mock::VerifyAndClear(&mock_); - - // No more call to ReadableCallback() is expected. - WaitAndRunPendingTasks(); -} - -TEST_P(FileDescriptorWatcherTest, DeleteControllerFromCallback) { - auto controller = WatchReadable(); - - // Write 1 byte to the pipe. Expect one call to ReadableCallback() from which - // |controller| is deleted. - WriteByte(); - RunLoop run_loop; - EXPECT_CALL(mock_, ReadableCallback()) - .WillOnce(testing::Invoke([&run_loop, &controller]() { - controller = nullptr; - run_loop.Quit(); - })); - run_loop.Run(); - testing::Mock::VerifyAndClear(&mock_); - - // Since |controller| has been deleted, no call to ReadableCallback() is - // expected even though the pipe is still readable without blocking. - WaitAndRunPendingTasks(); -} - -TEST_P(FileDescriptorWatcherTest, - DeleteControllerBeforeFileDescriptorReadable) { - auto controller = WatchReadable(); - - // Cancel the watch. - controller = nullptr; - - // Write 1 byte to the pipe to make it readable without blocking. - WriteByte(); - - // No call to ReadableCallback() is expected. - WaitAndRunPendingTasks(); -} - -TEST_P(FileDescriptorWatcherTest, DeleteControllerAfterFileDescriptorReadable) { - auto controller = WatchReadable(); - - // Write 1 byte to the pipe to make it readable without blocking. - WriteByte(); - - // Cancel the watch. - controller = nullptr; - - // No call to ReadableCallback() is expected. - WaitAndRunPendingTasks(); -} - -TEST_P(FileDescriptorWatcherTest, DeleteControllerAfterDeleteMessageLoopForIO) { - auto controller = WatchReadable(); - - // Delete the MessageLoopForIO. - if (GetParam() == - FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD) { - message_loop_ = nullptr; - } else { - other_thread_.Stop(); - } - - // Deleting |controller| shouldn't crash even though that causes a task to be - // posted to the MessageLoopForIO thread. - controller = nullptr; -} - -INSTANTIATE_TEST_CASE_P( - MessageLoopForIOOnMainThread, - FileDescriptorWatcherTest, - ::testing::Values( - FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD)); -INSTANTIATE_TEST_CASE_P( - MessageLoopForIOOnOtherThread, - FileDescriptorWatcherTest, - ::testing::Values( - FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD)); - -} // namespace base diff --git a/files/file_enumerator.cc b/files/file_enumerator.cc deleted file mode 100644 index 9dfb2ba04..000000000 --- a/files/file_enumerator.cc +++ /dev/null @@ -1,25 +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/files/file_enumerator.h" - -#include "base/files/file_util.h" - -namespace base { - -FileEnumerator::FileInfo::~FileInfo() = default; - -bool FileEnumerator::ShouldSkip(const FilePath& path) { - FilePath::StringType basename = path.BaseName().value(); - return basename == FILE_PATH_LITERAL(".") || - (basename == FILE_PATH_LITERAL("..") && - !(INCLUDE_DOT_DOT & file_type_)); -} - -bool FileEnumerator::IsTypeMatched(bool is_dir) const { - return (file_type_ & - (is_dir ? FileEnumerator::DIRECTORIES : FileEnumerator::FILES)) != 0; -} - -} // namespace base diff --git a/files/file_enumerator.h b/files/file_enumerator.h deleted file mode 100644 index 0fa99a686..000000000 --- a/files/file_enumerator.h +++ /dev/null @@ -1,174 +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_FILES_FILE_ENUMERATOR_H_ -#define BASE_FILES_FILE_ENUMERATOR_H_ - -#include -#include - -#include - -#include "base/base_export.h" -#include "base/containers/stack.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/time/time.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#include -#endif - -namespace base { - -// A class for enumerating the files in a provided path. The order of the -// results is not guaranteed. -// -// This is blocking. Do not use on critical threads. -// -// Example: -// -// base::FileEnumerator enum(my_dir, false, base::FileEnumerator::FILES, -// FILE_PATH_LITERAL("*.txt")); -// for (base::FilePath name = enum.Next(); !name.empty(); name = enum.Next()) -// ... -class BASE_EXPORT FileEnumerator { - public: - // Note: copy & assign supported. - class BASE_EXPORT FileInfo { - public: - FileInfo(); - ~FileInfo(); - - bool IsDirectory() const; - - // The name of the file. This will not include any path information. This - // is in constrast to the value returned by FileEnumerator.Next() which - // includes the |root_path| passed into the FileEnumerator constructor. - FilePath GetName() const; - - int64_t GetSize() const; - Time GetLastModifiedTime() const; - -#if defined(OS_WIN) - // Note that the cAlternateFileName (used to hold the "short" 8.3 name) - // of the WIN32_FIND_DATA will be empty. Since we don't use short file - // names, we tell Windows to omit it which speeds up the query slightly. - const WIN32_FIND_DATA& find_data() const { return find_data_; } -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - const struct stat& stat() const { return stat_; } -#endif - - private: - friend class FileEnumerator; - -#if defined(OS_WIN) - WIN32_FIND_DATA find_data_; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - struct stat stat_; - FilePath filename_; -#endif - }; - - enum FileType { - FILES = 1 << 0, - DIRECTORIES = 1 << 1, - INCLUDE_DOT_DOT = 1 << 2, -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - SHOW_SYM_LINKS = 1 << 4, -#endif - }; - - // Search policy for intermediate folders. - enum class FolderSearchPolicy { - // Recursive search will pass through folders whose names match the - // pattern. Inside each one, all files will be returned. Folders with names - // that do not match the pattern will be ignored within their interior. - MATCH_ONLY, - // Recursive search will pass through every folder and perform pattern - // matching inside each one. - ALL, - }; - - // |root_path| is the starting directory to search for. It may or may not end - // in a slash. - // - // If |recursive| is true, this will enumerate all matches in any - // subdirectories matched as well. It does a breadth-first search, so all - // files in one directory will be returned before any files in a - // subdirectory. - // - // |file_type|, a bit mask of FileType, specifies whether the enumerator - // should match files, directories, or both. - // - // |pattern| is an optional pattern for which files to match. This - // works like shell globbing. For example, "*.txt" or "Foo???.doc". - // However, be careful in specifying patterns that aren't cross platform - // since the underlying code uses OS-specific matching routines. In general, - // Windows matching is less featureful than others, so test there first. - // If unspecified, this will match all files. - FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type); - FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type, - const FilePath::StringType& pattern); - FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type, - const FilePath::StringType& pattern, - FolderSearchPolicy folder_search_policy); - ~FileEnumerator(); - - // Returns the next file or an empty string if there are no more results. - // - // The returned path will incorporate the |root_path| passed in the - // constructor: "/file_name.txt". If the |root_path| is absolute, - // then so will be the result of Next(). - FilePath Next(); - - // Write the file info into |info|. - FileInfo GetInfo() const; - - private: - // Returns true if the given path should be skipped in enumeration. - bool ShouldSkip(const FilePath& path); - - bool IsTypeMatched(bool is_dir) const; - - bool IsPatternMatched(const FilePath& src) const; - -#if defined(OS_WIN) - // True when find_data_ is valid. - bool has_find_data_ = false; - WIN32_FIND_DATA find_data_; - HANDLE find_handle_ = INVALID_HANDLE_VALUE; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // The files in the current directory - std::vector directory_entries_; - - // The next entry to use from the directory_entries_ vector - size_t current_directory_entry_; -#endif - FilePath root_path_; - const bool recursive_; - const int file_type_; - FilePath::StringType pattern_; - const FolderSearchPolicy folder_search_policy_; - - // A stack that keeps track of which subdirectories we still need to - // enumerate in the breadth-first search. - base::stack pending_paths_; - - DISALLOW_COPY_AND_ASSIGN(FileEnumerator); -}; - -} // namespace base - -#endif // BASE_FILES_FILE_ENUMERATOR_H_ diff --git a/files/file_enumerator_posix.cc b/files/file_enumerator_posix.cc deleted file mode 100644 index 4b429c644..000000000 --- a/files/file_enumerator_posix.cc +++ /dev/null @@ -1,188 +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/files/file_enumerator.h" - -#include -#include -#include -#include -#include - -#include "base/logging.h" -#include "base/threading/thread_restrictions.h" -#include "build/build_config.h" - -namespace base { -namespace { - -void GetStat(const FilePath& path, bool show_links, struct stat* st) { - DCHECK(st); - const int res = show_links ? lstat(path.value().c_str(), st) - : stat(path.value().c_str(), st); - if (res < 0) { - // Print the stat() error message unless it was ENOENT and we're following - // symlinks. - if (!(errno == ENOENT && !show_links)) - DPLOG(ERROR) << "Couldn't stat" << path.value(); - memset(st, 0, sizeof(*st)); - } -} - -} // namespace - -// FileEnumerator::FileInfo ---------------------------------------------------- - -FileEnumerator::FileInfo::FileInfo() { - memset(&stat_, 0, sizeof(stat_)); -} - -bool FileEnumerator::FileInfo::IsDirectory() const { - return S_ISDIR(stat_.st_mode); -} - -FilePath FileEnumerator::FileInfo::GetName() const { - return filename_; -} - -int64_t FileEnumerator::FileInfo::GetSize() const { - return stat_.st_size; -} - -base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { - return base::Time::FromTimeT(stat_.st_mtime); -} - -// FileEnumerator -------------------------------------------------------------- - -FileEnumerator::FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type) - : FileEnumerator(root_path, - recursive, - file_type, - FilePath::StringType(), - FolderSearchPolicy::MATCH_ONLY) {} - -FileEnumerator::FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type, - const FilePath::StringType& pattern) - : FileEnumerator(root_path, - recursive, - file_type, - pattern, - FolderSearchPolicy::MATCH_ONLY) {} - -FileEnumerator::FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type, - const FilePath::StringType& pattern, - FolderSearchPolicy folder_search_policy) - : current_directory_entry_(0), - root_path_(root_path), - recursive_(recursive), - file_type_(file_type), - pattern_(pattern), - folder_search_policy_(folder_search_policy) { - // INCLUDE_DOT_DOT must not be specified if recursive. - DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); - - pending_paths_.push(root_path); -} - -FileEnumerator::~FileEnumerator() = default; - -FilePath FileEnumerator::Next() { - AssertBlockingAllowed(); - - ++current_directory_entry_; - - // While we've exhausted the entries in the current directory, do the next - while (current_directory_entry_ >= directory_entries_.size()) { - if (pending_paths_.empty()) - return FilePath(); - - root_path_ = pending_paths_.top(); - root_path_ = root_path_.StripTrailingSeparators(); - pending_paths_.pop(); - - DIR* dir = opendir(root_path_.value().c_str()); - if (!dir) - continue; - - directory_entries_.clear(); - -#if defined(OS_FUCHSIA) - // Fuchsia does not support .. on the file system server side, see - // https://fuchsia.googlesource.com/docs/+/master/dotdot.md and - // https://crbug.com/735540. However, for UI purposes, having the parent - // directory show up in directory listings makes sense, so we add it here to - // match the expectation on other operating systems. In cases where this - // is useful it should be resolvable locally. - FileInfo dotdot; - dotdot.stat_.st_mode = S_IFDIR; - dotdot.filename_ = FilePath(".."); - if (!ShouldSkip(dotdot.filename_)) { - directory_entries_.push_back(std::move(dotdot)); - } -#endif // OS_FUCHSIA - - current_directory_entry_ = 0; - struct dirent* dent; - while ((dent = readdir(dir))) { - FileInfo info; - info.filename_ = FilePath(dent->d_name); - - if (ShouldSkip(info.filename_)) - continue; - - const bool is_pattern_matched = IsPatternMatched(info.filename_); - - // MATCH_ONLY policy enumerates files and directories which matching - // pattern only. So we can early skip further checks. - if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY && - !is_pattern_matched) - continue; - - // Do not call OS stat/lstat if there is no sense to do it. If pattern is - // not matched (file will not appear in results) and search is not - // recursive (possible directory will not be added to pending paths) - - // there is no sense to obtain item below. - if (!recursive_ && !is_pattern_matched) - continue; - - const FilePath full_path = root_path_.Append(info.filename_); - GetStat(full_path, file_type_ & SHOW_SYM_LINKS, &info.stat_); - - const bool is_dir = info.IsDirectory(); - - if (recursive_ && is_dir) - pending_paths_.push(full_path); - - if (is_pattern_matched && IsTypeMatched(is_dir)) - directory_entries_.push_back(std::move(info)); - } - closedir(dir); - - // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern. - // ALL policy enumerates files in all subfolders by origin pattern. - if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) - pattern_.clear(); - } - - return root_path_.Append( - directory_entries_[current_directory_entry_].filename_); -} - -FileEnumerator::FileInfo FileEnumerator::GetInfo() const { - return directory_entries_[current_directory_entry_]; -} - -bool FileEnumerator::IsPatternMatched(const FilePath& path) const { - return pattern_.empty() || - !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE); -} - -} // namespace base diff --git a/files/file_enumerator_unittest.cc b/files/file_enumerator_unittest.cc deleted file mode 100644 index 11df075ec..000000000 --- a/files/file_enumerator_unittest.cc +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2017 The Chromium 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_enumerator.h" - -#include -#include - -#include "base/containers/circular_deque.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::ElementsAre; -using testing::IsEmpty; -using testing::UnorderedElementsAre; - -namespace base { -namespace { - -const FilePath::StringType kEmptyPattern; - -const std::vector kFolderSearchPolicies{ - FileEnumerator::FolderSearchPolicy::MATCH_ONLY, - FileEnumerator::FolderSearchPolicy::ALL}; - -circular_deque RunEnumerator( - const FilePath& root_path, - bool recursive, - int file_type, - const FilePath::StringType& pattern, - FileEnumerator::FolderSearchPolicy folder_search_policy) { - circular_deque rv; - FileEnumerator enumerator(root_path, recursive, file_type, pattern, - folder_search_policy); - for (auto file = enumerator.Next(); !file.empty(); file = enumerator.Next()) - rv.emplace_back(std::move(file)); - return rv; -} - -bool CreateDummyFile(const FilePath& path) { - return WriteFile(path, "42", sizeof("42")) == sizeof("42"); -} - -} // namespace - -TEST(FileEnumerator, NotExistingPath) { - const FilePath path = FilePath::FromUTF8Unsafe("some_not_existing_path"); - ASSERT_FALSE(PathExists(path)); - - for (auto policy : kFolderSearchPolicies) { - const auto files = RunEnumerator( - path, true, FileEnumerator::FILES & FileEnumerator::DIRECTORIES, - FILE_PATH_LITERAL(""), policy); - EXPECT_THAT(files, IsEmpty()); - } -} - -TEST(FileEnumerator, EmptyFolder) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - for (auto policy : kFolderSearchPolicies) { - const auto files = - RunEnumerator(temp_dir.GetPath(), true, - FileEnumerator::FILES & FileEnumerator::DIRECTORIES, - kEmptyPattern, policy); - EXPECT_THAT(files, IsEmpty()); - } -} - -TEST(FileEnumerator, SingleFileInFolderForFileSearch) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath& path = temp_dir.GetPath(); - const FilePath file = path.AppendASCII("test.txt"); - ASSERT_TRUE(CreateDummyFile(file)); - - for (auto policy : kFolderSearchPolicies) { - const auto files = RunEnumerator( - temp_dir.GetPath(), true, FileEnumerator::FILES, kEmptyPattern, policy); - EXPECT_THAT(files, ElementsAre(file)); - } -} - -TEST(FileEnumerator, SingleFileInFolderForDirSearch) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath& path = temp_dir.GetPath(); - ASSERT_TRUE(CreateDummyFile(path.AppendASCII("test.txt"))); - - for (auto policy : kFolderSearchPolicies) { - const auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES, - kEmptyPattern, policy); - EXPECT_THAT(files, IsEmpty()); - } -} - -TEST(FileEnumerator, SingleFileInFolderWithFiltering) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath& path = temp_dir.GetPath(); - const FilePath file = path.AppendASCII("test.txt"); - ASSERT_TRUE(CreateDummyFile(file)); - - for (auto policy : kFolderSearchPolicies) { - auto files = RunEnumerator(path, true, FileEnumerator::FILES, - FILE_PATH_LITERAL("*.txt"), policy); - EXPECT_THAT(files, ElementsAre(file)); - - files = RunEnumerator(path, true, FileEnumerator::FILES, - FILE_PATH_LITERAL("*.pdf"), policy); - EXPECT_THAT(files, IsEmpty()); - } -} - -TEST(FileEnumerator, TwoFilesInFolder) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath& path = temp_dir.GetPath(); - const FilePath foo_txt = path.AppendASCII("foo.txt"); - const FilePath bar_txt = path.AppendASCII("bar.txt"); - ASSERT_TRUE(CreateDummyFile(foo_txt)); - ASSERT_TRUE(CreateDummyFile(bar_txt)); - - for (auto policy : kFolderSearchPolicies) { - auto files = RunEnumerator(path, true, FileEnumerator::FILES, - FILE_PATH_LITERAL("*.txt"), policy); - EXPECT_THAT(files, UnorderedElementsAre(foo_txt, bar_txt)); - - files = RunEnumerator(path, true, FileEnumerator::FILES, - FILE_PATH_LITERAL("foo*"), policy); - EXPECT_THAT(files, ElementsAre(foo_txt)); - - files = RunEnumerator(path, true, FileEnumerator::FILES, - FILE_PATH_LITERAL("*.pdf"), policy); - EXPECT_THAT(files, IsEmpty()); - - files = - RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy); - EXPECT_THAT(files, UnorderedElementsAre(foo_txt, bar_txt)); - } -} - -TEST(FileEnumerator, SingleFolderInFolderForFileSearch) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath& path = temp_dir.GetPath(); - - ScopedTempDir temp_subdir; - ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path)); - - for (auto policy : kFolderSearchPolicies) { - const auto files = - RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy); - EXPECT_THAT(files, IsEmpty()); - } -} - -TEST(FileEnumerator, SingleFolderInFolderForDirSearch) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath& path = temp_dir.GetPath(); - - ScopedTempDir temp_subdir; - ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path)); - - for (auto policy : kFolderSearchPolicies) { - const auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES, - kEmptyPattern, policy); - EXPECT_THAT(files, ElementsAre(temp_subdir.GetPath())); - } -} - -TEST(FileEnumerator, TwoFoldersInFolder) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath& path = temp_dir.GetPath(); - - const FilePath subdir_foo = path.AppendASCII("foo"); - const FilePath subdir_bar = path.AppendASCII("bar"); - ASSERT_TRUE(CreateDirectory(subdir_foo)); - ASSERT_TRUE(CreateDirectory(subdir_bar)); - - for (auto policy : kFolderSearchPolicies) { - auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES, - kEmptyPattern, policy); - EXPECT_THAT(files, UnorderedElementsAre(subdir_foo, subdir_bar)); - - files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES, - FILE_PATH_LITERAL("foo"), policy); - EXPECT_THAT(files, ElementsAre(subdir_foo)); - } -} - -TEST(FileEnumerator, FolderAndFileInFolder) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath& path = temp_dir.GetPath(); - - ScopedTempDir temp_subdir; - ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path)); - const FilePath file = path.AppendASCII("test.txt"); - ASSERT_TRUE(CreateDummyFile(file)); - - for (auto policy : kFolderSearchPolicies) { - auto files = - RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy); - EXPECT_THAT(files, ElementsAre(file)); - - files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES, - kEmptyPattern, policy); - EXPECT_THAT(files, ElementsAre(temp_subdir.GetPath())); - - files = RunEnumerator(path, true, - FileEnumerator::FILES | FileEnumerator::DIRECTORIES, - kEmptyPattern, policy); - EXPECT_THAT(files, UnorderedElementsAre(file, temp_subdir.GetPath())); - } -} - -TEST(FileEnumerator, FilesInParentFolderAlwaysFirst) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath& path = temp_dir.GetPath(); - - ScopedTempDir temp_subdir; - ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path)); - const FilePath foo_txt = path.AppendASCII("foo.txt"); - const FilePath bar_txt = temp_subdir.GetPath().AppendASCII("bar.txt"); - ASSERT_TRUE(CreateDummyFile(foo_txt)); - ASSERT_TRUE(CreateDummyFile(bar_txt)); - - for (auto policy : kFolderSearchPolicies) { - const auto files = - RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy); - EXPECT_THAT(files, ElementsAre(foo_txt, bar_txt)); - } -} - -TEST(FileEnumerator, FileInSubfolder) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath subdir = temp_dir.GetPath().AppendASCII("subdir"); - ASSERT_TRUE(CreateDirectory(subdir)); - - const FilePath file = subdir.AppendASCII("test.txt"); - ASSERT_TRUE(CreateDummyFile(file)); - - for (auto policy : kFolderSearchPolicies) { - auto files = RunEnumerator(temp_dir.GetPath(), true, FileEnumerator::FILES, - kEmptyPattern, policy); - EXPECT_THAT(files, ElementsAre(file)); - - files = RunEnumerator(temp_dir.GetPath(), false, FileEnumerator::FILES, - kEmptyPattern, policy); - EXPECT_THAT(files, IsEmpty()); - } -} - -TEST(FileEnumerator, FilesInSubfoldersWithFiltering) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - const FilePath test_txt = temp_dir.GetPath().AppendASCII("test.txt"); - const FilePath subdir_foo = temp_dir.GetPath().AppendASCII("foo_subdir"); - const FilePath subdir_bar = temp_dir.GetPath().AppendASCII("bar_subdir"); - const FilePath foo_test = subdir_foo.AppendASCII("test.txt"); - const FilePath foo_foo = subdir_foo.AppendASCII("foo.txt"); - const FilePath foo_bar = subdir_foo.AppendASCII("bar.txt"); - const FilePath bar_test = subdir_bar.AppendASCII("test.txt"); - const FilePath bar_foo = subdir_bar.AppendASCII("foo.txt"); - const FilePath bar_bar = subdir_bar.AppendASCII("bar.txt"); - ASSERT_TRUE(CreateDummyFile(test_txt)); - ASSERT_TRUE(CreateDirectory(subdir_foo)); - ASSERT_TRUE(CreateDirectory(subdir_bar)); - ASSERT_TRUE(CreateDummyFile(foo_test)); - ASSERT_TRUE(CreateDummyFile(foo_foo)); - ASSERT_TRUE(CreateDummyFile(foo_bar)); - ASSERT_TRUE(CreateDummyFile(bar_test)); - ASSERT_TRUE(CreateDummyFile(bar_foo)); - ASSERT_TRUE(CreateDummyFile(bar_bar)); - - auto files = - RunEnumerator(temp_dir.GetPath(), true, - FileEnumerator::FILES | FileEnumerator::DIRECTORIES, - FILE_PATH_LITERAL("foo*"), - FileEnumerator::FolderSearchPolicy::MATCH_ONLY); - EXPECT_THAT(files, - UnorderedElementsAre(subdir_foo, foo_test, foo_foo, foo_bar)); - - files = RunEnumerator(temp_dir.GetPath(), true, - FileEnumerator::FILES | FileEnumerator::DIRECTORIES, - FILE_PATH_LITERAL("foo*"), - FileEnumerator::FolderSearchPolicy::ALL); - EXPECT_THAT(files, UnorderedElementsAre(subdir_foo, foo_foo, bar_foo)); -} - -} // namespace base diff --git a/files/file_enumerator_win.cc b/files/file_enumerator_win.cc deleted file mode 100644 index f96074cec..000000000 --- a/files/file_enumerator_win.cc +++ /dev/null @@ -1,195 +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/files/file_enumerator.h" - -#include -#include -#include - -#include "base/logging.h" -#include "base/threading/thread_restrictions.h" - -namespace base { - -namespace { - -FilePath BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy, - const FilePath& root_path, - const FilePath::StringType& pattern) { - // MATCH_ONLY policy filters incoming files by pattern on OS side. ALL policy - // collects all files and filters them manually. - switch (policy) { - case FileEnumerator::FolderSearchPolicy::MATCH_ONLY: - return root_path.Append(pattern); - case FileEnumerator::FolderSearchPolicy::ALL: - return root_path.Append(L"*"); - } - NOTREACHED(); - return {}; -} - -} // namespace - -// FileEnumerator::FileInfo ---------------------------------------------------- - -FileEnumerator::FileInfo::FileInfo() { - memset(&find_data_, 0, sizeof(find_data_)); -} - -bool FileEnumerator::FileInfo::IsDirectory() const { - return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; -} - -FilePath FileEnumerator::FileInfo::GetName() const { - return FilePath(find_data_.cFileName); -} - -int64_t FileEnumerator::FileInfo::GetSize() const { - ULARGE_INTEGER size; - size.HighPart = find_data_.nFileSizeHigh; - size.LowPart = find_data_.nFileSizeLow; - DCHECK_LE(size.QuadPart, - static_cast(std::numeric_limits::max())); - return static_cast(size.QuadPart); -} - -base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { - return base::Time::FromFileTime(find_data_.ftLastWriteTime); -} - -// FileEnumerator -------------------------------------------------------------- - -FileEnumerator::FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type) - : FileEnumerator(root_path, - recursive, - file_type, - FilePath::StringType(), - FolderSearchPolicy::MATCH_ONLY) {} - -FileEnumerator::FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type, - const FilePath::StringType& pattern) - : FileEnumerator(root_path, - recursive, - file_type, - pattern, - FolderSearchPolicy::MATCH_ONLY) {} - -FileEnumerator::FileEnumerator(const FilePath& root_path, - bool recursive, - int file_type, - const FilePath::StringType& pattern, - FolderSearchPolicy folder_search_policy) - : recursive_(recursive), - file_type_(file_type), - pattern_(!pattern.empty() ? pattern : L"*"), - folder_search_policy_(folder_search_policy) { - // INCLUDE_DOT_DOT must not be specified if recursive. - DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); - memset(&find_data_, 0, sizeof(find_data_)); - pending_paths_.push(root_path); -} - -FileEnumerator::~FileEnumerator() { - if (find_handle_ != INVALID_HANDLE_VALUE) - FindClose(find_handle_); -} - -FileEnumerator::FileInfo FileEnumerator::GetInfo() const { - if (!has_find_data_) { - NOTREACHED(); - return FileInfo(); - } - FileInfo ret; - memcpy(&ret.find_data_, &find_data_, sizeof(find_data_)); - return ret; -} - -FilePath FileEnumerator::Next() { - AssertBlockingAllowed(); - - while (has_find_data_ || !pending_paths_.empty()) { - if (!has_find_data_) { - // The last find FindFirstFile operation is done, prepare a new one. - root_path_ = pending_paths_.top(); - pending_paths_.pop(); - - // Start a new find operation. - const FilePath src = - BuildSearchFilter(folder_search_policy_, root_path_, pattern_); - find_handle_ = FindFirstFileEx(src.value().c_str(), - FindExInfoBasic, // Omit short name. - &find_data_, FindExSearchNameMatch, - nullptr, FIND_FIRST_EX_LARGE_FETCH); - has_find_data_ = true; - } else { - // Search for the next file/directory. - if (!FindNextFile(find_handle_, &find_data_)) { - FindClose(find_handle_); - find_handle_ = INVALID_HANDLE_VALUE; - } - } - - if (INVALID_HANDLE_VALUE == find_handle_) { - has_find_data_ = false; - - // MATCH_ONLY policy clears pattern for matched subfolders. ALL policy - // applies pattern for all subfolders. - if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) { - // This is reached when we have finished a directory and are advancing - // to the next one in the queue. We applied the pattern (if any) to the - // files in the root search directory, but for those directories which - // were matched, we want to enumerate all files inside them. This will - // happen when the handle is empty. - pattern_ = L"*"; - } - - continue; - } - - const FilePath filename(find_data_.cFileName); - if (ShouldSkip(filename)) - continue; - - const bool is_dir = - (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - const FilePath abs_path = root_path_.Append(filename); - - // Check if directory should be processed recursive. - if (is_dir && recursive_) { - // If |cur_file| is a directory, and we are doing recursive searching, - // add it to pending_paths_ so we scan it after we finish scanning this - // directory. However, don't do recursion through reparse points or we - // may end up with an infinite cycle. - DWORD attributes = GetFileAttributes(abs_path.value().c_str()); - if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) - pending_paths_.push(abs_path); - } - - if (IsTypeMatched(is_dir) && IsPatternMatched(filename)) - return abs_path; - } - return FilePath(); -} - -bool FileEnumerator::IsPatternMatched(const FilePath& src) const { - switch (folder_search_policy_) { - case FolderSearchPolicy::MATCH_ONLY: - // MATCH_ONLY policy filters by pattern on search request, so all found - // files already fits to pattern. - return true; - case FolderSearchPolicy::ALL: - // ALL policy enumerates all files, we need to check pattern match - // manually. - return PathMatchSpec(src.value().c_str(), pattern_.c_str()) == TRUE; - } - NOTREACHED(); - return false; -} - -} // namespace base diff --git a/files/file_locking_unittest.cc b/files/file_locking_unittest.cc deleted file mode 100644 index e158b7d06..000000000 --- a/files/file_locking_unittest.cc +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/command_line.h" -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/macros.h" -#include "base/test/multiprocess_test.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/multiprocess_func_list.h" - -using base::File; -using base::FilePath; - -namespace { - -// Flag for the parent to share a temp dir to the child. -const char kTempDirFlag[] = "temp-dir"; - -// Flags to control how the subprocess unlocks the file. -const char kFileUnlock[] = "file-unlock"; -const char kCloseUnlock[] = "close-unlock"; -const char kExitUnlock[] = "exit-unlock"; - -// File to lock in temp dir. -const char kLockFile[] = "lockfile"; - -// Constants for various requests and responses, used as |signal_file| parameter -// to signal/wait helpers. -const char kSignalLockFileLocked[] = "locked.signal"; -const char kSignalLockFileClose[] = "close.signal"; -const char kSignalLockFileClosed[] = "closed.signal"; -const char kSignalLockFileUnlock[] = "unlock.signal"; -const char kSignalLockFileUnlocked[] = "unlocked.signal"; -const char kSignalExit[] = "exit.signal"; - -// Signal an event by creating a file which didn't previously exist. -bool SignalEvent(const FilePath& signal_dir, const char* signal_file) { - File file(signal_dir.AppendASCII(signal_file), - File::FLAG_CREATE | File::FLAG_WRITE); - return file.IsValid(); -} - -// Check whether an event was signaled. -bool CheckEvent(const FilePath& signal_dir, const char* signal_file) { - File file(signal_dir.AppendASCII(signal_file), - File::FLAG_OPEN | File::FLAG_READ); - return file.IsValid(); -} - -// Busy-wait for an event to be signaled, returning false for timeout. -bool WaitForEventWithTimeout(const FilePath& signal_dir, - const char* signal_file, - const base::TimeDelta& timeout) { - const base::Time finish_by = base::Time::Now() + timeout; - while (!CheckEvent(signal_dir, signal_file)) { - if (base::Time::Now() > finish_by) - return false; - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); - } - return true; -} - -// Wait forever for the event to be signaled (should never return false). -bool WaitForEvent(const FilePath& signal_dir, const char* signal_file) { - return WaitForEventWithTimeout(signal_dir, signal_file, - base::TimeDelta::Max()); -} - -// Keep these in sync so StartChild*() can refer to correct test main. -#define ChildMain ChildLockUnlock -#define ChildMainString "ChildLockUnlock" - -// Subprocess to test getting a file lock then releasing it. |kTempDirFlag| -// must pass in an existing temporary directory for the lockfile and signal -// files. One of the following flags must be passed to determine how to unlock -// the lock file: -// - |kFileUnlock| calls Unlock() to unlock. -// - |kCloseUnlock| calls Close() while the lock is held. -// - |kExitUnlock| exits while the lock is held. -MULTIPROCESS_TEST_MAIN(ChildMain) { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - const FilePath temp_path = command_line->GetSwitchValuePath(kTempDirFlag); - CHECK(base::DirectoryExists(temp_path)); - - // Immediately lock the file. - File file(temp_path.AppendASCII(kLockFile), - File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE); - CHECK(file.IsValid()); - CHECK_EQ(File::FILE_OK, file.Lock()); - CHECK(SignalEvent(temp_path, kSignalLockFileLocked)); - - if (command_line->HasSwitch(kFileUnlock)) { - // Wait for signal to unlock, then unlock the file. - CHECK(WaitForEvent(temp_path, kSignalLockFileUnlock)); - CHECK_EQ(File::FILE_OK, file.Unlock()); - CHECK(SignalEvent(temp_path, kSignalLockFileUnlocked)); - } else if (command_line->HasSwitch(kCloseUnlock)) { - // Wait for the signal to close, then close the file. - CHECK(WaitForEvent(temp_path, kSignalLockFileClose)); - file.Close(); - CHECK(!file.IsValid()); - CHECK(SignalEvent(temp_path, kSignalLockFileClosed)); - } else { - CHECK(command_line->HasSwitch(kExitUnlock)); - } - - // Wait for signal to exit, so that unlock or close can be distinguished from - // exit. - CHECK(WaitForEvent(temp_path, kSignalExit)); - return 0; -} - -} // namespace - -class FileLockingTest : public testing::Test { - public: - FileLockingTest() = default; - - protected: - void SetUp() override { - testing::Test::SetUp(); - - // Setup the temp dir and the lock file. - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - lock_file_.Initialize( - temp_dir_.GetPath().AppendASCII(kLockFile), - File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE); - ASSERT_TRUE(lock_file_.IsValid()); - } - - bool SignalEvent(const char* signal_file) { - return ::SignalEvent(temp_dir_.GetPath(), signal_file); - } - - bool WaitForEventOrTimeout(const char* signal_file) { - return ::WaitForEventWithTimeout(temp_dir_.GetPath(), signal_file, - TestTimeouts::action_timeout()); - } - - // Start a child process set to use the specified unlock action, and wait for - // it to lock the file. - void StartChildAndSignalLock(const char* unlock_action) { - // Create a temporary dir and spin up a ChildLockExit subprocess against it. - const FilePath temp_path = temp_dir_.GetPath(); - base::CommandLine child_command_line( - base::GetMultiProcessTestChildBaseCommandLine()); - child_command_line.AppendSwitchPath(kTempDirFlag, temp_path); - child_command_line.AppendSwitch(unlock_action); - lock_child_ = base::SpawnMultiProcessTestChild( - ChildMainString, child_command_line, base::LaunchOptions()); - ASSERT_TRUE(lock_child_.IsValid()); - - // Wait for the child to lock the file. - ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileLocked)); - } - - // Signal the child to exit cleanly. - void ExitChildCleanly() { - ASSERT_TRUE(SignalEvent(kSignalExit)); - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - lock_child_, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - - base::ScopedTempDir temp_dir_; - base::File lock_file_; - base::Process lock_child_; - - private: - DISALLOW_COPY_AND_ASSIGN(FileLockingTest); -}; - -// Test that locks are released by Unlock(). -TEST_F(FileLockingTest, LockAndUnlock) { - StartChildAndSignalLock(kFileUnlock); - - ASSERT_NE(File::FILE_OK, lock_file_.Lock()); - ASSERT_TRUE(SignalEvent(kSignalLockFileUnlock)); - ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileUnlocked)); - ASSERT_EQ(File::FILE_OK, lock_file_.Lock()); - ASSERT_EQ(File::FILE_OK, lock_file_.Unlock()); - - ExitChildCleanly(); -} - -// Test that locks are released on Close(). -TEST_F(FileLockingTest, UnlockOnClose) { - StartChildAndSignalLock(kCloseUnlock); - - ASSERT_NE(File::FILE_OK, lock_file_.Lock()); - ASSERT_TRUE(SignalEvent(kSignalLockFileClose)); - ASSERT_TRUE(WaitForEventOrTimeout(kSignalLockFileClosed)); - ASSERT_EQ(File::FILE_OK, lock_file_.Lock()); - ASSERT_EQ(File::FILE_OK, lock_file_.Unlock()); - - ExitChildCleanly(); -} - -// Test that locks are released on exit. -TEST_F(FileLockingTest, UnlockOnExit) { - StartChildAndSignalLock(kExitUnlock); - - ASSERT_NE(File::FILE_OK, lock_file_.Lock()); - ExitChildCleanly(); - ASSERT_EQ(File::FILE_OK, lock_file_.Lock()); - ASSERT_EQ(File::FILE_OK, lock_file_.Unlock()); -} - -// Test that killing the process releases the lock. This should cover crashing. -// Flaky on Android (http://crbug.com/747518) -#if defined(OS_ANDROID) -#define MAYBE_UnlockOnTerminate DISABLED_UnlockOnTerminate -#else -#define MAYBE_UnlockOnTerminate UnlockOnTerminate -#endif -TEST_F(FileLockingTest, MAYBE_UnlockOnTerminate) { - // The child will wait for an exit which never arrives. - StartChildAndSignalLock(kExitUnlock); - - ASSERT_NE(File::FILE_OK, lock_file_.Lock()); - ASSERT_TRUE(TerminateMultiProcessTestChild(lock_child_, 0, true)); - ASSERT_EQ(File::FILE_OK, lock_file_.Lock()); - ASSERT_EQ(File::FILE_OK, lock_file_.Unlock()); -} diff --git a/files/file_path.cc b/files/file_path.cc deleted file mode 100644 index 14f925166..000000000 --- a/files/file_path.cc +++ /dev/null @@ -1,1342 +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/files/file_path.h" - -#include -#include - -#include "base/logging.h" -#include "base/macros.h" -#include "base/pickle.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/strings/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" - -#if defined(OS_MACOSX) -#include "base/mac/scoped_cftyperef.h" -#include "base/third_party/icu/icu_utf.h" -#endif - -#if defined(OS_WIN) -#include -#elif defined(OS_MACOSX) -#include -#endif - -namespace base { - -using StringType = FilePath::StringType; -using StringPieceType = FilePath::StringPieceType; - -namespace { - -const char* const kCommonDoubleExtensionSuffixes[] = { "gz", "z", "bz2", "bz" }; -const char* const kCommonDoubleExtensions[] = { "user.js" }; - -const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0'); - -// If this FilePath contains a drive letter specification, returns the -// position of the last character of the drive letter specification, -// otherwise returns npos. This can only be true on Windows, when a pathname -// begins with a letter followed by a colon. On other platforms, this always -// returns npos. -StringPieceType::size_type FindDriveLetter(StringPieceType path) { -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - // This is dependent on an ASCII-based character set, but that's a - // reasonable assumption. iswalpha can be too inclusive here. - if (path.length() >= 2 && path[1] == L':' && - ((path[0] >= L'A' && path[0] <= L'Z') || - (path[0] >= L'a' && path[0] <= L'z'))) { - return 1; - } -#endif // FILE_PATH_USES_DRIVE_LETTERS - return StringType::npos; -} - -#if defined(FILE_PATH_USES_DRIVE_LETTERS) -bool EqualDriveLetterCaseInsensitive(StringPieceType a, StringPieceType b) { - size_t a_letter_pos = FindDriveLetter(a); - size_t b_letter_pos = FindDriveLetter(b); - - if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos) - return a == b; - - StringPieceType a_letter(a.substr(0, a_letter_pos + 1)); - StringPieceType b_letter(b.substr(0, b_letter_pos + 1)); - if (!StartsWith(a_letter, b_letter, CompareCase::INSENSITIVE_ASCII)) - return false; - - StringPieceType a_rest(a.substr(a_letter_pos + 1)); - StringPieceType b_rest(b.substr(b_letter_pos + 1)); - return a_rest == b_rest; -} -#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) - -bool IsPathAbsolute(StringPieceType path) { -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - StringType::size_type letter = FindDriveLetter(path); - if (letter != StringType::npos) { - // Look for a separator right after the drive specification. - return path.length() > letter + 1 && - FilePath::IsSeparator(path[letter + 1]); - } - // Look for a pair of leading separators. - return path.length() > 1 && - FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]); -#else // FILE_PATH_USES_DRIVE_LETTERS - // Look for a separator in the first position. - return path.length() > 0 && FilePath::IsSeparator(path[0]); -#endif // FILE_PATH_USES_DRIVE_LETTERS -} - -bool AreAllSeparators(const StringType& input) { - for (StringType::const_iterator it = input.begin(); - it != input.end(); ++it) { - if (!FilePath::IsSeparator(*it)) - return false; - } - - return true; -} - -// Find the position of the '.' that separates the extension from the rest -// of the file name. The position is relative to BaseName(), not value(). -// Returns npos if it can't find an extension. -StringType::size_type FinalExtensionSeparatorPosition(const StringType& path) { - // Special case "." and ".." - if (path == FilePath::kCurrentDirectory || path == FilePath::kParentDirectory) - return StringType::npos; - - return path.rfind(FilePath::kExtensionSeparator); -} - -// Same as above, but allow a second extension component of up to 4 -// characters when the rightmost extension component is a common double -// extension (gz, bz2, Z). For example, foo.tar.gz or foo.tar.Z would have -// extension components of '.tar.gz' and '.tar.Z' respectively. -StringType::size_type ExtensionSeparatorPosition(const StringType& path) { - const StringType::size_type last_dot = FinalExtensionSeparatorPosition(path); - - // No extension, or the extension is the whole filename. - if (last_dot == StringType::npos || last_dot == 0U) - return last_dot; - - const StringType::size_type penultimate_dot = - path.rfind(FilePath::kExtensionSeparator, last_dot - 1); - const StringType::size_type last_separator = - path.find_last_of(FilePath::kSeparators, last_dot - 1, - FilePath::kSeparatorsLength - 1); - - if (penultimate_dot == StringType::npos || - (last_separator != StringType::npos && - penultimate_dot < last_separator)) { - return last_dot; - } - - for (size_t i = 0; i < arraysize(kCommonDoubleExtensions); ++i) { - StringType extension(path, penultimate_dot + 1); - if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensions[i])) - return penultimate_dot; - } - - StringType extension(path, last_dot + 1); - for (size_t i = 0; i < arraysize(kCommonDoubleExtensionSuffixes); ++i) { - if (LowerCaseEqualsASCII(extension, kCommonDoubleExtensionSuffixes[i])) { - if ((last_dot - penultimate_dot) <= 5U && - (last_dot - penultimate_dot) > 1U) { - return penultimate_dot; - } - } - } - - return last_dot; -} - -// Returns true if path is "", ".", or "..". -bool IsEmptyOrSpecialCase(const StringType& path) { - // Special cases "", ".", and ".." - if (path.empty() || path == FilePath::kCurrentDirectory || - path == FilePath::kParentDirectory) { - return true; - } - - return false; -} - -} // namespace - -FilePath::FilePath() = default; - -FilePath::FilePath(const FilePath& that) = default; -FilePath::FilePath(FilePath&& that) noexcept = default; - -FilePath::FilePath(StringPieceType path) { - path.CopyToString(&path_); - StringType::size_type nul_pos = path_.find(kStringTerminator); - if (nul_pos != StringType::npos) - path_.erase(nul_pos, StringType::npos); -} - -FilePath::~FilePath() = default; - -FilePath& FilePath::operator=(const FilePath& that) = default; - -FilePath& FilePath::operator=(FilePath&& that) = default; - -bool FilePath::operator==(const FilePath& that) const { -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - return EqualDriveLetterCaseInsensitive(this->path_, that.path_); -#else // defined(FILE_PATH_USES_DRIVE_LETTERS) - return path_ == that.path_; -#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) -} - -bool FilePath::operator!=(const FilePath& that) const { -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - return !EqualDriveLetterCaseInsensitive(this->path_, that.path_); -#else // defined(FILE_PATH_USES_DRIVE_LETTERS) - return path_ != that.path_; -#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) -} - -std::ostream& operator<<(std::ostream& out, const FilePath& file_path) { - return out << file_path.value(); -} - -// static -bool FilePath::IsSeparator(CharType character) { - for (size_t i = 0; i < kSeparatorsLength - 1; ++i) { - if (character == kSeparators[i]) { - return true; - } - } - - return false; -} - -void FilePath::GetComponents(std::vector* components) const { - DCHECK(components); - if (!components) - return; - components->clear(); - if (value().empty()) - return; - - std::vector ret_val; - FilePath current = *this; - FilePath base; - - // Capture path components. - while (current != current.DirName()) { - base = current.BaseName(); - if (!AreAllSeparators(base.value())) - ret_val.push_back(base.value()); - current = current.DirName(); - } - - // Capture root, if any. - base = current.BaseName(); - if (!base.value().empty() && base.value() != kCurrentDirectory) - ret_val.push_back(current.BaseName().value()); - - // Capture drive letter, if any. - FilePath dir = current.DirName(); - StringType::size_type letter = FindDriveLetter(dir.value()); - if (letter != StringType::npos) { - ret_val.push_back(StringType(dir.value(), 0, letter + 1)); - } - - *components = std::vector(ret_val.rbegin(), ret_val.rend()); -} - -bool FilePath::IsParent(const FilePath& child) const { - return AppendRelativePath(child, nullptr); -} - -bool FilePath::AppendRelativePath(const FilePath& child, - FilePath* path) const { - std::vector parent_components; - std::vector child_components; - GetComponents(&parent_components); - child.GetComponents(&child_components); - - if (parent_components.empty() || - parent_components.size() >= child_components.size()) - return false; - - std::vector::const_iterator parent_comp = - parent_components.begin(); - std::vector::const_iterator child_comp = - child_components.begin(); - -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - // Windows can access case sensitive filesystems, so component - // comparisions must be case sensitive, but drive letters are - // never case sensitive. - if ((FindDriveLetter(*parent_comp) != StringType::npos) && - (FindDriveLetter(*child_comp) != StringType::npos)) { - if (!StartsWith(*parent_comp, *child_comp, CompareCase::INSENSITIVE_ASCII)) - return false; - ++parent_comp; - ++child_comp; - } -#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) - - while (parent_comp != parent_components.end()) { - if (*parent_comp != *child_comp) - return false; - ++parent_comp; - ++child_comp; - } - - if (path != nullptr) { - for (; child_comp != child_components.end(); ++child_comp) { - *path = path->Append(*child_comp); - } - } - return true; -} - -// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't -// guaranteed to not modify their input strings, and in fact are implemented -// differently in this regard on different platforms. Don't use them, but -// adhere to their behavior. -FilePath FilePath::DirName() const { - FilePath new_path(path_); - new_path.StripTrailingSeparatorsInternal(); - - // The drive letter, if any, always needs to remain in the output. If there - // is no drive letter, as will always be the case on platforms which do not - // support drive letters, letter will be npos, or -1, so the comparisons and - // resizes below using letter will still be valid. - StringType::size_type letter = FindDriveLetter(new_path.path_); - - StringType::size_type last_separator = - new_path.path_.find_last_of(kSeparators, StringType::npos, - kSeparatorsLength - 1); - if (last_separator == StringType::npos) { - // path_ is in the current directory. - new_path.path_.resize(letter + 1); - } else if (last_separator == letter + 1) { - // path_ is in the root directory. - new_path.path_.resize(letter + 2); - } else if (last_separator == letter + 2 && - IsSeparator(new_path.path_[letter + 1])) { - // path_ is in "//" (possibly with a drive letter); leave the double - // separator intact indicating alternate root. - new_path.path_.resize(letter + 3); - } else if (last_separator != 0) { - // path_ is somewhere else, trim the basename. - new_path.path_.resize(last_separator); - } - - new_path.StripTrailingSeparatorsInternal(); - if (!new_path.path_.length()) - new_path.path_ = kCurrentDirectory; - - return new_path; -} - -FilePath FilePath::BaseName() const { - FilePath new_path(path_); - new_path.StripTrailingSeparatorsInternal(); - - // The drive letter, if any, is always stripped. - StringType::size_type letter = FindDriveLetter(new_path.path_); - if (letter != StringType::npos) { - new_path.path_.erase(0, letter + 1); - } - - // Keep everything after the final separator, but if the pathname is only - // one character and it's a separator, leave it alone. - StringType::size_type last_separator = - new_path.path_.find_last_of(kSeparators, StringType::npos, - kSeparatorsLength - 1); - if (last_separator != StringType::npos && - last_separator < new_path.path_.length() - 1) { - new_path.path_.erase(0, last_separator + 1); - } - - return new_path; -} - -StringType FilePath::Extension() const { - FilePath base(BaseName()); - const StringType::size_type dot = ExtensionSeparatorPosition(base.path_); - if (dot == StringType::npos) - return StringType(); - - return base.path_.substr(dot, StringType::npos); -} - -StringType FilePath::FinalExtension() const { - FilePath base(BaseName()); - const StringType::size_type dot = FinalExtensionSeparatorPosition(base.path_); - if (dot == StringType::npos) - return StringType(); - - return base.path_.substr(dot, StringType::npos); -} - -FilePath FilePath::RemoveExtension() const { - if (Extension().empty()) - return *this; - - const StringType::size_type dot = ExtensionSeparatorPosition(path_); - if (dot == StringType::npos) - return *this; - - return FilePath(path_.substr(0, dot)); -} - -FilePath FilePath::RemoveFinalExtension() const { - if (FinalExtension().empty()) - return *this; - - const StringType::size_type dot = FinalExtensionSeparatorPosition(path_); - if (dot == StringType::npos) - return *this; - - return FilePath(path_.substr(0, dot)); -} - -FilePath FilePath::InsertBeforeExtension(StringPieceType suffix) const { - if (suffix.empty()) - return FilePath(path_); - - if (IsEmptyOrSpecialCase(BaseName().value())) - return FilePath(); - - StringType ext = Extension(); - StringType ret = RemoveExtension().value(); - suffix.AppendToString(&ret); - ret.append(ext); - return FilePath(ret); -} - -FilePath FilePath::InsertBeforeExtensionASCII(StringPiece suffix) - const { - DCHECK(IsStringASCII(suffix)); -#if defined(OS_WIN) - return InsertBeforeExtension(ASCIIToUTF16(suffix)); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - return InsertBeforeExtension(suffix); -#endif -} - -FilePath FilePath::AddExtension(StringPieceType extension) const { - if (IsEmptyOrSpecialCase(BaseName().value())) - return FilePath(); - - // If the new extension is "" or ".", then just return the current FilePath. - if (extension.empty() || - (extension.size() == 1 && extension[0] == kExtensionSeparator)) - return *this; - - StringType str = path_; - if (extension[0] != kExtensionSeparator && - *(str.end() - 1) != kExtensionSeparator) { - str.append(1, kExtensionSeparator); - } - extension.AppendToString(&str); - return FilePath(str); -} - -FilePath FilePath::ReplaceExtension(StringPieceType extension) const { - if (IsEmptyOrSpecialCase(BaseName().value())) - return FilePath(); - - FilePath no_ext = RemoveExtension(); - // If the new extension is "" or ".", then just remove the current extension. - if (extension.empty() || - (extension.size() == 1 && extension[0] == kExtensionSeparator)) - return no_ext; - - StringType str = no_ext.value(); - if (extension[0] != kExtensionSeparator) - str.append(1, kExtensionSeparator); - extension.AppendToString(&str); - return FilePath(str); -} - -bool FilePath::MatchesExtension(StringPieceType extension) const { - DCHECK(extension.empty() || extension[0] == kExtensionSeparator); - - StringType current_extension = Extension(); - - if (current_extension.length() != extension.length()) - return false; - - return FilePath::CompareEqualIgnoreCase(extension, current_extension); -} - -FilePath FilePath::Append(StringPieceType component) const { - StringPieceType appended = component; - StringType without_nuls; - - StringType::size_type nul_pos = component.find(kStringTerminator); - if (nul_pos != StringPieceType::npos) { - component.substr(0, nul_pos).CopyToString(&without_nuls); - appended = StringPieceType(without_nuls); - } - - DCHECK(!IsPathAbsolute(appended)); - - if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) { - // Append normally doesn't do any normalization, but as a special case, - // when appending to kCurrentDirectory, just return a new path for the - // component argument. Appending component to kCurrentDirectory would - // serve no purpose other than needlessly lengthening the path, and - // it's likely in practice to wind up with FilePath objects containing - // only kCurrentDirectory when calling DirName on a single relative path - // component. - return FilePath(appended); - } - - FilePath new_path(path_); - new_path.StripTrailingSeparatorsInternal(); - - // Don't append a separator if the path is empty (indicating the current - // directory) or if the path component is empty (indicating nothing to - // append). - if (!appended.empty() && !new_path.path_.empty()) { - // Don't append a separator if the path still ends with a trailing - // separator after stripping (indicating the root directory). - if (!IsSeparator(new_path.path_.back())) { - // Don't append a separator if the path is just a drive letter. - if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { - new_path.path_.append(1, kSeparators[0]); - } - } - } - - appended.AppendToString(&new_path.path_); - return new_path; -} - -FilePath FilePath::Append(const FilePath& component) const { - return Append(component.value()); -} - -FilePath FilePath::AppendASCII(StringPiece component) const { - DCHECK(base::IsStringASCII(component)); -#if defined(OS_WIN) - return Append(ASCIIToUTF16(component)); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - return Append(component); -#endif -} - -bool FilePath::IsAbsolute() const { - return IsPathAbsolute(path_); -} - -bool FilePath::EndsWithSeparator() const { - if (empty()) - return false; - return IsSeparator(path_.back()); -} - -FilePath FilePath::AsEndingWithSeparator() const { - if (EndsWithSeparator() || path_.empty()) - return *this; - - StringType path_str; - path_str.reserve(path_.length() + 1); // Only allocate string once. - - path_str = path_; - path_str.append(&kSeparators[0], 1); - return FilePath(path_str); -} - -FilePath FilePath::StripTrailingSeparators() const { - FilePath new_path(path_); - new_path.StripTrailingSeparatorsInternal(); - - return new_path; -} - -bool FilePath::ReferencesParent() const { - if (path_.find(kParentDirectory) == StringType::npos) { - // GetComponents is quite expensive, so avoid calling it in the majority - // of cases where there isn't a kParentDirectory anywhere in the path. - return false; - } - - std::vector components; - GetComponents(&components); - - std::vector::const_iterator it = components.begin(); - for (; it != components.end(); ++it) { - const StringType& component = *it; - // Windows has odd, undocumented behavior with path components containing - // only whitespace and . characters. So, if all we see is . and - // whitespace, then we treat any .. sequence as referencing parent. - // For simplicity we enforce this on all platforms. - if (component.find_first_not_of(FILE_PATH_LITERAL(". \n\r\t")) == - std::string::npos && - component.find(kParentDirectory) != std::string::npos) { - return true; - } - } - return false; -} - -#if defined(OS_WIN) - -string16 FilePath::LossyDisplayName() const { - return path_; -} - -std::string FilePath::MaybeAsASCII() const { - if (base::IsStringASCII(path_)) - return UTF16ToASCII(path_); - return std::string(); -} - -std::string FilePath::AsUTF8Unsafe() const { - return WideToUTF8(value()); -} - -string16 FilePath::AsUTF16Unsafe() const { - return value(); -} - -// static -FilePath FilePath::FromUTF8Unsafe(StringPiece utf8) { - return FilePath(UTF8ToWide(utf8)); -} - -// static -FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) { - return FilePath(utf16); -} - -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - -// See file_path.h for a discussion of the encoding of paths on POSIX -// platforms. These encoding conversion functions are not quite correct. - -string16 FilePath::LossyDisplayName() const { - return WideToUTF16(SysNativeMBToWide(path_)); -} - -std::string FilePath::MaybeAsASCII() const { - if (base::IsStringASCII(path_)) - return path_; - return std::string(); -} - -std::string FilePath::AsUTF8Unsafe() const { -#if defined(SYSTEM_NATIVE_UTF8) - return value(); -#else - return WideToUTF8(SysNativeMBToWide(value())); -#endif -} - -string16 FilePath::AsUTF16Unsafe() const { -#if defined(SYSTEM_NATIVE_UTF8) - return UTF8ToUTF16(value()); -#else - return WideToUTF16(SysNativeMBToWide(value())); -#endif -} - -// static -FilePath FilePath::FromUTF8Unsafe(StringPiece utf8) { -#if defined(SYSTEM_NATIVE_UTF8) - return FilePath(utf8); -#else - return FilePath(SysWideToNativeMB(UTF8ToWide(utf8))); -#endif -} - -// static -FilePath FilePath::FromUTF16Unsafe(StringPiece16 utf16) { -#if defined(SYSTEM_NATIVE_UTF8) - return FilePath(UTF16ToUTF8(utf16)); -#else - return FilePath(SysWideToNativeMB(UTF16ToWide(utf16.as_string()))); -#endif -} - -#endif // defined(OS_WIN) - -void FilePath::WriteToPickle(Pickle* pickle) const { -#if defined(OS_WIN) - pickle->WriteString16(path_); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - pickle->WriteString(path_); -#else -#error Unsupported platform -#endif -} - -bool FilePath::ReadFromPickle(PickleIterator* iter) { -#if defined(OS_WIN) - if (!iter->ReadString16(&path_)) - return false; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - if (!iter->ReadString(&path_)) - return false; -#else -#error Unsupported platform -#endif - - if (path_.find(kStringTerminator) != StringType::npos) - return false; - - return true; -} - -#if defined(OS_WIN) -// Windows specific implementation of file string comparisons. - -int FilePath::CompareIgnoreCase(StringPieceType string1, - StringPieceType string2) { - static decltype(::CharUpperW)* const char_upper_api = - reinterpret_cast( - ::GetProcAddress(::GetModuleHandle(L"user32.dll"), "CharUpperW")); - CHECK(char_upper_api); - // Perform character-wise upper case comparison rather than using the - // fully Unicode-aware CompareString(). For details see: - // http://blogs.msdn.com/michkap/archive/2005/10/17/481600.aspx - StringPieceType::const_iterator i1 = string1.begin(); - StringPieceType::const_iterator i2 = string2.begin(); - StringPieceType::const_iterator string1end = string1.end(); - StringPieceType::const_iterator string2end = string2.end(); - for ( ; i1 != string1end && i2 != string2end; ++i1, ++i2) { - wchar_t c1 = - (wchar_t)LOWORD(char_upper_api((LPWSTR)(DWORD_PTR)MAKELONG(*i1, 0))); - wchar_t c2 = - (wchar_t)LOWORD(char_upper_api((LPWSTR)(DWORD_PTR)MAKELONG(*i2, 0))); - if (c1 < c2) - return -1; - if (c1 > c2) - return 1; - } - if (i1 != string1end) - return 1; - if (i2 != string2end) - return -1; - return 0; -} - -#elif defined(OS_MACOSX) -// Mac OS X specific implementation of file string comparisons. - -// cf. http://developer.apple.com/mac/library/technotes/tn/tn1150.html#UnicodeSubtleties -// -// "When using CreateTextEncoding to create a text encoding, you should set -// the TextEncodingBase to kTextEncodingUnicodeV2_0, set the -// TextEncodingVariant to kUnicodeCanonicalDecompVariant, and set the -// TextEncodingFormat to kUnicode16BitFormat. Using these values ensures that -// the Unicode will be in the same form as on an HFS Plus volume, even as the -// Unicode standard evolves." -// -// Another technical article for X 10.4 updates this: one should use -// the new (unambiguous) kUnicodeHFSPlusDecompVariant. -// cf. http://developer.apple.com/mac/library/releasenotes/TextFonts/RN-TEC/index.html -// -// This implementation uses CFStringGetFileSystemRepresentation() to get the -// decomposed form, and an adapted version of the FastUnicodeCompare as -// described in the tech note to compare the strings. - -// Character conversion table for FastUnicodeCompare() -// -// The lower case table consists of a 256-entry high-byte table followed by -// some number of 256-entry subtables. The high-byte table contains either an -// offset to the subtable for characters with that high byte or zero, which -// means that there are no case mappings or ignored characters in that block. -// Ignored characters are mapped to zero. -// -// cf. downloadable file linked in -// http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm - -namespace { - -const UInt16 lower_case_table[] = { - // High-byte indices ( == 0 iff no case mapping and no ignorables ) - - /* 0 */ 0x0100, 0x0200, 0x0000, 0x0300, 0x0400, 0x0500, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* 1 */ 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* 2 */ 0x0700, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* 3 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* 4 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* 5 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* 6 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* 7 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* 8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* 9 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* A */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* B */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* C */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* D */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* E */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* F */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0900, 0x0A00, - - // Table 1 (for high byte 0x00) - - /* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - /* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, - /* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - /* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, - /* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, - /* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - /* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, - /* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, - /* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, - /* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, - /* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, - /* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, - 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, - /* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7, - 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, - /* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, - 0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF, - /* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, - 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, - /* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, - 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF, - - // Table 2 (for high byte 0x01) - - /* 0 */ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, - 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F, - /* 1 */ 0x0111, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117, - 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, - /* 2 */ 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0127, 0x0127, - 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F, - /* 3 */ 0x0130, 0x0131, 0x0133, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137, - 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x0140, - /* 4 */ 0x0140, 0x0142, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147, - 0x0148, 0x0149, 0x014B, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F, - /* 5 */ 0x0150, 0x0151, 0x0153, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157, - 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F, - /* 6 */ 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0167, 0x0167, - 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F, - /* 7 */ 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177, - 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F, - /* 8 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, - 0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259, - /* 9 */ 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, - 0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275, - /* A */ 0x01A0, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8, - 0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01AF, - /* B */ 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292, - 0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF, - /* C */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9, - 0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CD, 0x01CE, 0x01CF, - /* D */ 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7, - 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF, - /* E */ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E5, 0x01E5, 0x01E6, 0x01E7, - 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF, - /* F */ 0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7, - 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF, - - // Table 3 (for high byte 0x03) - - /* 0 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, - 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, - /* 1 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, - 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, - /* 2 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, - 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, - /* 3 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, - 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, - /* 4 */ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, - 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, - /* 5 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, - 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, - /* 6 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, - 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, - /* 7 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, - 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F, - /* 8 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387, - 0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F, - /* 9 */ 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, - 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, - /* A */ 0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, - 0x03C8, 0x03C9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, - /* B */ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, - 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, - /* C */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, - 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF, - /* D */ 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7, - 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF, - /* E */ 0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7, - 0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF, - /* F */ 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7, - 0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF, - - // Table 4 (for high byte 0x04) - - /* 0 */ 0x0400, 0x0401, 0x0452, 0x0403, 0x0454, 0x0455, 0x0456, 0x0407, - 0x0458, 0x0459, 0x045A, 0x045B, 0x040C, 0x040D, 0x040E, 0x045F, - /* 1 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, - 0x0438, 0x0419, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, - /* 2 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, - 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, - /* 3 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, - 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, - /* 4 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, - 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, - /* 5 */ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, - 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F, - /* 6 */ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467, - 0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F, - /* 7 */ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0476, 0x0477, - 0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F, - /* 8 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, - 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F, - /* 9 */ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497, - 0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F, - /* A */ 0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7, - 0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF, - /* B */ 0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7, - 0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF, - /* C */ 0x04C0, 0x04C1, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8, - 0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF, - /* D */ 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7, - 0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF, - /* E */ 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7, - 0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF, - /* F */ 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7, - 0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF, - - // Table 5 (for high byte 0x05) - - /* 0 */ 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, - 0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F, - /* 1 */ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517, - 0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F, - /* 2 */ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, - 0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F, - /* 3 */ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, - 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, - /* 4 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, - 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, - /* 5 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557, - 0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F, - /* 6 */ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567, - 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F, - /* 7 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577, - 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F, - /* 8 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587, - 0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F, - /* 9 */ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597, - 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F, - /* A */ 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7, - 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF, - /* B */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, - 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, - /* C */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7, - 0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF, - /* D */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, - 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, - /* E */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, - 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF, - /* F */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7, - 0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF, - - // Table 6 (for high byte 0x10) - - /* 0 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, - 0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F, - /* 1 */ 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017, - 0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F, - /* 2 */ 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, - 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F, - /* 3 */ 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037, - 0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F, - /* 4 */ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, - 0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F, - /* 5 */ 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057, - 0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F, - /* 6 */ 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067, - 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F, - /* 7 */ 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077, - 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F, - /* 8 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087, - 0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F, - /* 9 */ 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, - 0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F, - /* A */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, - 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, - /* B */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, - 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, - /* C */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10C6, 0x10C7, - 0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF, - /* D */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7, - 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF, - /* E */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, - 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF, - /* F */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7, - 0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF, - - // Table 7 (for high byte 0x20) - - /* 0 */ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, - 0x2008, 0x2009, 0x200A, 0x200B, 0x0000, 0x0000, 0x0000, 0x0000, - /* 1 */ 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x2017, - 0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F, - /* 2 */ 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026, 0x2027, - 0x2028, 0x2029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x202F, - /* 3 */ 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037, - 0x2038, 0x2039, 0x203A, 0x203B, 0x203C, 0x203D, 0x203E, 0x203F, - /* 4 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x2047, - 0x2048, 0x2049, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F, - /* 5 */ 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2057, - 0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F, - /* 6 */ 0x2060, 0x2061, 0x2062, 0x2063, 0x2064, 0x2065, 0x2066, 0x2067, - 0x2068, 0x2069, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - /* 7 */ 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077, - 0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F, - /* 8 */ 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087, - 0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x208F, - /* 9 */ 0x2090, 0x2091, 0x2092, 0x2093, 0x2094, 0x2095, 0x2096, 0x2097, - 0x2098, 0x2099, 0x209A, 0x209B, 0x209C, 0x209D, 0x209E, 0x209F, - /* A */ 0x20A0, 0x20A1, 0x20A2, 0x20A3, 0x20A4, 0x20A5, 0x20A6, 0x20A7, - 0x20A8, 0x20A9, 0x20AA, 0x20AB, 0x20AC, 0x20AD, 0x20AE, 0x20AF, - /* B */ 0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7, - 0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF, - /* C */ 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7, - 0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF, - /* D */ 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7, - 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF, - /* E */ 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7, - 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF, - /* F */ 0x20F0, 0x20F1, 0x20F2, 0x20F3, 0x20F4, 0x20F5, 0x20F6, 0x20F7, - 0x20F8, 0x20F9, 0x20FA, 0x20FB, 0x20FC, 0x20FD, 0x20FE, 0x20FF, - - // Table 8 (for high byte 0x21) - - /* 0 */ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107, - 0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F, - /* 1 */ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117, - 0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F, - /* 2 */ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127, - 0x2128, 0x2129, 0x212A, 0x212B, 0x212C, 0x212D, 0x212E, 0x212F, - /* 3 */ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137, - 0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F, - /* 4 */ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147, - 0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F, - /* 5 */ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157, - 0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F, - /* 6 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, - 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, - /* 7 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, - 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, - /* 8 */ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187, - 0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F, - /* 9 */ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197, - 0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F, - /* A */ 0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7, - 0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF, - /* B */ 0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7, - 0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF, - /* C */ 0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7, - 0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF, - /* D */ 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7, - 0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF, - /* E */ 0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7, - 0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF, - /* F */ 0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7, - 0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF, - - // Table 9 (for high byte 0xFE) - - /* 0 */ 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07, - 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F, - /* 1 */ 0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16, 0xFE17, - 0xFE18, 0xFE19, 0xFE1A, 0xFE1B, 0xFE1C, 0xFE1D, 0xFE1E, 0xFE1F, - /* 2 */ 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27, - 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F, - /* 3 */ 0xFE30, 0xFE31, 0xFE32, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37, - 0xFE38, 0xFE39, 0xFE3A, 0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F, - /* 4 */ 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0xFE45, 0xFE46, 0xFE47, - 0xFE48, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F, - /* 5 */ 0xFE50, 0xFE51, 0xFE52, 0xFE53, 0xFE54, 0xFE55, 0xFE56, 0xFE57, - 0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F, - /* 6 */ 0xFE60, 0xFE61, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE67, - 0xFE68, 0xFE69, 0xFE6A, 0xFE6B, 0xFE6C, 0xFE6D, 0xFE6E, 0xFE6F, - /* 7 */ 0xFE70, 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE75, 0xFE76, 0xFE77, - 0xFE78, 0xFE79, 0xFE7A, 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F, - /* 8 */ 0xFE80, 0xFE81, 0xFE82, 0xFE83, 0xFE84, 0xFE85, 0xFE86, 0xFE87, - 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, 0xFE8D, 0xFE8E, 0xFE8F, - /* 9 */ 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, 0xFE96, 0xFE97, - 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, 0xFE9F, - /* A */ 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7, - 0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF, - /* B */ 0xFEB0, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7, - 0xFEB8, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF, - /* C */ 0xFEC0, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7, - 0xFEC8, 0xFEC9, 0xFECA, 0xFECB, 0xFECC, 0xFECD, 0xFECE, 0xFECF, - /* D */ 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, 0xFED5, 0xFED6, 0xFED7, - 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, 0xFEDE, 0xFEDF, - /* E */ 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, 0xFEE7, - 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF, - /* F */ 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7, - 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0xFEFD, 0xFEFE, 0x0000, - - // Table 10 (for high byte 0xFF) - - /* 0 */ 0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07, - 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F, - /* 1 */ 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, - 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F, - /* 2 */ 0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, - 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, - /* 3 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, - 0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F, - /* 4 */ 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, - 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, - /* 5 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, - 0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F, - /* 6 */ 0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67, - 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F, - /* 7 */ 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77, - 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F, - /* 8 */ 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87, - 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F, - /* 9 */ 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97, - 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F, - /* A */ 0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7, - 0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF, - /* B */ 0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7, - 0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF, - /* C */ 0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7, - 0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF, - /* D */ 0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7, - 0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF, - /* E */ 0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7, - 0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF, - /* F */ 0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7, - 0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, -}; - -// Returns the next non-ignorable codepoint within string starting from the -// position indicated by index, or zero if there are no more. -// The passed-in index is automatically advanced as the characters in the input -// HFS-decomposed UTF-8 strings are read. -inline int HFSReadNextNonIgnorableCodepoint(const char* string, - int length, - int* index) { - int codepoint = 0; - while (*index < length && codepoint == 0) { - // CBU8_NEXT returns a value < 0 in error cases. For purposes of string - // comparison, we just use that value and flag it with DCHECK. - CBU8_NEXT(string, *index, length, codepoint); - DCHECK_GT(codepoint, 0); - if (codepoint > 0) { - // Check if there is a subtable for this upper byte. - int lookup_offset = lower_case_table[codepoint >> 8]; - if (lookup_offset != 0) - codepoint = lower_case_table[lookup_offset + (codepoint & 0x00FF)]; - // Note: codepoint1 may be again 0 at this point if the character was - // an ignorable. - } - } - return codepoint; -} - -} // namespace - -// Special UTF-8 version of FastUnicodeCompare. Cf: -// http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm -// The input strings must be in the special HFS decomposed form. -int FilePath::HFSFastUnicodeCompare(StringPieceType string1, - StringPieceType string2) { - int length1 = string1.length(); - int length2 = string2.length(); - int index1 = 0; - int index2 = 0; - - for (;;) { - int codepoint1 = HFSReadNextNonIgnorableCodepoint(string1.data(), - length1, - &index1); - int codepoint2 = HFSReadNextNonIgnorableCodepoint(string2.data(), - length2, - &index2); - if (codepoint1 != codepoint2) - return (codepoint1 < codepoint2) ? -1 : 1; - if (codepoint1 == 0) { - DCHECK_EQ(index1, length1); - DCHECK_EQ(index2, length2); - return 0; - } - } -} - -StringType FilePath::GetHFSDecomposedForm(StringPieceType string) { - StringType result; - ScopedCFTypeRef cfstring( - CFStringCreateWithBytesNoCopy( - NULL, - reinterpret_cast(string.data()), - string.length(), - kCFStringEncodingUTF8, - false, - kCFAllocatorNull)); - if (cfstring) { - // Query the maximum length needed to store the result. In most cases this - // will overestimate the required space. The return value also already - // includes the space needed for a terminating 0. - CFIndex length = CFStringGetMaximumSizeOfFileSystemRepresentation(cfstring); - DCHECK_GT(length, 0); // should be at least 1 for the 0-terminator. - // Reserve enough space for CFStringGetFileSystemRepresentation to write - // into. Also set the length to the maximum so that we can shrink it later. - // (Increasing rather than decreasing it would clobber the string contents!) - result.reserve(length); - result.resize(length - 1); - Boolean success = CFStringGetFileSystemRepresentation(cfstring, - &result[0], - length); - if (success) { - // Reduce result.length() to actual string length. - result.resize(strlen(result.c_str())); - } else { - // An error occurred -> clear result. - result.clear(); - } - } - return result; -} - -int FilePath::CompareIgnoreCase(StringPieceType string1, - StringPieceType string2) { - // Quick checks for empty strings - these speed things up a bit and make the - // following code cleaner. - if (string1.empty()) - return string2.empty() ? 0 : -1; - if (string2.empty()) - return 1; - - StringType hfs1 = GetHFSDecomposedForm(string1); - StringType hfs2 = GetHFSDecomposedForm(string2); - - // GetHFSDecomposedForm() returns an empty string in an error case. - if (hfs1.empty() || hfs2.empty()) { - NOTREACHED(); - ScopedCFTypeRef cfstring1( - CFStringCreateWithBytesNoCopy( - NULL, - reinterpret_cast(string1.data()), - string1.length(), - kCFStringEncodingUTF8, - false, - kCFAllocatorNull)); - ScopedCFTypeRef cfstring2( - CFStringCreateWithBytesNoCopy( - NULL, - reinterpret_cast(string2.data()), - string2.length(), - kCFStringEncodingUTF8, - false, - kCFAllocatorNull)); - return CFStringCompare(cfstring1, - cfstring2, - kCFCompareCaseInsensitive); - } - - return HFSFastUnicodeCompare(hfs1, hfs2); -} - -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - -// Generic Posix system comparisons. -int FilePath::CompareIgnoreCase(StringPieceType string1, - StringPieceType string2) { - // Specifically need null termianted strings for this API call. - int comparison = strcasecmp(string1.as_string().c_str(), - string2.as_string().c_str()); - if (comparison < 0) - return -1; - if (comparison > 0) - return 1; - return 0; -} - -#endif // OS versions of CompareIgnoreCase() - - -void FilePath::StripTrailingSeparatorsInternal() { - // If there is no drive letter, start will be 1, which will prevent stripping - // the leading separator if there is only one separator. If there is a drive - // letter, start will be set appropriately to prevent stripping the first - // separator following the drive letter, if a separator immediately follows - // the drive letter. - StringType::size_type start = FindDriveLetter(path_) + 2; - - StringType::size_type last_stripped = StringType::npos; - for (StringType::size_type pos = path_.length(); - pos > start && IsSeparator(path_[pos - 1]); - --pos) { - // If the string only has two separators and they're at the beginning, - // don't strip them, unless the string began with more than two separators. - if (pos != start + 1 || last_stripped == start + 2 || - !IsSeparator(path_[start - 1])) { - path_.resize(pos - 1); - last_stripped = pos; - } - } -} - -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 = 0; i < kSeparatorsLength; ++i) { - std::replace(copy.begin(), copy.end(), kSeparators[i], separator); - } - return FilePath(copy); -#else - return *this; -#endif -} - -#if defined(OS_ANDROID) -bool FilePath::IsContentUri() const { - return StartsWith(path_, "content://", base::CompareCase::INSENSITIVE_ASCII); -} -#endif - -} // namespace base diff --git a/files/file_path.h b/files/file_path.h deleted file mode 100644 index 2dc15f9d0..000000000 --- a/files/file_path.h +++ /dev/null @@ -1,481 +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. - -// FilePath is a container for pathnames stored in a platform's native string -// type, providing containers for manipulation in according with the -// platform's conventions for pathnames. It supports the following path -// types: -// -// POSIX Windows -// --------------- ---------------------------------- -// Fundamental type char[] wchar_t[] -// Encoding unspecified* UTF-16 -// Separator / \, tolerant of / -// Drive letters no case-insensitive A-Z followed by : -// Alternate root // (surprise!) \\, for UNC paths -// -// * The encoding need not be specified on POSIX systems, although some -// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8. -// Chrome OS also uses UTF-8. -// Linux does not specify an encoding, but in practice, the locale's -// character set may be used. -// -// For more arcane bits of path trivia, see below. -// -// FilePath objects are intended to be used anywhere paths are. An -// application may pass FilePath objects around internally, masking the -// underlying differences between systems, only differing in implementation -// where interfacing directly with the system. For example, a single -// OpenFile(const FilePath &) function may be made available, allowing all -// callers to operate without regard to the underlying implementation. On -// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might -// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This -// allows each platform to pass pathnames around without requiring conversions -// between encodings, which has an impact on performance, but more imporantly, -// has an impact on correctness on platforms that do not have well-defined -// encodings for pathnames. -// -// Several methods are available to perform common operations on a FilePath -// object, such as determining the parent directory (DirName), isolating the -// final path component (BaseName), and appending a relative pathname string -// to an existing FilePath object (Append). These methods are highly -// recommended over attempting to split and concatenate strings directly. -// These methods are based purely on string manipulation and knowledge of -// platform-specific pathname conventions, and do not consult the filesystem -// at all, making them safe to use without fear of blocking on I/O operations. -// These methods do not function as mutators but instead return distinct -// instances of FilePath objects, and are therefore safe to use on const -// objects. The objects themselves are safe to share between threads. -// -// To aid in initialization of FilePath objects from string literals, a -// FILE_PATH_LITERAL macro is provided, which accounts for the difference -// between char[]-based pathnames on POSIX systems and wchar_t[]-based -// pathnames on Windows. -// -// As a precaution against premature truncation, paths can't contain NULs. -// -// Because a FilePath object should not be instantiated at the global scope, -// instead, use a FilePath::CharType[] and initialize it with -// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the -// character array. Example: -// -// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt"); -// | -// | void Function() { -// | FilePath log_file_path(kLogFileName); -// | [...] -// | } -// -// WARNING: FilePaths should ALWAYS be displayed with LTR directionality, even -// when the UI language is RTL. This means you always need to pass filepaths -// through base::i18n::WrapPathWithLTRFormatting() before displaying it in the -// RTL UI. -// -// This is a very common source of bugs, please try to keep this in mind. -// -// ARCANE BITS OF PATH TRIVIA -// -// - A double leading slash is actually part of the POSIX standard. Systems -// are allowed to treat // as an alternate root, as Windows does for UNC -// (network share) paths. Most POSIX systems don't do anything special -// with two leading slashes, but FilePath handles this case properly -// in case it ever comes across such a system. FilePath needs this support -// for Windows UNC paths, anyway. -// References: -// The Open Group Base Specifications Issue 7, sections 3.267 ("Pathname") -// and 4.12 ("Pathname Resolution"), available at: -// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267 -// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12 -// -// - Windows treats c:\\ the same way it treats \\. This was intended to -// allow older applications that require drive letters to support UNC paths -// like \\server\share\path, by permitting c:\\server\share\path as an -// equivalent. Since the OS treats these paths specially, FilePath needs -// to do the same. Since Windows can use either / or \ as the separator, -// FilePath treats c://, c:\\, //, and \\ all equivalently. -// Reference: -// The Old New Thing, "Why is a drive letter permitted in front of UNC -// paths (sometimes)?", available at: -// http://blogs.msdn.com/oldnewthing/archive/2005/11/22/495740.aspx - -#ifndef BASE_FILES_FILE_PATH_H_ -#define BASE_FILES_FILE_PATH_H_ - -#include - -#include -#include -#include - -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/strings/string16.h" -#include "base/strings/string_piece.h" -#include "build/build_config.h" - -// Windows-style drive letter support and pathname separator characters can be -// enabled and disabled independently, to aid testing. These #defines are -// here so that the same setting can be used in both the implementation and -// in the unit test. -#if defined(OS_WIN) -#define FILE_PATH_USES_DRIVE_LETTERS -#define FILE_PATH_USES_WIN_SEPARATORS -#endif // OS_WIN - -// To print path names portably use PRIsFP (based on PRIuS and friends from -// C99 and format_macros.h) like this: -// base::StringPrintf("Path is %" PRIsFP ".\n", path.value().c_str()); -#if defined(OS_WIN) -#define PRIsFP "ls" -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#define PRIsFP "s" -#endif // OS_WIN - -namespace base { - -class Pickle; -class PickleIterator; - -// An abstraction to isolate users from the differences between native -// pathnames on different platforms. -class BASE_EXPORT FilePath { - public: -#if defined(OS_WIN) - // On Windows, for Unicode-aware applications, native pathnames are wchar_t - // arrays encoded in UTF-16. - typedef std::wstring StringType; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // On most platforms, native pathnames are char arrays, and the encoding - // may or may not be specified. On Mac OS X, native pathnames are encoded - // in UTF-8. - typedef std::string StringType; -#endif // OS_WIN - - typedef BasicStringPiece StringPieceType; - typedef StringType::value_type CharType; - - // Null-terminated array of separators used to separate components in - // hierarchical paths. Each character in this array is a valid separator, - // but kSeparators[0] is treated as the canonical separator and will be used - // when composing pathnames. - static const CharType kSeparators[]; - - // arraysize(kSeparators). - static const size_t kSeparatorsLength; - - // A special path component meaning "this directory." - static const CharType kCurrentDirectory[]; - - // A special path component meaning "the parent directory." - static const CharType kParentDirectory[]; - - // The character used to identify a file extension. - static const CharType kExtensionSeparator; - - FilePath(); - FilePath(const FilePath& that); - explicit FilePath(StringPieceType path); - ~FilePath(); - FilePath& operator=(const FilePath& that); - - // Constructs FilePath with the contents of |that|, which is left in valid but - // unspecified state. - FilePath(FilePath&& that) noexcept; - // Replaces the contents with those of |that|, which is left in valid but - // unspecified state. - FilePath& operator=(FilePath&& that); - - bool operator==(const FilePath& that) const; - - bool operator!=(const FilePath& that) const; - - // Required for some STL containers and operations - bool operator<(const FilePath& that) const { - return path_ < that.path_; - } - - const StringType& value() const { return path_; } - - bool empty() const { return path_.empty(); } - - void clear() { path_.clear(); } - - // Returns true if |character| is in kSeparators. - static bool IsSeparator(CharType character); - - // 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* components) const; - - // Returns true if this FilePath is a strict parent of the |child|. Absolute - // and relative paths are accepted i.e. is /foo parent to /foo/bar and - // is foo parent to foo/bar. Does not convert paths to absolute, follow - // symlinks or directory navigation (e.g. ".."). A path is *NOT* its own - // parent. - bool IsParent(const FilePath& child) const; - - // If IsParent(child) holds, appends to path (if non-NULL) the - // relative path to child and returns true. For example, if parent - // holds "/Users/johndoe/Library/Application Support", child holds - // "/Users/johndoe/Library/Application Support/Google/Chrome/Default", and - // *path holds "/Users/johndoe/Library/Caches", then after - // parent.AppendRelativePath(child, path) is called *path will hold - // "/Users/johndoe/Library/Caches/Google/Chrome/Default". Otherwise, - // returns false. - bool AppendRelativePath(const FilePath& child, FilePath* path) const; - - // Returns a FilePath corresponding to the directory containing the path - // named by this object, stripping away the file component. If this object - // only contains one component, returns a FilePath identifying - // kCurrentDirectory. If this object already refers to the root directory, - // returns a FilePath identifying the root directory. Please note that this - // doesn't resolve directory navigation, e.g. the result for "../a" is "..". - FilePath DirName() const WARN_UNUSED_RESULT; - - // Returns a FilePath corresponding to the last path component of this - // object, either a file or a directory. If this object already refers to - // the root directory, returns a FilePath identifying the root directory; - // this is the only situation in which BaseName will return an absolute path. - FilePath BaseName() const WARN_UNUSED_RESULT; - - // Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if - // the file has no extension. If non-empty, Extension() will always start - // with precisely one ".". The following code should always work regardless - // of the value of path. For common double-extensions like .tar.gz and - // .user.js, this method returns the combined extension. For a single - // component, use FinalExtension(). - // new_path = path.RemoveExtension().value().append(path.Extension()); - // ASSERT(new_path == path.value()); - // NOTE: this is different from the original file_util implementation which - // returned the extension without a leading "." ("jpg" instead of ".jpg") - StringType Extension() const WARN_UNUSED_RESULT; - - // Returns the path's file extension, as in Extension(), but will - // never return a double extension. - // - // 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 base::GetUniquePathNumber(). - StringType FinalExtension() const WARN_UNUSED_RESULT; - - // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg" - // NOTE: this is slightly different from the similar file_util implementation - // which returned simply 'jojo'. - FilePath RemoveExtension() const WARN_UNUSED_RESULT; - - // Removes the path's file extension, as in RemoveExtension(), but - // ignores double extensions. - FilePath RemoveFinalExtension() const WARN_UNUSED_RESULT; - - // Inserts |suffix| after the file name portion of |path| but before the - // extension. Returns "" if BaseName() == "." or "..". - // Examples: - // path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg" - // path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg" - // path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)" - // path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)" - FilePath InsertBeforeExtension( - StringPieceType suffix) const WARN_UNUSED_RESULT; - FilePath InsertBeforeExtensionASCII( - StringPiece suffix) const WARN_UNUSED_RESULT; - - // Adds |extension| to |file_name|. Returns the current FilePath if - // |extension| is empty. Returns "" if BaseName() == "." or "..". - FilePath AddExtension(StringPieceType extension) const WARN_UNUSED_RESULT; - - // Replaces the extension of |file_name| with |extension|. If |file_name| - // does not have an extension, then |extension| is added. If |extension| is - // empty, then the extension is removed from |file_name|. - // Returns "" if BaseName() == "." or "..". - FilePath ReplaceExtension(StringPieceType extension) const WARN_UNUSED_RESULT; - - // Returns true if the file path matches the specified extension. The test is - // case insensitive. Don't forget the leading period if appropriate. - bool MatchesExtension(StringPieceType extension) const; - - // Returns a FilePath by appending a separator and the supplied path - // component to this object's path. Append takes care to avoid adding - // excessive separators if this object's path already ends with a separator. - // If this object's path is kCurrentDirectory, a new FilePath corresponding - // only to |component| is returned. |component| must be a relative path; - // it is an error to pass an absolute path. - FilePath Append(StringPieceType component) const WARN_UNUSED_RESULT; - FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT; - - // Although Windows StringType is std::wstring, since the encoding it uses for - // paths is well defined, it can handle ASCII path components as well. - // Mac uses UTF8, and since ASCII is a subset of that, it works there as well. - // On Linux, although it can use any 8-bit encoding for paths, we assume that - // ASCII is a valid subset, regardless of the encoding, since many operating - // system paths will always be ASCII. - FilePath AppendASCII(StringPiece component) const WARN_UNUSED_RESULT; - - // Returns true if this FilePath contains an absolute path. On Windows, an - // absolute path begins with either a drive letter specification followed by - // a separator character, or with two separator characters. On POSIX - // platforms, an absolute path begins with a separator character. - bool IsAbsolute() const; - - // Returns true if the patch ends with a path separator character. - bool EndsWithSeparator() const WARN_UNUSED_RESULT; - - // Returns a copy of this FilePath that ends with a trailing separator. If - // the input path is empty, an empty FilePath will be returned. - FilePath AsEndingWithSeparator() const WARN_UNUSED_RESULT; - - // Returns a copy of this FilePath that does not end with a trailing - // separator. - FilePath StripTrailingSeparators() const WARN_UNUSED_RESULT; - - // 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. - // Warning: you can *not*, in general, go from a display name back to a real - // path. Only use this when displaying paths to users, not just when you - // want to stuff a string16 into some other API. - string16 LossyDisplayName() const; - - // Return the path as ASCII, or the empty string if the path is not ASCII. - // This should only be used for cases where the FilePath is representing a - // known-ASCII filename. - std::string MaybeAsASCII() const; - - // Return the path as UTF-8. - // - // This function is *unsafe* as there is no way to tell what encoding is - // used in file names on POSIX systems other than Mac and Chrome OS, - // although UTF-8 is practically used everywhere these days. To mitigate - // the encoding issue, this function internally calls - // SysNativeMBToWide() on POSIX systems other than Mac and Chrome OS, - // per assumption that the current locale's encoding is used in file - // names, but this isn't a perfect solution. - // - // Once it becomes safe to to stop caring about non-UTF-8 file names, - // the SysNativeMBToWide() hack will be removed from the code, along - // with "Unsafe" in the function name. - std::string AsUTF8Unsafe() const; - - // Similar to AsUTF8Unsafe, but returns UTF-16 instead. - string16 AsUTF16Unsafe() const; - - // Returns a FilePath object from a path name in UTF-8. This function - // should only be used for cases where you are sure that the input - // string is UTF-8. - // - // Like AsUTF8Unsafe(), this function is unsafe. This function - // internally calls SysWideToNativeMB() on POSIX systems other than Mac - // and Chrome OS, to mitigate the encoding issue. See the comment at - // AsUTF8Unsafe() for details. - static FilePath FromUTF8Unsafe(StringPiece utf8); - - // Similar to FromUTF8Unsafe, but accepts UTF-16 instead. - static FilePath FromUTF16Unsafe(StringPiece16 utf16); - - void WriteToPickle(Pickle* pickle) const; - bool ReadFromPickle(PickleIterator* iter); - - // Normalize all path separators to backslash on Windows - // (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 - // methods here. - // The methods are written as a static method so that they can also be used - // on parts of a file path, e.g., just the extension. - // CompareIgnoreCase() returns -1, 0 or 1 for less-than, equal-to and - // greater-than respectively. - static int CompareIgnoreCase(StringPieceType string1, - StringPieceType string2); - static bool CompareEqualIgnoreCase(StringPieceType string1, - StringPieceType string2) { - return CompareIgnoreCase(string1, string2) == 0; - } - static bool CompareLessIgnoreCase(StringPieceType string1, - StringPieceType string2) { - return CompareIgnoreCase(string1, string2) < 0; - } - -#if defined(OS_MACOSX) - // Returns the string in the special canonical decomposed form as defined for - // HFS, which is close to, but not quite, decomposition form D. See - // http://developer.apple.com/mac/library/technotes/tn/tn1150.html#UnicodeSubtleties - // for further comments. - // Returns the epmty string if the conversion failed. - static StringType GetHFSDecomposedForm(StringPieceType string); - - // Special UTF-8 version of FastUnicodeCompare. Cf: - // http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm - // IMPORTANT: The input strings must be in the special HFS decomposed form! - // (cf. above GetHFSDecomposedForm method) - static int HFSFastUnicodeCompare(StringPieceType string1, - StringPieceType string2); -#endif - -#if defined(OS_ANDROID) - // On android, file selection dialog can return a file with content uri - // scheme(starting with content://). Content uri needs to be opened with - // ContentResolver to guarantee that the app has appropriate permissions - // to access it. - // Returns true if the path is a content uri, or false otherwise. - bool IsContentUri() const; -#endif - - private: - // Remove trailing separators from this object. If the path is absolute, it - // will never be stripped any more than to refer to the absolute root - // directory, so "////" will become "/", not "". A leading pair of - // separators is never stripped, to support alternate roots. This is used to - // support UNC paths on Windows. - void StripTrailingSeparatorsInternal(); - - StringType path_; -}; - -BASE_EXPORT std::ostream& operator<<(std::ostream& out, - const FilePath& file_path); - -} // namespace base - -// Macros for string literal initialization of FilePath::CharType[], and for -// using a FilePath::CharType[] in a printf-style format string. -#if defined(OS_WIN) -#define FILE_PATH_LITERAL(x) L ## x -#define PRFilePath "ls" -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#define FILE_PATH_LITERAL(x) x -#define PRFilePath "s" -#endif // OS_WIN - -namespace std { - -template <> -struct hash { - typedef base::FilePath argument_type; - typedef std::size_t result_type; - result_type operator()(argument_type const& f) const { - return hash()(f.value()); - } -}; - -} // namespace std - -#endif // BASE_FILES_FILE_PATH_H_ diff --git a/files/file_path_constants.cc b/files/file_path_constants.cc deleted file mode 100644 index 0b748466c..000000000 --- a/files/file_path_constants.cc +++ /dev/null @@ -1,25 +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 - -#include "base/files/file_path.h" -#include "base/macros.h" - -namespace base { - -#if defined(FILE_PATH_USES_WIN_SEPARATORS) -const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); -#else // FILE_PATH_USES_WIN_SEPARATORS -const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); -#endif // FILE_PATH_USES_WIN_SEPARATORS - -const size_t FilePath::kSeparatorsLength = arraysize(kSeparators); - -const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); -const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); - -const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.'); - -} // namespace base diff --git a/files/file_path_unittest.cc b/files/file_path_unittest.cc deleted file mode 100644 index e722c6828..000000000 --- a/files/file_path_unittest.cc +++ /dev/null @@ -1,1319 +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 - -#include - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include "base/test/scoped_locale.h" -#endif - -// This macro helps avoid wrapped lines in the test structs. -#define FPL(x) FILE_PATH_LITERAL(x) - -// This macro constructs strings which can contain NULs. -#define FPS(x) FilePath::StringType(FPL(x), arraysize(FPL(x)) - 1) - -namespace base { - -struct UnaryTestData { - const FilePath::CharType* input; - const FilePath::CharType* expected; -}; - -struct UnaryBooleanTestData { - const FilePath::CharType* input; - bool expected; -}; - -struct BinaryTestData { - const FilePath::CharType* inputs[2]; - const FilePath::CharType* expected; -}; - -struct BinaryBooleanTestData { - const FilePath::CharType* inputs[2]; - bool expected; -}; - -struct BinaryIntTestData { - const FilePath::CharType* inputs[2]; - int expected; -}; - -struct UTF8TestData { - const FilePath::CharType* native; - const char* utf8; -}; - -// file_util winds up using autoreleased objects on the Mac, so this needs -// to be a PlatformTest -typedef PlatformTest FilePathTest; - -TEST_F(FilePathTest, DirName) { - const struct UnaryTestData cases[] = { - { FPL(""), FPL(".") }, - { FPL("aa"), FPL(".") }, - { FPL("/aa/bb"), FPL("/aa") }, - { FPL("/aa/bb/"), FPL("/aa") }, - { FPL("/aa/bb//"), FPL("/aa") }, - { FPL("/aa/bb/ccc"), FPL("/aa/bb") }, - { FPL("/aa"), FPL("/") }, - { FPL("/aa/"), FPL("/") }, - { FPL("/"), FPL("/") }, - { FPL("//"), FPL("//") }, - { FPL("///"), FPL("/") }, - { FPL("aa/"), FPL(".") }, - { FPL("aa/bb"), FPL("aa") }, - { FPL("aa/bb/"), FPL("aa") }, - { FPL("aa/bb//"), FPL("aa") }, - { FPL("aa//bb//"), FPL("aa") }, - { FPL("aa//bb/"), FPL("aa") }, - { FPL("aa//bb"), FPL("aa") }, - { FPL("//aa/bb"), FPL("//aa") }, - { FPL("//aa/"), FPL("//") }, - { FPL("//aa"), FPL("//") }, - { FPL("0:"), FPL(".") }, - { FPL("@:"), FPL(".") }, - { FPL("[:"), FPL(".") }, - { FPL("`:"), FPL(".") }, - { FPL("{:"), FPL(".") }, - { FPL("\xB3:"), FPL(".") }, - { FPL("\xC5:"), FPL(".") }, - { FPL("/aa/../bb/cc"), FPL("/aa/../bb")}, -#if defined(OS_WIN) - { FPL("\x0143:"), FPL(".") }, -#endif // OS_WIN -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { FPL("c:"), FPL("c:") }, - { FPL("C:"), FPL("C:") }, - { FPL("A:"), FPL("A:") }, - { FPL("Z:"), FPL("Z:") }, - { FPL("a:"), FPL("a:") }, - { FPL("z:"), FPL("z:") }, - { FPL("c:aa"), FPL("c:") }, - { FPL("c:/"), FPL("c:/") }, - { FPL("c://"), FPL("c://") }, - { FPL("c:///"), FPL("c:/") }, - { FPL("c:/aa"), FPL("c:/") }, - { FPL("c:/aa/"), FPL("c:/") }, - { FPL("c:/aa/bb"), FPL("c:/aa") }, - { FPL("c:aa/bb"), FPL("c:aa") }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { FPL("\\aa\\bb"), FPL("\\aa") }, - { FPL("\\aa\\bb\\"), FPL("\\aa") }, - { FPL("\\aa\\bb\\\\"), FPL("\\aa") }, - { FPL("\\aa\\bb\\ccc"), FPL("\\aa\\bb") }, - { FPL("\\aa"), FPL("\\") }, - { FPL("\\aa\\"), FPL("\\") }, - { FPL("\\"), FPL("\\") }, - { FPL("\\\\"), FPL("\\\\") }, - { FPL("\\\\\\"), FPL("\\") }, - { FPL("aa\\"), FPL(".") }, - { FPL("aa\\bb"), FPL("aa") }, - { FPL("aa\\bb\\"), FPL("aa") }, - { FPL("aa\\bb\\\\"), FPL("aa") }, - { FPL("aa\\\\bb\\\\"), FPL("aa") }, - { FPL("aa\\\\bb\\"), FPL("aa") }, - { FPL("aa\\\\bb"), FPL("aa") }, - { FPL("\\\\aa\\bb"), FPL("\\\\aa") }, - { FPL("\\\\aa\\"), FPL("\\\\") }, - { FPL("\\\\aa"), FPL("\\\\") }, - { FPL("aa\\..\\bb\\c"), FPL("aa\\..\\bb")}, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { FPL("c:\\"), FPL("c:\\") }, - { FPL("c:\\\\"), FPL("c:\\\\") }, - { FPL("c:\\\\\\"), FPL("c:\\") }, - { FPL("c:\\aa"), FPL("c:\\") }, - { FPL("c:\\aa\\"), FPL("c:\\") }, - { FPL("c:\\aa\\bb"), FPL("c:\\aa") }, - { FPL("c:aa\\bb"), FPL("c:aa") }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#endif // FILE_PATH_USES_WIN_SEPARATORS - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath input(cases[i].input); - FilePath observed = input.DirName(); - EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) << - "i: " << i << ", input: " << input.value(); - } -} - -TEST_F(FilePathTest, BaseName) { - const struct UnaryTestData cases[] = { - { FPL(""), FPL("") }, - { FPL("aa"), FPL("aa") }, - { FPL("/aa/bb"), FPL("bb") }, - { FPL("/aa/bb/"), FPL("bb") }, - { FPL("/aa/bb//"), FPL("bb") }, - { FPL("/aa/bb/ccc"), FPL("ccc") }, - { FPL("/aa"), FPL("aa") }, - { FPL("/"), FPL("/") }, - { FPL("//"), FPL("//") }, - { FPL("///"), FPL("/") }, - { FPL("aa/"), FPL("aa") }, - { FPL("aa/bb"), FPL("bb") }, - { FPL("aa/bb/"), FPL("bb") }, - { FPL("aa/bb//"), FPL("bb") }, - { FPL("aa//bb//"), FPL("bb") }, - { FPL("aa//bb/"), FPL("bb") }, - { FPL("aa//bb"), FPL("bb") }, - { FPL("//aa/bb"), FPL("bb") }, - { FPL("//aa/"), FPL("aa") }, - { FPL("//aa"), FPL("aa") }, - { FPL("0:"), FPL("0:") }, - { FPL("@:"), FPL("@:") }, - { FPL("[:"), FPL("[:") }, - { FPL("`:"), FPL("`:") }, - { FPL("{:"), FPL("{:") }, - { FPL("\xB3:"), FPL("\xB3:") }, - { FPL("\xC5:"), FPL("\xC5:") }, -#if defined(OS_WIN) - { FPL("\x0143:"), FPL("\x0143:") }, -#endif // OS_WIN -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { FPL("c:"), FPL("") }, - { FPL("C:"), FPL("") }, - { FPL("A:"), FPL("") }, - { FPL("Z:"), FPL("") }, - { FPL("a:"), FPL("") }, - { FPL("z:"), FPL("") }, - { FPL("c:aa"), FPL("aa") }, - { FPL("c:/"), FPL("/") }, - { FPL("c://"), FPL("//") }, - { FPL("c:///"), FPL("/") }, - { FPL("c:/aa"), FPL("aa") }, - { FPL("c:/aa/"), FPL("aa") }, - { FPL("c:/aa/bb"), FPL("bb") }, - { FPL("c:aa/bb"), FPL("bb") }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { FPL("\\aa\\bb"), FPL("bb") }, - { FPL("\\aa\\bb\\"), FPL("bb") }, - { FPL("\\aa\\bb\\\\"), FPL("bb") }, - { FPL("\\aa\\bb\\ccc"), FPL("ccc") }, - { FPL("\\aa"), FPL("aa") }, - { FPL("\\"), FPL("\\") }, - { FPL("\\\\"), FPL("\\\\") }, - { FPL("\\\\\\"), FPL("\\") }, - { FPL("aa\\"), FPL("aa") }, - { FPL("aa\\bb"), FPL("bb") }, - { FPL("aa\\bb\\"), FPL("bb") }, - { FPL("aa\\bb\\\\"), FPL("bb") }, - { FPL("aa\\\\bb\\\\"), FPL("bb") }, - { FPL("aa\\\\bb\\"), FPL("bb") }, - { FPL("aa\\\\bb"), FPL("bb") }, - { FPL("\\\\aa\\bb"), FPL("bb") }, - { FPL("\\\\aa\\"), FPL("aa") }, - { FPL("\\\\aa"), FPL("aa") }, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { FPL("c:\\"), FPL("\\") }, - { FPL("c:\\\\"), FPL("\\\\") }, - { FPL("c:\\\\\\"), FPL("\\") }, - { FPL("c:\\aa"), FPL("aa") }, - { FPL("c:\\aa\\"), FPL("aa") }, - { FPL("c:\\aa\\bb"), FPL("bb") }, - { FPL("c:aa\\bb"), FPL("bb") }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#endif // FILE_PATH_USES_WIN_SEPARATORS - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath input(cases[i].input); - FilePath observed = input.BaseName(); - EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) << - "i: " << i << ", input: " << input.value(); - } -} - -TEST_F(FilePathTest, Append) { - const struct BinaryTestData cases[] = { - { { FPL(""), FPL("cc") }, FPL("cc") }, - { { FPL("."), FPL("ff") }, FPL("ff") }, - { { FPL("."), FPL("") }, FPL(".") }, - { { FPL("/"), FPL("cc") }, FPL("/cc") }, - { { FPL("/aa"), FPL("") }, FPL("/aa") }, - { { FPL("/aa/"), FPL("") }, FPL("/aa") }, - { { FPL("//aa"), FPL("") }, FPL("//aa") }, - { { FPL("//aa/"), FPL("") }, FPL("//aa") }, - { { FPL("//"), FPL("aa") }, FPL("//aa") }, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { { FPL("c:"), FPL("a") }, FPL("c:a") }, - { { FPL("c:"), FPL("") }, FPL("c:") }, - { { FPL("c:/"), FPL("a") }, FPL("c:/a") }, - { { FPL("c://"), FPL("a") }, FPL("c://a") }, - { { FPL("c:///"), FPL("a") }, FPL("c:/a") }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - // Append introduces the default separator character, so these test cases - // need to be defined with different expected results on platforms that use - // different default separator characters. - { { FPL("\\"), FPL("cc") }, FPL("\\cc") }, - { { FPL("\\aa"), FPL("") }, FPL("\\aa") }, - { { FPL("\\aa\\"), FPL("") }, FPL("\\aa") }, - { { FPL("\\\\aa"), FPL("") }, FPL("\\\\aa") }, - { { FPL("\\\\aa\\"), FPL("") }, FPL("\\\\aa") }, - { { FPL("\\\\"), FPL("aa") }, FPL("\\\\aa") }, - { { FPL("/aa/bb"), FPL("cc") }, FPL("/aa/bb\\cc") }, - { { FPL("/aa/bb/"), FPL("cc") }, FPL("/aa/bb\\cc") }, - { { FPL("aa/bb/"), FPL("cc") }, FPL("aa/bb\\cc") }, - { { FPL("aa/bb"), FPL("cc") }, FPL("aa/bb\\cc") }, - { { FPL("a/b"), FPL("c") }, FPL("a/b\\c") }, - { { FPL("a/b/"), FPL("c") }, FPL("a/b\\c") }, - { { FPL("//aa"), FPL("bb") }, FPL("//aa\\bb") }, - { { FPL("//aa/"), FPL("bb") }, FPL("//aa\\bb") }, - { { FPL("\\aa\\bb"), FPL("cc") }, FPL("\\aa\\bb\\cc") }, - { { FPL("\\aa\\bb\\"), FPL("cc") }, FPL("\\aa\\bb\\cc") }, - { { FPL("aa\\bb\\"), FPL("cc") }, FPL("aa\\bb\\cc") }, - { { FPL("aa\\bb"), FPL("cc") }, FPL("aa\\bb\\cc") }, - { { FPL("a\\b"), FPL("c") }, FPL("a\\b\\c") }, - { { FPL("a\\b\\"), FPL("c") }, FPL("a\\b\\c") }, - { { FPL("\\\\aa"), FPL("bb") }, FPL("\\\\aa\\bb") }, - { { FPL("\\\\aa\\"), FPL("bb") }, FPL("\\\\aa\\bb") }, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { { FPL("c:\\"), FPL("a") }, FPL("c:\\a") }, - { { FPL("c:\\\\"), FPL("a") }, FPL("c:\\\\a") }, - { { FPL("c:\\\\\\"), FPL("a") }, FPL("c:\\a") }, - { { FPL("c:\\"), FPL("") }, FPL("c:\\") }, - { { FPL("c:\\a"), FPL("b") }, FPL("c:\\a\\b") }, - { { FPL("c:\\a\\"), FPL("b") }, FPL("c:\\a\\b") }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#else // FILE_PATH_USES_WIN_SEPARATORS - { { FPL("/aa/bb"), FPL("cc") }, FPL("/aa/bb/cc") }, - { { FPL("/aa/bb/"), FPL("cc") }, FPL("/aa/bb/cc") }, - { { FPL("aa/bb/"), FPL("cc") }, FPL("aa/bb/cc") }, - { { FPL("aa/bb"), FPL("cc") }, FPL("aa/bb/cc") }, - { { FPL("a/b"), FPL("c") }, FPL("a/b/c") }, - { { FPL("a/b/"), FPL("c") }, FPL("a/b/c") }, - { { FPL("//aa"), FPL("bb") }, FPL("//aa/bb") }, - { { FPL("//aa/"), FPL("bb") }, FPL("//aa/bb") }, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { { FPL("c:/"), FPL("a") }, FPL("c:/a") }, - { { FPL("c:/"), FPL("") }, FPL("c:/") }, - { { FPL("c:/a"), FPL("b") }, FPL("c:/a/b") }, - { { FPL("c:/a/"), FPL("b") }, FPL("c:/a/b") }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#endif // FILE_PATH_USES_WIN_SEPARATORS - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath root(cases[i].inputs[0]); - FilePath::StringType leaf(cases[i].inputs[1]); - FilePath observed_str = root.Append(leaf); - EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_str.value()) << - "i: " << i << ", root: " << root.value() << ", leaf: " << leaf; - FilePath observed_path = root.Append(FilePath(leaf)); - EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_path.value()) << - "i: " << i << ", root: " << root.value() << ", leaf: " << leaf; - - // TODO(erikkay): It would be nice to have a unicode test append value to - // handle the case when AppendASCII is passed UTF8 -#if defined(OS_WIN) - std::string ascii = WideToUTF8(leaf); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - std::string ascii = leaf; -#endif - observed_str = root.AppendASCII(ascii); - EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_str.value()) << - "i: " << i << ", root: " << root.value() << ", leaf: " << leaf; - } -} - -TEST_F(FilePathTest, StripTrailingSeparators) { - const struct UnaryTestData cases[] = { - { FPL(""), FPL("") }, - { FPL("/"), FPL("/") }, - { FPL("//"), FPL("//") }, - { FPL("///"), FPL("/") }, - { FPL("////"), FPL("/") }, - { FPL("a/"), FPL("a") }, - { FPL("a//"), FPL("a") }, - { FPL("a///"), FPL("a") }, - { FPL("a////"), FPL("a") }, - { FPL("/a"), FPL("/a") }, - { FPL("/a/"), FPL("/a") }, - { FPL("/a//"), FPL("/a") }, - { FPL("/a///"), FPL("/a") }, - { FPL("/a////"), FPL("/a") }, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { FPL("c:"), FPL("c:") }, - { FPL("c:/"), FPL("c:/") }, - { FPL("c://"), FPL("c://") }, - { FPL("c:///"), FPL("c:/") }, - { FPL("c:////"), FPL("c:/") }, - { FPL("c:/a"), FPL("c:/a") }, - { FPL("c:/a/"), FPL("c:/a") }, - { FPL("c:/a//"), FPL("c:/a") }, - { FPL("c:/a///"), FPL("c:/a") }, - { FPL("c:/a////"), FPL("c:/a") }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { FPL("\\"), FPL("\\") }, - { FPL("\\\\"), FPL("\\\\") }, - { FPL("\\\\\\"), FPL("\\") }, - { FPL("\\\\\\\\"), FPL("\\") }, - { FPL("a\\"), FPL("a") }, - { FPL("a\\\\"), FPL("a") }, - { FPL("a\\\\\\"), FPL("a") }, - { FPL("a\\\\\\\\"), FPL("a") }, - { FPL("\\a"), FPL("\\a") }, - { FPL("\\a\\"), FPL("\\a") }, - { FPL("\\a\\\\"), FPL("\\a") }, - { FPL("\\a\\\\\\"), FPL("\\a") }, - { FPL("\\a\\\\\\\\"), FPL("\\a") }, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { FPL("c:\\"), FPL("c:\\") }, - { FPL("c:\\\\"), FPL("c:\\\\") }, - { FPL("c:\\\\\\"), FPL("c:\\") }, - { FPL("c:\\\\\\\\"), FPL("c:\\") }, - { FPL("c:\\a"), FPL("c:\\a") }, - { FPL("c:\\a\\"), FPL("c:\\a") }, - { FPL("c:\\a\\\\"), FPL("c:\\a") }, - { FPL("c:\\a\\\\\\"), FPL("c:\\a") }, - { FPL("c:\\a\\\\\\\\"), FPL("c:\\a") }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#endif // FILE_PATH_USES_WIN_SEPARATORS - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath input(cases[i].input); - FilePath observed = input.StripTrailingSeparators(); - EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) << - "i: " << i << ", input: " << input.value(); - } -} - -TEST_F(FilePathTest, IsAbsolute) { - const struct UnaryBooleanTestData cases[] = { - { FPL(""), false }, - { FPL("a"), false }, - { FPL("c:"), false }, - { FPL("c:a"), false }, - { FPL("a/b"), false }, - { FPL("//"), true }, - { FPL("//a"), true }, - { FPL("c:a/b"), false }, - { FPL("?:/a"), false }, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { FPL("/"), false }, - { FPL("/a"), false }, - { FPL("/."), false }, - { FPL("/.."), false }, - { FPL("c:/"), true }, - { FPL("c:/a"), true }, - { FPL("c:/."), true }, - { FPL("c:/.."), true }, - { FPL("C:/a"), true }, - { FPL("d:/a"), true }, -#else // FILE_PATH_USES_DRIVE_LETTERS - { FPL("/"), true }, - { FPL("/a"), true }, - { FPL("/."), true }, - { FPL("/.."), true }, - { FPL("c:/"), false }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { FPL("a\\b"), false }, - { FPL("\\\\"), true }, - { FPL("\\\\a"), true }, - { FPL("a\\b"), false }, - { FPL("\\\\"), true }, - { FPL("//a"), true }, - { FPL("c:a\\b"), false }, - { FPL("?:\\a"), false }, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { FPL("\\"), false }, - { FPL("\\a"), false }, - { FPL("\\."), false }, - { FPL("\\.."), false }, - { FPL("c:\\"), true }, - { FPL("c:\\"), true }, - { FPL("c:\\a"), true }, - { FPL("c:\\."), true }, - { FPL("c:\\.."), true }, - { FPL("C:\\a"), true }, - { FPL("d:\\a"), true }, -#else // FILE_PATH_USES_DRIVE_LETTERS - { FPL("\\"), true }, - { FPL("\\a"), true }, - { FPL("\\."), true }, - { FPL("\\.."), true }, - { FPL("c:\\"), false }, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#endif // FILE_PATH_USES_WIN_SEPARATORS - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath input(cases[i].input); - bool observed = input.IsAbsolute(); - EXPECT_EQ(cases[i].expected, observed) << - "i: " << i << ", input: " << input.value(); - } -} - -TEST_F(FilePathTest, PathComponentsTest) { - const struct UnaryTestData cases[] = { - { FPL("//foo/bar/baz/"), FPL("|//|foo|bar|baz")}, - { FPL("///"), FPL("|/")}, - { FPL("/foo//bar//baz/"), FPL("|/|foo|bar|baz")}, - { FPL("/foo/bar/baz/"), FPL("|/|foo|bar|baz")}, - { FPL("/foo/bar/baz//"), FPL("|/|foo|bar|baz")}, - { FPL("/foo/bar/baz///"), FPL("|/|foo|bar|baz")}, - { FPL("/foo/bar/baz"), FPL("|/|foo|bar|baz")}, - { FPL("/foo/bar.bot/baz.txt"), FPL("|/|foo|bar.bot|baz.txt")}, - { FPL("//foo//bar/baz"), FPL("|//|foo|bar|baz")}, - { FPL("/"), FPL("|/")}, - { FPL("foo"), FPL("|foo")}, - { FPL(""), FPL("")}, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { FPL("e:/foo"), FPL("|e:|/|foo")}, - { FPL("e:/"), FPL("|e:|/")}, - { FPL("e:"), FPL("|e:")}, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { FPL("../foo"), FPL("|..|foo")}, - { FPL("./foo"), FPL("|foo")}, - { FPL("../foo/bar/"), FPL("|..|foo|bar") }, - { FPL("\\\\foo\\bar\\baz\\"), FPL("|\\\\|foo|bar|baz")}, - { FPL("\\\\\\"), FPL("|\\")}, - { FPL("\\foo\\\\bar\\\\baz\\"), FPL("|\\|foo|bar|baz")}, - { FPL("\\foo\\bar\\baz\\"), FPL("|\\|foo|bar|baz")}, - { FPL("\\foo\\bar\\baz\\\\"), FPL("|\\|foo|bar|baz")}, - { FPL("\\foo\\bar\\baz\\\\\\"), FPL("|\\|foo|bar|baz")}, - { FPL("\\foo\\bar\\baz"), FPL("|\\|foo|bar|baz")}, - { FPL("\\foo\\bar/baz\\\\\\"), FPL("|\\|foo|bar|baz")}, - { FPL("/foo\\bar\\baz"), FPL("|/|foo|bar|baz")}, - { FPL("\\foo\\bar.bot\\baz.txt"), FPL("|\\|foo|bar.bot|baz.txt")}, - { FPL("\\\\foo\\\\bar\\baz"), FPL("|\\\\|foo|bar|baz")}, - { FPL("\\"), FPL("|\\")}, -#endif // FILE_PATH_USES_WIN_SEPARATORS - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath input(cases[i].input); - std::vector comps; - input.GetComponents(&comps); - - FilePath::StringType observed; - for (size_t j = 0; j < comps.size(); ++j) { - observed.append(FILE_PATH_LITERAL("|"), 1); - observed.append(comps[j]); - } - EXPECT_EQ(FilePath::StringType(cases[i].expected), observed) << - "i: " << i << ", input: " << input.value(); - } -} - -TEST_F(FilePathTest, IsParentTest) { - const struct BinaryBooleanTestData cases[] = { - { { FPL("/"), FPL("/foo/bar/baz") }, true}, - { { FPL("/foo/bar"), FPL("/foo/bar/baz") }, true}, - { { FPL("/foo/bar/"), FPL("/foo/bar/baz") }, true}, - { { FPL("//foo/bar/"), FPL("//foo/bar/baz") }, true}, - { { FPL("/foo/bar"), FPL("/foo2/bar/baz") }, false}, - { { FPL("/foo/bar.txt"), FPL("/foo/bar/baz") }, false}, - { { FPL("/foo/bar"), FPL("/foo/bar2/baz") }, false}, - { { FPL("/foo/bar"), FPL("/foo/bar") }, false}, - { { FPL("/foo/bar/baz"), FPL("/foo/bar") }, false}, - { { FPL("foo/bar"), FPL("foo/bar/baz") }, true}, - { { FPL("foo/bar"), FPL("foo2/bar/baz") }, false}, - { { FPL("foo/bar"), FPL("foo/bar2/baz") }, false}, - { { FPL(""), FPL("foo") }, false}, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { { FPL("c:/foo/bar"), FPL("c:/foo/bar/baz") }, true}, - { { FPL("E:/foo/bar"), FPL("e:/foo/bar/baz") }, true}, - { { FPL("f:/foo/bar"), FPL("F:/foo/bar/baz") }, true}, - { { FPL("E:/Foo/bar"), FPL("e:/foo/bar/baz") }, false}, - { { FPL("f:/foo/bar"), FPL("F:/foo/Bar/baz") }, false}, - { { FPL("c:/"), FPL("c:/foo/bar/baz") }, true}, - { { FPL("c:"), FPL("c:/foo/bar/baz") }, true}, - { { FPL("c:/foo/bar"), FPL("d:/foo/bar/baz") }, false}, - { { FPL("c:/foo/bar"), FPL("D:/foo/bar/baz") }, false}, - { { FPL("C:/foo/bar"), FPL("d:/foo/bar/baz") }, false}, - { { FPL("c:/foo/bar"), FPL("c:/foo2/bar/baz") }, false}, - { { FPL("e:/foo/bar"), FPL("E:/foo2/bar/baz") }, false}, - { { FPL("F:/foo/bar"), FPL("f:/foo2/bar/baz") }, false}, - { { FPL("c:/foo/bar"), FPL("c:/foo/bar2/baz") }, false}, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { { FPL("\\foo\\bar"), FPL("\\foo\\bar\\baz") }, true}, - { { FPL("\\foo/bar"), FPL("\\foo\\bar\\baz") }, true}, - { { FPL("\\foo/bar"), FPL("\\foo/bar/baz") }, true}, - { { FPL("\\"), FPL("\\foo\\bar\\baz") }, true}, - { { FPL(""), FPL("\\foo\\bar\\baz") }, false}, - { { FPL("\\foo\\bar"), FPL("\\foo2\\bar\\baz") }, false}, - { { FPL("\\foo\\bar"), FPL("\\foo\\bar2\\baz") }, false}, -#endif // FILE_PATH_USES_WIN_SEPARATORS - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath parent(cases[i].inputs[0]); - FilePath child(cases[i].inputs[1]); - - EXPECT_EQ(parent.IsParent(child), cases[i].expected) << - "i: " << i << ", parent: " << parent.value() << ", child: " << - child.value(); - } -} - -TEST_F(FilePathTest, AppendRelativePathTest) { - const struct BinaryTestData cases[] = { -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { { FPL("/"), FPL("/foo/bar/baz") }, FPL("foo\\bar\\baz")}, -#else // FILE_PATH_USES_WIN_SEPARATORS - { { FPL("/"), FPL("/foo/bar/baz") }, FPL("foo/bar/baz")}, -#endif // FILE_PATH_USES_WIN_SEPARATORS - { { FPL("/foo/bar"), FPL("/foo/bar/baz") }, FPL("baz")}, - { { FPL("/foo/bar/"), FPL("/foo/bar/baz") }, FPL("baz")}, - { { FPL("//foo/bar/"), FPL("//foo/bar/baz") }, FPL("baz")}, - { { FPL("/foo/bar"), FPL("/foo2/bar/baz") }, FPL("")}, - { { FPL("/foo/bar.txt"), FPL("/foo/bar/baz") }, FPL("")}, - { { FPL("/foo/bar"), FPL("/foo/bar2/baz") }, FPL("")}, - { { FPL("/foo/bar"), FPL("/foo/bar") }, FPL("")}, - { { FPL("/foo/bar/baz"), FPL("/foo/bar") }, FPL("")}, - { { FPL("foo/bar"), FPL("foo/bar/baz") }, FPL("baz")}, - { { FPL("foo/bar"), FPL("foo2/bar/baz") }, FPL("")}, - { { FPL("foo/bar"), FPL("foo/bar2/baz") }, FPL("")}, - { { FPL(""), FPL("foo") }, FPL("")}, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { { FPL("c:/foo/bar"), FPL("c:/foo/bar/baz") }, FPL("baz")}, - { { FPL("E:/foo/bar"), FPL("e:/foo/bar/baz") }, FPL("baz")}, - { { FPL("f:/foo/bar"), FPL("F:/foo/bar/baz") }, FPL("baz")}, - { { FPL("E:/Foo/bar"), FPL("e:/foo/bar/baz") }, FPL("")}, - { { FPL("f:/foo/bar"), FPL("F:/foo/Bar/baz") }, FPL("")}, -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { { FPL("c:/"), FPL("c:/foo/bar/baz") }, FPL("foo\\bar\\baz")}, - // TODO(akalin): Figure out how to handle the corner case in the - // commented-out test case below. Appending to an empty path gives - // /foo\bar\baz but appending to a nonempty path "blah" gives - // blah\foo\bar\baz. - // { { FPL("c:"), FPL("c:/foo/bar/baz") }, FPL("foo\\bar\\baz")}, -#endif // FILE_PATH_USES_WIN_SEPARATORS - { { FPL("c:/foo/bar"), FPL("d:/foo/bar/baz") }, FPL("")}, - { { FPL("c:/foo/bar"), FPL("D:/foo/bar/baz") }, FPL("")}, - { { FPL("C:/foo/bar"), FPL("d:/foo/bar/baz") }, FPL("")}, - { { FPL("c:/foo/bar"), FPL("c:/foo2/bar/baz") }, FPL("")}, - { { FPL("e:/foo/bar"), FPL("E:/foo2/bar/baz") }, FPL("")}, - { { FPL("F:/foo/bar"), FPL("f:/foo2/bar/baz") }, FPL("")}, - { { FPL("c:/foo/bar"), FPL("c:/foo/bar2/baz") }, FPL("")}, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { { FPL("\\foo\\bar"), FPL("\\foo\\bar\\baz") }, FPL("baz")}, - { { FPL("\\foo/bar"), FPL("\\foo\\bar\\baz") }, FPL("baz")}, - { { FPL("\\foo/bar"), FPL("\\foo/bar/baz") }, FPL("baz")}, - { { FPL("\\"), FPL("\\foo\\bar\\baz") }, FPL("foo\\bar\\baz")}, - { { FPL(""), FPL("\\foo\\bar\\baz") }, FPL("")}, - { { FPL("\\foo\\bar"), FPL("\\foo2\\bar\\baz") }, FPL("")}, - { { FPL("\\foo\\bar"), FPL("\\foo\\bar2\\baz") }, FPL("")}, -#endif // FILE_PATH_USES_WIN_SEPARATORS - }; - - const FilePath base(FPL("blah")); - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath parent(cases[i].inputs[0]); - FilePath child(cases[i].inputs[1]); - { - FilePath result; - bool success = parent.AppendRelativePath(child, &result); - EXPECT_EQ(cases[i].expected[0] != '\0', success) << - "i: " << i << ", parent: " << parent.value() << ", child: " << - child.value(); - EXPECT_STREQ(cases[i].expected, result.value().c_str()) << - "i: " << i << ", parent: " << parent.value() << ", child: " << - child.value(); - } - { - FilePath result(base); - bool success = parent.AppendRelativePath(child, &result); - EXPECT_EQ(cases[i].expected[0] != '\0', success) << - "i: " << i << ", parent: " << parent.value() << ", child: " << - child.value(); - EXPECT_EQ(base.Append(cases[i].expected).value(), result.value()) << - "i: " << i << ", parent: " << parent.value() << ", child: " << - child.value(); - } - } -} - -TEST_F(FilePathTest, EqualityTest) { - const struct BinaryBooleanTestData cases[] = { - { { FPL("/foo/bar/baz"), FPL("/foo/bar/baz") }, true}, - { { FPL("/foo/bar"), FPL("/foo/bar/baz") }, false}, - { { FPL("/foo/bar/baz"), FPL("/foo/bar") }, false}, - { { FPL("//foo/bar/"), FPL("//foo/bar/") }, true}, - { { FPL("/foo/bar"), FPL("/foo2/bar") }, false}, - { { FPL("/foo/bar.txt"), FPL("/foo/bar") }, false}, - { { FPL("foo/bar"), FPL("foo/bar") }, true}, - { { FPL("foo/bar"), FPL("foo/bar/baz") }, false}, - { { FPL(""), FPL("foo") }, false}, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { { FPL("c:/foo/bar"), FPL("c:/foo/bar") }, true}, - { { FPL("E:/foo/bar"), FPL("e:/foo/bar") }, true}, - { { FPL("f:/foo/bar"), FPL("F:/foo/bar") }, true}, - { { FPL("E:/Foo/bar"), FPL("e:/foo/bar") }, false}, - { { FPL("f:/foo/bar"), FPL("F:/foo/Bar") }, false}, - { { FPL("c:/"), FPL("c:/") }, true}, - { { FPL("c:"), FPL("c:") }, true}, - { { FPL("c:/foo/bar"), FPL("d:/foo/bar") }, false}, - { { FPL("c:/foo/bar"), FPL("D:/foo/bar") }, false}, - { { FPL("C:/foo/bar"), FPL("d:/foo/bar") }, false}, - { { FPL("c:/foo/bar"), FPL("c:/foo2/bar") }, false}, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { { FPL("\\foo\\bar"), FPL("\\foo\\bar") }, true}, - { { FPL("\\foo/bar"), FPL("\\foo/bar") }, true}, - { { FPL("\\foo/bar"), FPL("\\foo\\bar") }, false}, - { { FPL("\\"), FPL("\\") }, true}, - { { FPL("\\"), FPL("/") }, false}, - { { FPL(""), FPL("\\") }, false}, - { { FPL("\\foo\\bar"), FPL("\\foo2\\bar") }, false}, - { { FPL("\\foo\\bar"), FPL("\\foo\\bar2") }, false}, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { { FPL("c:\\foo\\bar"), FPL("c:\\foo\\bar") }, true}, - { { FPL("E:\\foo\\bar"), FPL("e:\\foo\\bar") }, true}, - { { FPL("f:\\foo\\bar"), FPL("F:\\foo/bar") }, false}, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#endif // FILE_PATH_USES_WIN_SEPARATORS - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath a(cases[i].inputs[0]); - FilePath b(cases[i].inputs[1]); - - EXPECT_EQ(a == b, cases[i].expected) << - "equality i: " << i << ", a: " << a.value() << ", b: " << - b.value(); - } - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath a(cases[i].inputs[0]); - FilePath b(cases[i].inputs[1]); - - EXPECT_EQ(a != b, !cases[i].expected) << - "inequality i: " << i << ", a: " << a.value() << ", b: " << - b.value(); - } -} - -TEST_F(FilePathTest, Extension) { - FilePath base_dir(FILE_PATH_LITERAL("base_dir")); - - FilePath jpg = base_dir.Append(FILE_PATH_LITERAL("foo.jpg")); - EXPECT_EQ(FILE_PATH_LITERAL(".jpg"), jpg.Extension()); - EXPECT_EQ(FILE_PATH_LITERAL(".jpg"), jpg.FinalExtension()); - - FilePath base = jpg.BaseName().RemoveExtension(); - EXPECT_EQ(FILE_PATH_LITERAL("foo"), base.value()); - - FilePath path_no_ext = base_dir.Append(base); - EXPECT_EQ(path_no_ext.value(), jpg.RemoveExtension().value()); - - EXPECT_EQ(path_no_ext.value(), path_no_ext.RemoveExtension().value()); - EXPECT_EQ(FILE_PATH_LITERAL(""), path_no_ext.Extension()); - EXPECT_EQ(FILE_PATH_LITERAL(""), path_no_ext.FinalExtension()); -} - -TEST_F(FilePathTest, Extension2) { - const struct UnaryTestData cases[] = { -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { FPL("C:\\a\\b\\c.ext"), FPL(".ext") }, - { FPL("C:\\a\\b\\c."), FPL(".") }, - { FPL("C:\\a\\b\\c"), FPL("") }, - { FPL("C:\\a\\b\\"), FPL("") }, - { FPL("C:\\a\\b.\\"), FPL(".") }, - { FPL("C:\\a\\b\\c.ext1.ext2"), FPL(".ext2") }, - { FPL("C:\\foo.bar\\\\\\"), FPL(".bar") }, - { FPL("C:\\foo.bar\\.."), FPL("") }, - { FPL("C:\\foo.bar\\..\\\\"), FPL("") }, -#endif - { FPL("/foo/bar/baz.ext"), FPL(".ext") }, - { FPL("/foo/bar/baz."), FPL(".") }, - { FPL("/foo/bar/baz.."), FPL(".") }, - { FPL("/foo/bar/baz"), FPL("") }, - { FPL("/foo/bar/"), FPL("") }, - { FPL("/foo/bar./"), FPL(".") }, - { FPL("/foo/bar/baz.ext1.ext2"), FPL(".ext2") }, - { FPL("/subversion-1.6.12.zip"), FPL(".zip") }, - { FPL("/foo.12345.gz"), FPL(".gz") }, - { FPL("/foo..gz"), FPL(".gz") }, - { FPL("."), FPL("") }, - { FPL(".."), FPL("") }, - { FPL("./foo"), FPL("") }, - { FPL("./foo.ext"), FPL(".ext") }, - { FPL("/foo.ext1/bar.ext2"), FPL(".ext2") }, - { FPL("/foo.bar////"), FPL(".bar") }, - { FPL("/foo.bar/.."), FPL("") }, - { FPL("/foo.bar/..////"), FPL("") }, - { FPL("/foo.1234.luser.js"), FPL(".js") }, - { FPL("/user.js"), FPL(".js") }, - }; - const struct UnaryTestData double_extension_cases[] = { - { FPL("/foo.tar.gz"), FPL(".tar.gz") }, - { FPL("/foo.tar.Z"), FPL(".tar.Z") }, - { FPL("/foo.tar.bz2"), FPL(".tar.bz2") }, - { FPL("/foo.1234.gz"), FPL(".1234.gz") }, - { FPL("/foo.1234.tar.gz"), FPL(".tar.gz") }, - { FPL("/foo.tar.tar.gz"), FPL(".tar.gz") }, - { FPL("/foo.tar.gz.gz"), FPL(".gz.gz") }, - { FPL("/foo.1234.user.js"), FPL(".user.js") }, - { FPL("foo.user.js"), FPL(".user.js") }, - { FPL("/foo.tar.bz"), FPL(".tar.bz") }, - }; - for (unsigned int i = 0; i < arraysize(cases); ++i) { - FilePath path(cases[i].input); - FilePath::StringType extension = path.Extension(); - FilePath::StringType final_extension = path.FinalExtension(); - EXPECT_STREQ(cases[i].expected, extension.c_str()) - << "i: " << i << ", path: " << path.value(); - EXPECT_STREQ(cases[i].expected, final_extension.c_str()) - << "i: " << i << ", path: " << path.value(); - } - for (unsigned int i = 0; i < arraysize(double_extension_cases); ++i) { - FilePath path(double_extension_cases[i].input); - FilePath::StringType extension = path.Extension(); - EXPECT_STREQ(double_extension_cases[i].expected, extension.c_str()) - << "i: " << i << ", path: " << path.value(); - } -} - -TEST_F(FilePathTest, InsertBeforeExtension) { - const struct BinaryTestData cases[] = { - { { FPL(""), FPL("") }, FPL("") }, - { { FPL(""), FPL("txt") }, FPL("") }, - { { FPL("."), FPL("txt") }, FPL("") }, - { { FPL(".."), FPL("txt") }, FPL("") }, - { { FPL("foo.dll"), FPL("txt") }, FPL("footxt.dll") }, - { { FPL("."), FPL("") }, FPL(".") }, - { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.txt.dll") }, - { { FPL("foo"), FPL("txt") }, FPL("footxt") }, - { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") }, - { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baztxt.dll") }, - { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.txt.dll") }, - { { FPL("foo.dll"), FPL("") }, FPL("foo.dll") }, - { { FPL("foo.dll"), FPL(".") }, FPL("foo..dll") }, - { { FPL("foo"), FPL("") }, FPL("foo") }, - { { FPL("foo"), FPL(".") }, FPL("foo.") }, - { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz.dll") }, - { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz..dll") }, -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { { FPL("\\"), FPL("") }, FPL("\\") }, - { { FPL("\\"), FPL("txt") }, FPL("\\txt") }, - { { FPL("\\."), FPL("txt") }, FPL("") }, - { { FPL("\\.."), FPL("txt") }, FPL("") }, - { { FPL("\\."), FPL("") }, FPL("\\.") }, - { { FPL("C:\\bar\\foo.dll"), FPL("txt") }, - FPL("C:\\bar\\footxt.dll") }, - { { FPL("C:\\bar.baz\\foodll"), FPL("txt") }, - FPL("C:\\bar.baz\\foodlltxt") }, - { { FPL("C:\\bar.baz\\foo.dll"), FPL("txt") }, - FPL("C:\\bar.baz\\footxt.dll") }, - { { FPL("C:\\bar.baz\\foo.dll.exe"), FPL("txt") }, - FPL("C:\\bar.baz\\foo.dlltxt.exe") }, - { { FPL("C:\\bar.baz\\foo"), FPL("") }, - FPL("C:\\bar.baz\\foo") }, - { { FPL("C:\\bar.baz\\foo.exe"), FPL("") }, - FPL("C:\\bar.baz\\foo.exe") }, - { { FPL("C:\\bar.baz\\foo.dll.exe"), FPL("") }, - FPL("C:\\bar.baz\\foo.dll.exe") }, - { { FPL("C:\\bar\\baz\\foo.exe"), FPL(" (1)") }, - FPL("C:\\bar\\baz\\foo (1).exe") }, - { { FPL("C:\\foo.baz\\\\"), FPL(" (1)") }, FPL("C:\\foo (1).baz") }, - { { FPL("C:\\foo.baz\\..\\"), FPL(" (1)") }, FPL("") }, -#endif - { { FPL("/"), FPL("") }, FPL("/") }, - { { FPL("/"), FPL("txt") }, FPL("/txt") }, - { { FPL("/."), FPL("txt") }, FPL("") }, - { { FPL("/.."), FPL("txt") }, FPL("") }, - { { FPL("/."), FPL("") }, FPL("/.") }, - { { FPL("/bar/foo.dll"), FPL("txt") }, FPL("/bar/footxt.dll") }, - { { FPL("/bar.baz/foodll"), FPL("txt") }, FPL("/bar.baz/foodlltxt") }, - { { FPL("/bar.baz/foo.dll"), FPL("txt") }, FPL("/bar.baz/footxt.dll") }, - { { FPL("/bar.baz/foo.dll.exe"), FPL("txt") }, - FPL("/bar.baz/foo.dlltxt.exe") }, - { { FPL("/bar.baz/foo"), FPL("") }, FPL("/bar.baz/foo") }, - { { FPL("/bar.baz/foo.exe"), FPL("") }, FPL("/bar.baz/foo.exe") }, - { { FPL("/bar.baz/foo.dll.exe"), FPL("") }, FPL("/bar.baz/foo.dll.exe") }, - { { FPL("/bar/baz/foo.exe"), FPL(" (1)") }, FPL("/bar/baz/foo (1).exe") }, - { { FPL("/bar/baz/..////"), FPL(" (1)") }, FPL("") }, - }; - for (unsigned int i = 0; i < arraysize(cases); ++i) { - FilePath path(cases[i].inputs[0]); - FilePath result = path.InsertBeforeExtension(cases[i].inputs[1]); - EXPECT_EQ(cases[i].expected, result.value()) << "i: " << i << - ", path: " << path.value() << ", insert: " << cases[i].inputs[1]; - } -} - -TEST_F(FilePathTest, RemoveExtension) { - const struct UnaryTestData cases[] = { - { FPL(""), FPL("") }, - { FPL("."), FPL(".") }, - { FPL(".."), FPL("..") }, - { FPL("foo.dll"), FPL("foo") }, - { FPL("./foo.dll"), FPL("./foo") }, - { FPL("foo..dll"), FPL("foo.") }, - { FPL("foo"), FPL("foo") }, - { FPL("foo."), FPL("foo") }, - { FPL("foo.."), FPL("foo.") }, - { FPL("foo.baz.dll"), FPL("foo.baz") }, -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { FPL("C:\\foo.bar\\foo"), FPL("C:\\foo.bar\\foo") }, - { FPL("C:\\foo.bar\\..\\\\"), FPL("C:\\foo.bar\\..\\\\") }, -#endif - { FPL("/foo.bar/foo"), FPL("/foo.bar/foo") }, - { FPL("/foo.bar/..////"), FPL("/foo.bar/..////") }, - }; - for (unsigned int i = 0; i < arraysize(cases); ++i) { - FilePath path(cases[i].input); - FilePath removed = path.RemoveExtension(); - FilePath removed_final = path.RemoveFinalExtension(); - EXPECT_EQ(cases[i].expected, removed.value()) << "i: " << i << - ", path: " << path.value(); - EXPECT_EQ(cases[i].expected, removed_final.value()) << "i: " << i << - ", path: " << path.value(); - } - { - FilePath path(FPL("foo.tar.gz")); - FilePath removed = path.RemoveExtension(); - FilePath removed_final = path.RemoveFinalExtension(); - EXPECT_EQ(FPL("foo"), removed.value()) << ", path: " << path.value(); - EXPECT_EQ(FPL("foo.tar"), removed_final.value()) << ", path: " - << path.value(); - } -} - -TEST_F(FilePathTest, ReplaceExtension) { - const struct BinaryTestData cases[] = { - { { FPL(""), FPL("") }, FPL("") }, - { { FPL(""), FPL("txt") }, FPL("") }, - { { FPL("."), FPL("txt") }, FPL("") }, - { { FPL(".."), FPL("txt") }, FPL("") }, - { { FPL("."), FPL("") }, FPL("") }, - { { FPL("foo.dll"), FPL("txt") }, FPL("foo.txt") }, - { { FPL("./foo.dll"), FPL("txt") }, FPL("./foo.txt") }, - { { FPL("foo..dll"), FPL("txt") }, FPL("foo..txt") }, - { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.txt") }, - { { FPL("foo"), FPL("txt") }, FPL("foo.txt") }, - { { FPL("foo."), FPL("txt") }, FPL("foo.txt") }, - { { FPL("foo.."), FPL("txt") }, FPL("foo..txt") }, - { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") }, - { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baz.txt") }, - { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.txt") }, - { { FPL("foo.dll"), FPL("") }, FPL("foo") }, - { { FPL("foo.dll"), FPL(".") }, FPL("foo") }, - { { FPL("foo"), FPL("") }, FPL("foo") }, - { { FPL("foo"), FPL(".") }, FPL("foo") }, - { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz") }, - { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz") }, -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { { FPL("C:\\foo.bar\\foo"), FPL("baz") }, FPL("C:\\foo.bar\\foo.baz") }, - { { FPL("C:\\foo.bar\\..\\\\"), FPL("baz") }, FPL("") }, -#endif - { { FPL("/foo.bar/foo"), FPL("baz") }, FPL("/foo.bar/foo.baz") }, - { { FPL("/foo.bar/..////"), FPL("baz") }, FPL("") }, - }; - for (unsigned int i = 0; i < arraysize(cases); ++i) { - FilePath path(cases[i].inputs[0]); - FilePath replaced = path.ReplaceExtension(cases[i].inputs[1]); - EXPECT_EQ(cases[i].expected, replaced.value()) << "i: " << i << - ", path: " << path.value() << ", replace: " << cases[i].inputs[1]; - } -} - -TEST_F(FilePathTest, AddExtension) { - const struct BinaryTestData cases[] = { - { { FPL(""), FPL("") }, FPL("") }, - { { FPL(""), FPL("txt") }, FPL("") }, - { { FPL("."), FPL("txt") }, FPL("") }, - { { FPL(".."), FPL("txt") }, FPL("") }, - { { FPL("."), FPL("") }, FPL("") }, - { { FPL("foo.dll"), FPL("txt") }, FPL("foo.dll.txt") }, - { { FPL("./foo.dll"), FPL("txt") }, FPL("./foo.dll.txt") }, - { { FPL("foo..dll"), FPL("txt") }, FPL("foo..dll.txt") }, - { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.dll.txt") }, - { { FPL("foo"), FPL("txt") }, FPL("foo.txt") }, - { { FPL("foo."), FPL("txt") }, FPL("foo.txt") }, - { { FPL("foo.."), FPL("txt") }, FPL("foo..txt") }, - { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") }, - { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baz.dll.txt") }, - { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.dll.txt") }, - { { FPL("foo.dll"), FPL("") }, FPL("foo.dll") }, - { { FPL("foo.dll"), FPL(".") }, FPL("foo.dll") }, - { { FPL("foo"), FPL("") }, FPL("foo") }, - { { FPL("foo"), FPL(".") }, FPL("foo") }, - { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz.dll") }, - { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz.dll") }, -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { { FPL("C:\\foo.bar\\foo"), FPL("baz") }, FPL("C:\\foo.bar\\foo.baz") }, - { { FPL("C:\\foo.bar\\..\\\\"), FPL("baz") }, FPL("") }, -#endif - { { FPL("/foo.bar/foo"), FPL("baz") }, FPL("/foo.bar/foo.baz") }, - { { FPL("/foo.bar/..////"), FPL("baz") }, FPL("") }, - }; - for (unsigned int i = 0; i < arraysize(cases); ++i) { - FilePath path(cases[i].inputs[0]); - FilePath added = path.AddExtension(cases[i].inputs[1]); - EXPECT_EQ(cases[i].expected, added.value()) << "i: " << i << - ", path: " << path.value() << ", add: " << cases[i].inputs[1]; - } -} - -TEST_F(FilePathTest, MatchesExtension) { - const struct BinaryBooleanTestData cases[] = { - { { FPL("foo"), FPL("") }, true}, - { { FPL("foo"), FPL(".") }, false}, - { { FPL("foo."), FPL("") }, false}, - { { FPL("foo."), FPL(".") }, true}, - { { FPL("foo.txt"), FPL(".dll") }, false}, - { { FPL("foo.txt"), FPL(".txt") }, true}, - { { FPL("foo.txt.dll"), FPL(".txt") }, false}, - { { FPL("foo.txt.dll"), FPL(".dll") }, true}, - { { FPL("foo.TXT"), FPL(".txt") }, true}, - { { FPL("foo.txt"), FPL(".TXT") }, true}, - { { FPL("foo.tXt"), FPL(".txt") }, true}, - { { FPL("foo.txt"), FPL(".tXt") }, true}, - { { FPL("foo.tXt"), FPL(".TXT") }, true}, - { { FPL("foo.tXt"), FPL(".tXt") }, true}, -#if defined(FILE_PATH_USES_DRIVE_LETTERS) - { { FPL("c:/foo.txt.dll"), FPL(".txt") }, false}, - { { FPL("c:/foo.txt"), FPL(".txt") }, true}, -#endif // FILE_PATH_USES_DRIVE_LETTERS -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - { { FPL("c:\\bar\\foo.txt.dll"), FPL(".txt") }, false}, - { { FPL("c:\\bar\\foo.txt"), FPL(".txt") }, true}, -#endif // FILE_PATH_USES_DRIVE_LETTERS - { { FPL("/bar/foo.txt.dll"), FPL(".txt") }, false}, - { { FPL("/bar/foo.txt"), FPL(".txt") }, true}, -#if defined(OS_WIN) || defined(OS_MACOSX) - // Umlauts A, O, U: direct comparison, and upper case vs. lower case - { { FPL("foo.\u00E4\u00F6\u00FC"), FPL(".\u00E4\u00F6\u00FC") }, true}, - { { FPL("foo.\u00C4\u00D6\u00DC"), FPL(".\u00E4\u00F6\u00FC") }, true}, - // C with circumflex: direct comparison, and upper case vs. lower case - { { FPL("foo.\u0109"), FPL(".\u0109") }, true}, - { { FPL("foo.\u0108"), FPL(".\u0109") }, true}, -#endif - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath path(cases[i].inputs[0]); - FilePath::StringType ext(cases[i].inputs[1]); - - EXPECT_EQ(cases[i].expected, path.MatchesExtension(ext)) << - "i: " << i << ", path: " << path.value() << ", ext: " << ext; - } -} - -TEST_F(FilePathTest, CompareIgnoreCase) { - const struct BinaryIntTestData cases[] = { - { { FPL("foo"), FPL("foo") }, 0}, - { { FPL("FOO"), FPL("foo") }, 0}, - { { FPL("foo.ext"), FPL("foo.ext") }, 0}, - { { FPL("FOO.EXT"), FPL("foo.ext") }, 0}, - { { FPL("Foo.Ext"), FPL("foo.ext") }, 0}, - { { FPL("foO"), FPL("foo") }, 0}, - { { FPL("foo"), FPL("foO") }, 0}, - { { FPL("fOo"), FPL("foo") }, 0}, - { { FPL("foo"), FPL("fOo") }, 0}, - { { FPL("bar"), FPL("foo") }, -1}, - { { FPL("foo"), FPL("bar") }, 1}, - { { FPL("BAR"), FPL("foo") }, -1}, - { { FPL("FOO"), FPL("bar") }, 1}, - { { FPL("bar"), FPL("FOO") }, -1}, - { { FPL("foo"), FPL("BAR") }, 1}, - { { FPL("BAR"), FPL("FOO") }, -1}, - { { FPL("FOO"), FPL("BAR") }, 1}, - // German "Eszett" (lower case and the new-fangled upper case) - // Note that uc() => "SS", NOT ! - // However, neither Windows nor Mac OSX converts these. - // (or even have glyphs for ) - { { FPL("\u00DF"), FPL("\u00DF") }, 0}, - { { FPL("\u1E9E"), FPL("\u1E9E") }, 0}, - { { FPL("\u00DF"), FPL("\u1E9E") }, -1}, - { { FPL("SS"), FPL("\u00DF") }, -1}, - { { FPL("SS"), FPL("\u1E9E") }, -1}, -#if defined(OS_WIN) || defined(OS_MACOSX) - // Umlauts A, O, U: direct comparison, and upper case vs. lower case - { { FPL("\u00E4\u00F6\u00FC"), FPL("\u00E4\u00F6\u00FC") }, 0}, - { { FPL("\u00C4\u00D6\u00DC"), FPL("\u00E4\u00F6\u00FC") }, 0}, - // C with circumflex: direct comparison, and upper case vs. lower case - { { FPL("\u0109"), FPL("\u0109") }, 0}, - { { FPL("\u0108"), FPL("\u0109") }, 0}, - // Cyrillic letter SHA: direct comparison, and upper case vs. lower case - { { FPL("\u0428"), FPL("\u0428") }, 0}, - { { FPL("\u0428"), FPL("\u0448") }, 0}, - // Greek letter DELTA: direct comparison, and upper case vs. lower case - { { FPL("\u0394"), FPL("\u0394") }, 0}, - { { FPL("\u0394"), FPL("\u03B4") }, 0}, - // Japanese full-width A: direct comparison, and upper case vs. lower case - // Note that full-width and standard characters are considered different. - { { FPL("\uFF21"), FPL("\uFF21") }, 0}, - { { FPL("\uFF21"), FPL("\uFF41") }, 0}, - { { FPL("A"), FPL("\uFF21") }, -1}, - { { FPL("A"), FPL("\uFF41") }, -1}, - { { FPL("a"), FPL("\uFF21") }, -1}, - { { FPL("a"), FPL("\uFF41") }, -1}, -#endif -#if defined(OS_MACOSX) - // Codepoints > 0x1000 - // Georgian letter DON: direct comparison, and upper case vs. lower case - { { FPL("\u10A3"), FPL("\u10A3") }, 0}, - { { FPL("\u10A3"), FPL("\u10D3") }, 0}, - // Combining characters vs. pre-composed characters, upper and lower case - { { FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("\u1E31\u1E77\u1E53n") }, 0}, - { { FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("kuon") }, 1}, - { { FPL("kuon"), FPL("k\u0301u\u032Do\u0304\u0301n") }, -1}, - { { FPL("K\u0301U\u032DO\u0304\u0301N"), FPL("KUON") }, 1}, - { { FPL("KUON"), FPL("K\u0301U\u032DO\u0304\u0301N") }, -1}, - { { FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("KUON") }, 1}, - { { FPL("K\u0301U\u032DO\u0304\u0301N"), FPL("\u1E31\u1E77\u1E53n") }, 0}, - { { FPL("k\u0301u\u032Do\u0304\u0301n"), FPL("\u1E30\u1E76\u1E52n") }, 0}, - { { FPL("k\u0301u\u032Do\u0304\u0302n"), FPL("\u1E30\u1E76\u1E52n") }, 1}, -#endif - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath::StringType s1(cases[i].inputs[0]); - FilePath::StringType s2(cases[i].inputs[1]); - int result = FilePath::CompareIgnoreCase(s1, s2); - EXPECT_EQ(cases[i].expected, result) << - "i: " << i << ", s1: " << s1 << ", s2: " << s2; - } -} - -TEST_F(FilePathTest, ReferencesParent) { - const struct UnaryBooleanTestData cases[] = { - { FPL("."), false }, - { FPL(".."), true }, - { FPL(".. "), true }, - { FPL(" .."), true }, - { FPL("..."), true }, - { FPL("a.."), false }, - { FPL("..a"), false }, - { FPL("../"), true }, - { FPL("/.."), true }, - { FPL("/../"), true }, - { FPL("/a../"), false }, - { FPL("/..a/"), false }, - { FPL("//.."), true }, - { FPL("..//"), true }, - { FPL("//..//"), true }, - { FPL("a//..//c"), true }, - { FPL("../b/c"), true }, - { FPL("/../b/c"), true }, - { FPL("a/b/.."), true }, - { FPL("a/b/../"), true }, - { FPL("a/../c"), true }, - { FPL("a/b/c"), false }, - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath input(cases[i].input); - bool observed = input.ReferencesParent(); - EXPECT_EQ(cases[i].expected, observed) << - "i: " << i << ", input: " << input.value(); - } -} - -TEST_F(FilePathTest, FromUTF8Unsafe_And_AsUTF8Unsafe) { - const struct UTF8TestData cases[] = { - { FPL("foo.txt"), "foo.txt" }, - // "aeo" with accents. Use http://0xcc.net/jsescape/ to decode them. - { FPL("\u00E0\u00E8\u00F2.txt"), "\xC3\xA0\xC3\xA8\xC3\xB2.txt" }, - // Full-width "ABC". - { FPL("\uFF21\uFF22\uFF23.txt"), - "\xEF\xBC\xA1\xEF\xBC\xA2\xEF\xBC\xA3.txt" }, - }; - -#if !defined(SYSTEM_NATIVE_UTF8) && defined(OS_LINUX) - ScopedLocale locale("en_US.UTF-8"); -#endif - - for (size_t i = 0; i < arraysize(cases); ++i) { - // Test FromUTF8Unsafe() works. - FilePath from_utf8 = FilePath::FromUTF8Unsafe(cases[i].utf8); - EXPECT_EQ(cases[i].native, from_utf8.value()) - << "i: " << i << ", input: " << cases[i].native; - // Test AsUTF8Unsafe() works. - FilePath from_native = FilePath(cases[i].native); - EXPECT_EQ(cases[i].utf8, from_native.AsUTF8Unsafe()) - << "i: " << i << ", input: " << cases[i].native; - // Test the two file paths are identical. - EXPECT_EQ(from_utf8.value(), from_native.value()); - } -} - -TEST_F(FilePathTest, ConstructWithNUL) { - // Assert FPS() works. - ASSERT_EQ(3U, FPS("a\0b").length()); - - // Test constructor strips '\0' - FilePath path(FPS("a\0b")); - EXPECT_EQ(1U, path.value().length()); - EXPECT_EQ(FPL("a"), path.value()); -} - -TEST_F(FilePathTest, AppendWithNUL) { - // Assert FPS() works. - ASSERT_EQ(3U, FPS("b\0b").length()); - - // Test Append() strips '\0' - FilePath path(FPL("a")); - path = path.Append(FPS("b\0b")); - EXPECT_EQ(3U, path.value().length()); -#if defined(FILE_PATH_USES_WIN_SEPARATORS) - EXPECT_EQ(FPL("a\\b"), path.value()); -#else - EXPECT_EQ(FPL("a/b"), path.value()); -#endif -} - -TEST_F(FilePathTest, ReferencesParentWithNUL) { - // Assert FPS() works. - ASSERT_EQ(3U, FPS("..\0").length()); - - // Test ReferencesParent() doesn't break with "..\0" - FilePath path(FPS("..\0")); - EXPECT_TRUE(path.ReferencesParent()); -} - -#if defined(FILE_PATH_USES_WIN_SEPARATORS) -TEST_F(FilePathTest, NormalizePathSeparators) { - const struct UnaryTestData cases[] = { - { FPL("foo/bar"), FPL("foo\\bar") }, - { FPL("foo/bar\\betz"), FPL("foo\\bar\\betz") }, - { FPL("foo\\bar"), FPL("foo\\bar") }, - { FPL("foo\\bar/betz"), FPL("foo\\bar\\betz") }, - { FPL("foo"), FPL("foo") }, - // Trailing slashes don't automatically get stripped. That's what - // StripTrailingSeparators() is for. - { FPL("foo\\"), FPL("foo\\") }, - { FPL("foo/"), FPL("foo\\") }, - { FPL("foo/bar\\"), FPL("foo\\bar\\") }, - { FPL("foo\\bar/"), FPL("foo\\bar\\") }, - { FPL("foo/bar/"), FPL("foo\\bar\\") }, - { FPL("foo\\bar\\"), FPL("foo\\bar\\") }, - { FPL("\\foo/bar"), FPL("\\foo\\bar") }, - { FPL("/foo\\bar"), FPL("\\foo\\bar") }, - { FPL("c:/foo/bar/"), FPL("c:\\foo\\bar\\") }, - { FPL("/foo/bar/"), FPL("\\foo\\bar\\") }, - { FPL("\\foo\\bar\\"), FPL("\\foo\\bar\\") }, - { FPL("c:\\foo/bar"), FPL("c:\\foo\\bar") }, - { FPL("//foo\\bar\\"), FPL("\\\\foo\\bar\\") }, - { FPL("\\\\foo\\bar\\"), FPL("\\\\foo\\bar\\") }, - { FPL("//foo\\bar\\"), FPL("\\\\foo\\bar\\") }, - // This method does not normalize the number of path separators. - { FPL("foo\\\\bar"), FPL("foo\\\\bar") }, - { FPL("foo//bar"), FPL("foo\\\\bar") }, - { FPL("foo/\\bar"), FPL("foo\\\\bar") }, - { FPL("foo\\/bar"), FPL("foo\\\\bar") }, - { FPL("///foo\\\\bar"), FPL("\\\\\\foo\\\\bar") }, - { FPL("foo//bar///"), FPL("foo\\\\bar\\\\\\") }, - { FPL("foo/\\bar/\\"), FPL("foo\\\\bar\\\\") }, - { FPL("/\\foo\\/bar"), FPL("\\\\foo\\\\bar") }, - }; - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath input(cases[i].input); - FilePath observed = input.NormalizePathSeparators(); - EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) << - "i: " << i << ", input: " << input.value(); - } -} -#endif - -TEST_F(FilePathTest, EndsWithSeparator) { - const UnaryBooleanTestData cases[] = { - { FPL(""), false }, - { FPL("/"), true }, - { FPL("foo/"), true }, - { FPL("bar"), false }, - { FPL("/foo/bar"), false }, - }; - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath input = FilePath(cases[i].input).NormalizePathSeparators(); - EXPECT_EQ(cases[i].expected, input.EndsWithSeparator()); - } -} - -TEST_F(FilePathTest, AsEndingWithSeparator) { - const UnaryTestData cases[] = { - { FPL(""), FPL("") }, - { FPL("/"), FPL("/") }, - { FPL("foo"), FPL("foo/") }, - { FPL("foo/"), FPL("foo/") } - }; - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath input = FilePath(cases[i].input).NormalizePathSeparators(); - FilePath expected = FilePath(cases[i].expected).NormalizePathSeparators(); - EXPECT_EQ(expected.value(), input.AsEndingWithSeparator().value()); - } -} - -#if defined(OS_ANDROID) -TEST_F(FilePathTest, ContentUriTest) { - const struct UnaryBooleanTestData cases[] = { - { FPL("content://foo.bar"), true }, - { FPL("content://foo.bar/"), true }, - { FPL("content://foo/bar"), true }, - { FPL("CoNTenT://foo.bar"), true }, - { FPL("content://"), true }, - { FPL("content:///foo.bar"), true }, - { FPL("content://3foo/bar"), true }, - { FPL("content://_foo/bar"), true }, - { FPL(".. "), false }, - { FPL("foo.bar"), false }, - { FPL("content:foo.bar"), false }, - { FPL("content:/foo.ba"), false }, - { FPL("content:/dir/foo.bar"), false }, - { FPL("content: //foo.bar"), false }, - { FPL("content%2a%2f%2f"), false }, - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath input(cases[i].input); - bool observed = input.IsContentUri(); - EXPECT_EQ(cases[i].expected, observed) << - "i: " << i << ", input: " << input.value(); - } -} -#endif - -// Test the operator<<(ostream, FilePath). -TEST_F(FilePathTest, PrintToOstream) { - std::stringstream ss; - FilePath fp(FPL("foo")); - ss << fp; - EXPECT_EQ("foo", ss.str()); -} - -// Test GetHFSDecomposedForm should return empty result for invalid UTF-8 -// strings. -#if defined(OS_MACOSX) -TEST_F(FilePathTest, GetHFSDecomposedFormWithInvalidInput) { - const FilePath::CharType* cases[] = { - FPL("\xc3\x28"), - FPL("\xe2\x82\x28"), - FPL("\xe2\x28\xa1"), - FPL("\xf0\x28\x8c\xbc"), - FPL("\xf0\x28\x8c\x28"), - }; - for (auto* invalid_input : cases) { - FilePath::StringType observed = FilePath::GetHFSDecomposedForm( - invalid_input); - EXPECT_TRUE(observed.empty()); - } -} -#endif - -} // namespace base diff --git a/files/file_path_watcher.cc b/files/file_path_watcher.cc deleted file mode 100644 index af4034685..000000000 --- a/files/file_path_watcher.cc +++ /dev/null @@ -1,46 +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. - -// Cross platform methods for FilePathWatcher. See the various platform -// specific implementation files, too. - -#include "base/files/file_path_watcher.h" - -#include "base/logging.h" -#include "build/build_config.h" - -namespace base { - -FilePathWatcher::~FilePathWatcher() { - DCHECK(sequence_checker_.CalledOnValidSequence()); - impl_->Cancel(); -} - -// static -bool FilePathWatcher::RecursiveWatchAvailable() { -#if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN) || \ - defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) - return true; -#else - // FSEvents isn't available on iOS. - return false; -#endif -} - -FilePathWatcher::PlatformDelegate::PlatformDelegate(): cancelled_(false) { -} - -FilePathWatcher::PlatformDelegate::~PlatformDelegate() { - DCHECK(is_cancelled()); -} - -bool FilePathWatcher::Watch(const FilePath& path, - bool recursive, - const Callback& callback) { - DCHECK(sequence_checker_.CalledOnValidSequence()); - DCHECK(path.IsAbsolute()); - return impl_->Watch(path, recursive, callback); -} - -} // namespace base diff --git a/files/file_path_watcher.h b/files/file_path_watcher.h deleted file mode 100644 index 9e29d0a9d..000000000 --- a/files/file_path_watcher.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This module provides a way to monitor a file or directory for changes. - -#ifndef BASE_FILES_FILE_PATH_WATCHER_H_ -#define BASE_FILES_FILE_PATH_WATCHER_H_ - -#include - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/sequence_checker.h" -#include "base/sequenced_task_runner.h" - -namespace base { - -// This class lets you register interest in changes on a FilePath. -// The callback will get called whenever the file or directory referenced by the -// FilePath is changed, including created or deleted. Due to limitations in the -// underlying OS APIs, FilePathWatcher has slightly different semantics on OS X -// than on Windows or Linux. FilePathWatcher on Linux and Windows will detect -// modifications to files in a watched directory. FilePathWatcher on Mac will -// detect the creation and deletion of files in a watched directory, but will -// not detect modifications to those files. See file_path_watcher_kqueue.cc for -// details. -// -// Must be destroyed on the sequence that invokes Watch(). -class BASE_EXPORT FilePathWatcher { - public: - // Callback type for Watch(). |path| points to the file that was updated, - // and |error| is true if the platform specific code detected an error. In - // that case, the callback won't be invoked again. - typedef base::Callback Callback; - - // Used internally to encapsulate different members on different platforms. - class PlatformDelegate { - public: - PlatformDelegate(); - virtual ~PlatformDelegate(); - - // Start watching for the given |path| and notify |delegate| about changes. - virtual bool Watch(const FilePath& path, - bool recursive, - const Callback& callback) WARN_UNUSED_RESULT = 0; - - // Stop watching. This is called from FilePathWatcher's dtor in order to - // allow to shut down properly while the object is still alive. - virtual void Cancel() = 0; - - protected: - friend class FilePathWatcher; - - scoped_refptr task_runner() const { - return task_runner_; - } - - void set_task_runner(scoped_refptr runner) { - task_runner_ = std::move(runner); - } - - // Must be called before the PlatformDelegate is deleted. - void set_cancelled() { - cancelled_ = true; - } - - bool is_cancelled() const { - return cancelled_; - } - - private: - scoped_refptr task_runner_; - bool cancelled_; - - DISALLOW_COPY_AND_ASSIGN(PlatformDelegate); - }; - - FilePathWatcher(); - ~FilePathWatcher(); - - // 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. Set |recursive| to true to watch |path| and its - // children. The callback will be invoked on the same sequence. Returns true - // on success. - // - // On POSIX, this must be called from a thread that supports - // FileDescriptorWatcher. - // - // 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); - - private: - std::unique_ptr impl_; - - SequenceChecker sequence_checker_; - - DISALLOW_COPY_AND_ASSIGN(FilePathWatcher); -}; - -} // namespace base - -#endif // BASE_FILES_FILE_PATH_WATCHER_H_ diff --git a/files/file_path_watcher_fsevents.cc b/files/file_path_watcher_fsevents.cc deleted file mode 100644 index 49ed36ba4..000000000 --- a/files/file_path_watcher_fsevents.cc +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/file_path_watcher_fsevents.h" - -#include - -#include - -#include "base/bind.h" -#include "base/files/file_util.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/strings/stringprintf.h" -#include "base/threading/sequenced_task_runner_handle.h" - -namespace base { - -namespace { - -// The latency parameter passed to FSEventsStreamCreate(). -const CFAbsoluteTime kEventLatencySeconds = 0.3; - -// Resolve any symlinks in the path. -FilePath ResolvePath(const FilePath& path) { - const unsigned kMaxLinksToResolve = 255; - - std::vector component_vector; - path.GetComponents(&component_vector); - std::list - 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 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; -} - -} // namespace - -FilePathWatcherFSEvents::FilePathWatcherFSEvents() - : queue_(dispatch_queue_create( - base::StringPrintf("org.chromium.base.FilePathWatcher.%p", this) - .c_str(), - DISPATCH_QUEUE_SERIAL)), - fsevent_stream_(nullptr), - weak_factory_(this) {} - -FilePathWatcherFSEvents::~FilePathWatcherFSEvents() { - DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence()); - DCHECK(callback_.is_null()) - << "Cancel() must be called before FilePathWatcher is destroyed."; -} - -bool FilePathWatcherFSEvents::Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) { - DCHECK(!callback.is_null()); - DCHECK(callback_.is_null()); - - // This class could support non-recursive watches, but that is currently - // left to FilePathWatcherKQueue. - if (!recursive) - return false; - - set_task_runner(SequencedTaskRunnerHandle::Get()); - callback_ = callback; - - FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); - // The block runtime would implicitly capture the reference, not the object - // it's referencing. Copy the path into a local, so that the value is - // captured by the block's scope. - const FilePath path_copy(path); - - dispatch_async(queue_, ^{ - StartEventStream(start_event, path_copy); - }); - return true; -} - -void FilePathWatcherFSEvents::Cancel() { - set_cancelled(); - callback_.Reset(); - - // Switch to the dispatch queue to tear down the event stream. As the queue is - // owned by |this|, and this method is called from the destructor, execute the - // block synchronously. - dispatch_sync(queue_, ^{ - if (fsevent_stream_) { - DestroyEventStream(); - target_.clear(); - resolved_target_.clear(); - } - }); -} - -// static -void FilePathWatcherFSEvents::FSEventsCallback( - ConstFSEventStreamRef stream, - void* event_watcher, - size_t num_events, - void* event_paths, - const FSEventStreamEventFlags flags[], - const FSEventStreamEventId event_ids[]) { - FilePathWatcherFSEvents* watcher = - reinterpret_cast(event_watcher); - bool root_changed = watcher->ResolveTargetPath(); - std::vector 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(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 do the reset asynchronously. - // - // We can't dispatch_async a call to UpdateEventStream() directly because - // there would be no guarantee that |watcher| still exists when it runs. - // - // Instead, bounce on task_runner() and use a WeakPtr to verify that - // |watcher| still exists. If it does, dispatch_async a call to - // UpdateEventStream(). Because the destructor of |watcher| runs on - // task_runner() and calls dispatch_sync, it is guaranteed that |watcher| - // still exists when UpdateEventStream() runs. - watcher->task_runner()->PostTask( - FROM_HERE, Bind( - [](WeakPtr weak_watcher, - FSEventStreamEventId root_change_at) { - if (!weak_watcher) - return; - FilePathWatcherFSEvents* watcher = weak_watcher.get(); - dispatch_async(watcher->queue_, ^{ - watcher->UpdateEventStream(root_change_at); - }); - }, - watcher->weak_factory_.GetWeakPtr(), root_change_at)); - } - - watcher->OnFilePathsChanged(paths); -} - -void FilePathWatcherFSEvents::OnFilePathsChanged( - const std::vector& paths) { - DCHECK(!resolved_target_.empty()); - task_runner()->PostTask( - FROM_HERE, - Bind(&FilePathWatcherFSEvents::DispatchEvents, weak_factory_.GetWeakPtr(), - paths, target_, resolved_target_)); -} - -void FilePathWatcherFSEvents::DispatchEvents(const std::vector& paths, - const FilePath& target, - const FilePath& resolved_target) { - DCHECK(task_runner()->RunsTasksInCurrentSequence()); - - // Don't issue callbacks after Cancel() has been called. - if (is_cancelled() || callback_.is_null()) { - return; - } - - for (const FilePath& path : paths) { - if (resolved_target.IsParent(path) || resolved_target == path) { - callback_.Run(target, false); - return; - } - } -} - -void FilePathWatcherFSEvents::UpdateEventStream( - FSEventStreamEventId start_event) { - // 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 (resolved_target_.empty()) - return; - - if (fsevent_stream_) - DestroyEventStream(); - - ScopedCFTypeRef cf_path(CFStringCreateWithCString( - NULL, resolved_target_.value().c_str(), kCFStringEncodingMacHFS)); - ScopedCFTypeRef cf_dir_path(CFStringCreateWithCString( - NULL, resolved_target_.DirName().value().c_str(), - kCFStringEncodingMacHFS)); - CFStringRef paths_array[] = { cf_path.get(), cf_dir_path.get() }; - ScopedCFTypeRef watched_paths(CFArrayCreate( - NULL, reinterpret_cast(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_, queue_); - - if (!FSEventStreamStart(fsevent_stream_)) { - task_runner()->PostTask(FROM_HERE, - Bind(&FilePathWatcherFSEvents::ReportError, - weak_factory_.GetWeakPtr(), target_)); - } -} - -bool FilePathWatcherFSEvents::ResolveTargetPath() { - FilePath resolved = ResolvePath(target_).StripTrailingSeparators(); - bool changed = resolved != resolved_target_; - resolved_target_ = resolved; - if (resolved_target_.empty()) { - task_runner()->PostTask(FROM_HERE, - Bind(&FilePathWatcherFSEvents::ReportError, - weak_factory_.GetWeakPtr(), target_)); - } - return changed; -} - -void FilePathWatcherFSEvents::ReportError(const FilePath& target) { - DCHECK(task_runner()->RunsTasksInCurrentSequence()); - if (!callback_.is_null()) { - callback_.Run(target, true); - } -} - -void FilePathWatcherFSEvents::DestroyEventStream() { - FSEventStreamStop(fsevent_stream_); - FSEventStreamInvalidate(fsevent_stream_); - FSEventStreamRelease(fsevent_stream_); - fsevent_stream_ = NULL; -} - -void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event, - const FilePath& path) { - DCHECK(resolved_target_.empty()); - - target_ = path; - ResolveTargetPath(); - UpdateEventStream(start_event); -} - -} // namespace base diff --git a/files/file_path_watcher_fsevents.h b/files/file_path_watcher_fsevents.h deleted file mode 100644 index dcdf2fbf9..000000000 --- a/files/file_path_watcher_fsevents.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_ -#define BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_ - -#include -#include - -#include - -#include "base/files/file_path.h" -#include "base/files/file_path_watcher.h" -#include "base/mac/scoped_dispatch_object.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.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(); - ~FilePathWatcherFSEvents() override; - - // FilePathWatcher::PlatformDelegate overrides. - bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override; - void Cancel() override; - - private: - static void FSEventsCallback(ConstFSEventStreamRef stream, - void* event_watcher, - size_t num_events, - void* event_paths, - const FSEventStreamEventFlags flags[], - const FSEventStreamEventId event_ids[]); - - // Called from FSEventsCallback whenever there is a change to the paths. - void OnFilePathsChanged(const std::vector& paths); - - // Called on the message_loop() thread to dispatch path events. Can't access - // target_ and resolved_target_ directly as those are modified on the - // libdispatch thread. - void DispatchEvents(const std::vector& paths, - const FilePath& target, - const FilePath& resolved_target); - - // (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(); - - // Report an error watching the given target. - void ReportError(const FilePath& target); - - // Destroy the event stream. - void DestroyEventStream(); - - // Start watching the FSEventStream. - void StartEventStream(FSEventStreamEventId start_event, const FilePath& path); - - // Callback to notify upon changes. - // (Only accessed from the message_loop() thread.) - FilePathWatcher::Callback callback_; - - // The dispatch queue on which the the event stream is scheduled. - ScopedDispatchObject queue_; - - // Target path to watch (passed to callback). - // (Only accessed from the libdispatch queue.) - FilePath target_; - - // Target path with all symbolic links resolved. - // (Only accessed from the libdispatch queue.) - FilePath resolved_target_; - - // Backend stream we receive event callbacks from (strong reference). - // (Only accessed from the libdispatch queue.) - FSEventStreamRef fsevent_stream_; - - WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(FilePathWatcherFSEvents); -}; - -} // namespace base - -#endif // BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_ diff --git a/files/file_path_watcher_fuchsia.cc b/files/file_path_watcher_fuchsia.cc deleted file mode 100644 index 53c927e57..000000000 --- a/files/file_path_watcher_fuchsia.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2017 The Chromium 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/macros.h" -#include "base/memory/ptr_util.h" -#include "base/threading/sequenced_task_runner_handle.h" - -namespace base { - -namespace { - -class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { - public: - FilePathWatcherImpl() {} - ~FilePathWatcherImpl() override {} - - bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override; - - void Cancel() override; - - private: - FilePathWatcher::Callback callback_; - FilePath target_; - - DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); -}; - -bool FilePathWatcherImpl::Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) { - DCHECK(!callback.is_null()); - DCHECK(callback_.is_null()); - - callback_ = callback; - NOTIMPLEMENTED(); - return false; -} - -void FilePathWatcherImpl::Cancel() { - NOTIMPLEMENTED(); -} - -} // namespace - -FilePathWatcher::FilePathWatcher() { - sequence_checker_.DetachFromSequence(); - impl_ = std::make_unique(); -} - -} // namespace base diff --git a/files/file_path_watcher_kqueue.cc b/files/file_path_watcher_kqueue.cc deleted file mode 100644 index 036809d6c..000000000 --- a/files/file_path_watcher_kqueue.cc +++ /dev/null @@ -1,372 +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/files/file_path_watcher_kqueue.h" - -#include -#include -#include - -#include "base/bind.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/strings/stringprintf.h" -#include "base/threading/sequenced_task_runner_handle.h" - -// On some platforms these are not defined. -#if !defined(EV_RECEIPT) -#define EV_RECEIPT 0 -#endif -#if !defined(O_EVTONLY) -#define O_EVTONLY O_RDONLY -#endif - -namespace base { - -FilePathWatcherKQueue::FilePathWatcherKQueue() : kqueue_(-1) {} - -FilePathWatcherKQueue::~FilePathWatcherKQueue() { - DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence()); -} - -void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) { - CloseFileDescriptor(&event.ident); - EventData* entry = EventDataForKevent(event); - delete entry; - event.udata = NULL; -} - -int FilePathWatcherKQueue::EventsForPath(FilePath path, EventVector* events) { - // Make sure that we are working with a clean slate. - DCHECK(events->empty()); - - std::vector components; - path.GetComponents(&components); - - if (components.size() < 1) { - return -1; - } - - int last_existing_entry = 0; - FilePath built_path; - bool path_still_exists = true; - for (std::vector::iterator i = components.begin(); - i != components.end(); ++i) { - if (i == components.begin()) { - built_path = FilePath(*i); - } else { - built_path = built_path.Append(*i); - } - uintptr_t fd = kNoFileDescriptor; - if (path_still_exists) { - fd = FileDescriptorForPath(built_path); - if (fd == kNoFileDescriptor) { - path_still_exists = false; - } else { - ++last_existing_entry; - } - } - FilePath::StringType subdir = (i != (components.end() - 1)) ? *(i + 1) : ""; - EventData* data = new EventData(built_path, subdir); - struct kevent event; - EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT), - (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB | - NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data); - events->push_back(event); - } - return last_existing_entry; -} - -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 FilePathWatcherKQueue::CloseFileDescriptor(uintptr_t* fd) { - if (*fd == kNoFileDescriptor) { - return; - } - - if (IGNORE_EINTR(close(*fd)) != 0) { - DPLOG(ERROR) << "close"; - } - *fd = kNoFileDescriptor; -} - -bool FilePathWatcherKQueue::AreKeventValuesValid(struct kevent* kevents, - int count) { - if (count < 0) { - DPLOG(ERROR) << "kevent"; - return false; - } - bool valid = true; - for (int i = 0; i < count; ++i) { - if (kevents[i].flags & EV_ERROR && kevents[i].data) { - // Find the kevent in |events_| that matches the kevent with the error. - EventVector::iterator event = events_.begin(); - for (; event != events_.end(); ++event) { - if (event->ident == kevents[i].ident) { - break; - } - } - std::string path_name; - if (event != events_.end()) { - EventData* event_data = EventDataForKevent(*event); - if (event_data != NULL) { - path_name = event_data->path_.value(); - } - } - if (path_name.empty()) { - path_name = base::StringPrintf( - "fd %ld", reinterpret_cast(&kevents[i].ident)); - } - DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name; - valid = false; - } - } - return valid; -} - -void FilePathWatcherKQueue::HandleAttributesChange( - const EventVector::iterator& event, - bool* target_file_affected, - bool* update_watches) { - EventVector::iterator next_event = event + 1; - EventData* next_event_data = EventDataForKevent(*next_event); - // Check to see if the next item in path is still accessible. - uintptr_t have_access = FileDescriptorForPath(next_event_data->path_); - if (have_access == kNoFileDescriptor) { - *target_file_affected = true; - *update_watches = true; - EventVector::iterator local_event(event); - for (; local_event != events_.end(); ++local_event) { - // Close all nodes from the event down. This has the side effect of - // potentially rendering other events in |updates| invalid. - // There is no need to remove the events from |kqueue_| because this - // happens as a side effect of closing the file descriptor. - CloseFileDescriptor(&local_event->ident); - } - } else { - CloseFileDescriptor(&have_access); - } -} - -void FilePathWatcherKQueue::HandleDeleteOrMoveChange( - const EventVector::iterator& event, - bool* target_file_affected, - bool* update_watches) { - *target_file_affected = true; - *update_watches = true; - EventVector::iterator local_event(event); - for (; local_event != events_.end(); ++local_event) { - // Close all nodes from the event down. This has the side effect of - // potentially rendering other events in |updates| invalid. - // There is no need to remove the events from |kqueue_| because this - // happens as a side effect of closing the file descriptor. - CloseFileDescriptor(&local_event->ident); - } -} - -void FilePathWatcherKQueue::HandleCreateItemChange( - const EventVector::iterator& event, - bool* target_file_affected, - bool* update_watches) { - // Get the next item in the path. - EventVector::iterator next_event = event + 1; - // Check to see if it already has a valid file descriptor. - if (!IsKeventFileDescriptorOpen(*next_event)) { - EventData* next_event_data = EventDataForKevent(*next_event); - // If not, attempt to open a file descriptor for it. - next_event->ident = FileDescriptorForPath(next_event_data->path_); - if (IsKeventFileDescriptorOpen(*next_event)) { - *update_watches = true; - if (next_event_data->subdir_.empty()) { - *target_file_affected = true; - } - } - } -} - -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. - // This is to get around races in directory creation in a watched path. - bool update_watches = true; - while (update_watches) { - size_t valid; - for (valid = 0; valid < events_.size(); ++valid) { - if (!IsKeventFileDescriptorOpen(events_[valid])) { - break; - } - } - if (valid == 0) { - // The root of the file path is inaccessible? - return false; - } - - EventVector updates(valid); - int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], valid, &updates[0], - valid, NULL)); - if (!AreKeventValuesValid(&updates[0], count)) { - return false; - } - update_watches = false; - for (; valid < events_.size(); ++valid) { - EventData* event_data = EventDataForKevent(events_[valid]); - events_[valid].ident = FileDescriptorForPath(event_data->path_); - if (IsKeventFileDescriptorOpen(events_[valid])) { - update_watches = true; - if (event_data->subdir_.empty()) { - *target_file_affected = true; - } - } else { - break; - } - } - } - return true; -} - -bool FilePathWatcherKQueue::Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) { - DCHECK(target_.value().empty()); // Can only watch one path. - DCHECK(!callback.is_null()); - DCHECK_EQ(kqueue_, -1); - // Recursive watch is not supported using kqueue. - DCHECK(!recursive); - - callback_ = callback; - target_ = path; - - set_task_runner(SequencedTaskRunnerHandle::Get()); - - kqueue_ = kqueue(); - if (kqueue_ == -1) { - DPLOG(ERROR) << "kqueue"; - return false; - } - - int last_entry = EventsForPath(target_, &events_); - DCHECK_NE(last_entry, 0); - - EventVector responses(last_entry); - - int count = HANDLE_EINTR(kevent(kqueue_, &events_[0], last_entry, - &responses[0], last_entry, NULL)); - if (!AreKeventValuesValid(&responses[0], count)) { - // Calling Cancel() here to close any file descriptors that were opened. - // This would happen in the destructor anyways, but FilePathWatchers tend to - // be long lived, and if an error has occurred, there is no reason to waste - // the file descriptors. - Cancel(); - return false; - } - - // It's safe to use Unretained() because the watch is cancelled and the - // callback cannot be invoked after |kqueue_watch_controller_| (which is a - // member of |this|) has been deleted. - kqueue_watch_controller_ = FileDescriptorWatcher::WatchReadable( - kqueue_, - Bind(&FilePathWatcherKQueue::OnKQueueReadable, Unretained(this))); - - return true; -} - -void FilePathWatcherKQueue::Cancel() { - if (!task_runner()) { - set_cancelled(); - return; - } - - DCHECK(task_runner()->RunsTasksInCurrentSequence()); - if (!is_cancelled()) { - set_cancelled(); - kqueue_watch_controller_.reset(); - if (IGNORE_EINTR(close(kqueue_)) != 0) { - DPLOG(ERROR) << "close kqueue"; - } - kqueue_ = -1; - std::for_each(events_.begin(), events_.end(), ReleaseEvent); - events_.clear(); - callback_.Reset(); - } -} - -void FilePathWatcherKQueue::OnKQueueReadable() { - DCHECK(task_runner()->RunsTasksInCurrentSequence()); - DCHECK(events_.size()); - - // Request the file system update notifications that have occurred and return - // them in |updates|. |count| will contain the number of updates that have - // occurred. - EventVector updates(events_.size()); - struct timespec timeout = {0, 0}; - int count = HANDLE_EINTR(kevent(kqueue_, NULL, 0, &updates[0], updates.size(), - &timeout)); - - // Error values are stored within updates, so check to make sure that no - // errors occurred. - if (!AreKeventValuesValid(&updates[0], count)) { - callback_.Run(target_, true /* error */); - Cancel(); - return; - } - - bool update_watches = false; - bool send_notification = false; - - // Iterate through each of the updates and react to them. - for (int i = 0; i < count; ++i) { - // Find our kevent record that matches the update notification. - EventVector::iterator event = events_.begin(); - for (; event != events_.end(); ++event) { - if (!IsKeventFileDescriptorOpen(*event) || - event->ident == updates[i].ident) { - break; - } - } - if (event == events_.end() || !IsKeventFileDescriptorOpen(*event)) { - // The event may no longer exist in |events_| because another event - // modified |events_| in such a way to make it invalid. For example if - // the path is /foo/bar/bam and foo is deleted, NOTE_DELETE events for - // foo, bar and bam will be sent. If foo is processed first, then - // the file descriptors for bar and bam will already be closed and set - // to -1 before they get a chance to be processed. - continue; - } - - EventData* event_data = EventDataForKevent(*event); - - // If the subdir is empty, this is the last item on the path and is the - // target file. - bool target_file_affected = event_data->subdir_.empty(); - if ((updates[i].fflags & NOTE_ATTRIB) && !target_file_affected) { - HandleAttributesChange(event, &target_file_affected, &update_watches); - } - if (updates[i].fflags & (NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME)) { - HandleDeleteOrMoveChange(event, &target_file_affected, &update_watches); - } - if ((updates[i].fflags & NOTE_WRITE) && !target_file_affected) { - HandleCreateItemChange(event, &target_file_affected, &update_watches); - } - send_notification |= target_file_affected; - } - - if (update_watches) { - if (!UpdateWatches(&send_notification)) { - callback_.Run(target_, true /* error */); - Cancel(); - } - } - - if (send_notification) { - callback_.Run(target_, false); - } -} - -} // namespace base diff --git a/files/file_path_watcher_kqueue.h b/files/file_path_watcher_kqueue.h deleted file mode 100644 index ef79be559..000000000 --- a/files/file_path_watcher_kqueue.h +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_ -#define BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_ - -#include - -#include -#include - -#include "base/files/file_descriptor_watcher_posix.h" -#include "base/files/file_path.h" -#include "base/files/file_path_watcher.h" -#include "base/macros.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: - FilePathWatcherKQueue(); - ~FilePathWatcherKQueue() override; - - // FilePathWatcher::PlatformDelegate overrides. - bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override; - void Cancel() override; - - 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 EventVector; - - // Called when data is available in |kqueue_|. - void OnKQueueReadable(); - - // 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(-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(event.udata); - } - - EventVector events_; - FilePathWatcher::Callback callback_; - FilePath target_; - int kqueue_; - - // Throughout the lifetime of this, OnKQueueReadable() will be called when - // data is available in |kqueue_|. - std::unique_ptr kqueue_watch_controller_; - - DISALLOW_COPY_AND_ASSIGN(FilePathWatcherKQueue); -}; - -} // namespace base - -#endif // BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_ diff --git a/files/file_path_watcher_linux.cc b/files/file_path_watcher_linux.cc deleted file mode 100644 index c58d6865c..000000000 --- a/files/file_path_watcher_linux.cc +++ /dev/null @@ -1,680 +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/files/file_path_watcher.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "base/bind.h" -#include "base/files/file_enumerator.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/lazy_instance.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/memory/weak_ptr.h" -#include "base/posix/eintr_wrapper.h" -#include "base/single_thread_task_runner.h" -#include "base/stl_util.h" -#include "base/synchronization/lock.h" -#include "base/threading/platform_thread.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "base/trace_event/trace_event.h" - -namespace base { - -namespace { - -class FilePathWatcherImpl; -class InotifyReader; - -class InotifyReaderThreadDelegate final : public PlatformThread::Delegate { - public: - InotifyReaderThreadDelegate(int inotify_fd) : inotify_fd_(inotify_fd){}; - - ~InotifyReaderThreadDelegate() override = default; - - private: - void ThreadMain() override; - - int inotify_fd_; - - DISALLOW_COPY_AND_ASSIGN(InotifyReaderThreadDelegate); -}; - -// Singleton to manage all inotify watches. -// TODO(tony): It would be nice if this wasn't a singleton. -// http://crbug.com/38174 -class InotifyReader { - public: - typedef int Watch; // Watch descriptor used by AddWatch and RemoveWatch. - static const Watch kInvalidWatch = -1; - - // Watch directory |path| for changes. |watcher| will be notified on each - // change. Returns kInvalidWatch on failure. - Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher); - - // Remove |watch| if it's valid. - void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher); - - // Callback for InotifyReaderTask. - void OnInotifyEvent(const inotify_event* event); - - private: - friend struct LazyInstanceTraitsBase; - - typedef std::set WatcherSet; - - InotifyReader(); - // There is no destructor because |g_inotify_reader| is a - // base::LazyInstace::Leaky object. Having a destructor causes build - // issues with GCC 6 (http://crbug.com/636346). - - // Returns true on successful thread creation. - bool StartThread(); - - // We keep track of which delegates want to be notified on which watches. - std::unordered_map watchers_; - - // Lock to protect watchers_. - Lock lock_; - - // File descriptor returned by inotify_init. - const int inotify_fd_; - - // Thread delegate for the Inotify thread. - InotifyReaderThreadDelegate thread_delegate_; - - // Flag set to true when startup was successful. - bool valid_; - - DISALLOW_COPY_AND_ASSIGN(InotifyReader); -}; - -class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { - public: - FilePathWatcherImpl(); - ~FilePathWatcherImpl() override; - - // 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|. - // - // |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 deleted, - bool is_dir); - - private: - void OnFilePathChangedOnOriginSequence(InotifyReader::Watch fired_watch, - const FilePath::StringType& child, - bool created, - bool deleted, - bool is_dir); - - // Start watching |path| for changes and notify |delegate| on each change. - // Returns true if watch for |path| has been added successfully. - bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override; - - // Cancel the watch. This unregisters the instance with InotifyReader. - void Cancel() override; - - // Inotify watches are installed for all directory components of |target_|. - // A WatchEntry instance holds: - // - |watch|: the watch descriptor for a component. - // - |subdir|: the subdirectory that identifies the next component. - // - For the last component, there is no next component, so it is empty. - // - |linkname|: the target of the symlink. - // - Only if the target being watched is a symbolic link. - struct WatchEntry { - explicit WatchEntry(const FilePath::StringType& dirname) - : watch(InotifyReader::kInvalidWatch), - subdir(dirname) {} - - InotifyReader::Watch watch; - FilePath::StringType subdir; - FilePath::StringType linkname; - }; - typedef std::vector WatchVector; - - // Reconfigure to watch for the most specific parent directory of |target_| - // 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. Update |watch_entry| on success. - void AddWatchForBrokenSymlink(const FilePath& path, WatchEntry* watch_entry); - - bool HasValidWatchVector() const; - - // Callback to notify upon changes. - FilePathWatcher::Callback callback_; - - // 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|. - WatchVector watches_; - - std::unordered_map recursive_paths_by_watch_; - std::map recursive_watches_by_path_; - - // Read only while INotifyReader::lock_ is held, and used to post asynchronous - // notifications to the Watcher on its home task_runner(). Ideally this should - // be const, but since it is initialized from |weak_factory_|, which must - // appear after it, that is not possible. - WeakPtr weak_ptr_; - - WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); -}; - -LazyInstance::Leaky g_inotify_reader = LAZY_INSTANCE_INITIALIZER; - -void InotifyReaderThreadDelegate::ThreadMain() { - PlatformThread::SetName("inotify_reader"); - - // Make sure the file descriptors are good for use with select(). - CHECK_LE(0, inotify_fd_); - CHECK_GT(FD_SETSIZE, inotify_fd_); - - while (true) { - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(inotify_fd_, &rfds); - - // Wait until some inotify events are available. - int select_result = - HANDLE_EINTR(select(inotify_fd_ + 1, &rfds, nullptr, nullptr, nullptr)); - if (select_result < 0) { - DPLOG(WARNING) << "select failed"; - return; - } - - // Adjust buffer size to current event queue size. - int buffer_size; - int ioctl_result = HANDLE_EINTR(ioctl(inotify_fd_, FIONREAD, &buffer_size)); - - if (ioctl_result != 0) { - DPLOG(WARNING) << "ioctl failed"; - return; - } - - std::vector buffer(buffer_size); - - ssize_t bytes_read = - HANDLE_EINTR(read(inotify_fd_, &buffer[0], buffer_size)); - - if (bytes_read < 0) { - DPLOG(WARNING) << "read from inotify fd failed"; - return; - } - - ssize_t i = 0; - while (i < bytes_read) { - inotify_event* event = reinterpret_cast(&buffer[i]); - size_t event_size = sizeof(inotify_event) + event->len; - DCHECK(i + event_size <= static_cast(bytes_read)); - g_inotify_reader.Get().OnInotifyEvent(event); - i += event_size; - } - } -} - -InotifyReader::InotifyReader() - : inotify_fd_(inotify_init()), - thread_delegate_(inotify_fd_), - valid_(false) { - if (inotify_fd_ < 0) { - PLOG(ERROR) << "inotify_init() failed"; - return; - } - - if (!StartThread()) - return; - - valid_ = true; -} - -bool InotifyReader::StartThread() { - // This object is LazyInstance::Leaky, so thread_delegate_ will outlive the - // thread. - return PlatformThread::CreateNonJoinable(0, &thread_delegate_); -} - -InotifyReader::Watch InotifyReader::AddWatch( - const FilePath& path, FilePathWatcherImpl* watcher) { - if (!valid_) - return kInvalidWatch; - - AutoLock auto_lock(lock_); - - Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(), - IN_ATTRIB | IN_CREATE | IN_DELETE | - IN_CLOSE_WRITE | IN_MOVE | - IN_ONLYDIR); - - if (watch == kInvalidWatch) - return kInvalidWatch; - - watchers_[watch].insert(watcher); - - return watch; -} - -void InotifyReader::RemoveWatch(Watch watch, FilePathWatcherImpl* watcher) { - if (!valid_ || (watch == kInvalidWatch)) - return; - - AutoLock auto_lock(lock_); - - watchers_[watch].erase(watcher); - - if (watchers_[watch].empty()) { - watchers_.erase(watch); - inotify_rm_watch(inotify_fd_, watch); - } -} - -void InotifyReader::OnInotifyEvent(const inotify_event* event) { - if (event->mask & IN_IGNORED) - return; - - FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL("")); - AutoLock auto_lock(lock_); - - for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); - watcher != watchers_[event->wd].end(); - ++watcher) { - (*watcher)->OnFilePathChanged(event->wd, - child, - event->mask & (IN_CREATE | IN_MOVED_TO), - event->mask & (IN_DELETE | IN_MOVED_FROM), - event->mask & IN_ISDIR); - } -} - -FilePathWatcherImpl::FilePathWatcherImpl() - : recursive_(false), weak_factory_(this) { - weak_ptr_ = weak_factory_.GetWeakPtr(); -} - -FilePathWatcherImpl::~FilePathWatcherImpl() { - DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence()); -} - -void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, - const FilePath::StringType& child, - bool created, - bool deleted, - bool is_dir) { - DCHECK(!task_runner()->RunsTasksInCurrentSequence()); - - // This method is invoked on the Inotify thread. Switch to task_runner() to - // access |watches_| safely. Use a WeakPtr to prevent the callback from - // running after |this| is destroyed (i.e. after the watch is cancelled). - task_runner()->PostTask( - FROM_HERE, - BindOnce(&FilePathWatcherImpl::OnFilePathChangedOnOriginSequence, - weak_ptr_, fired_watch, child, created, deleted, is_dir)); -} - -void FilePathWatcherImpl::OnFilePathChangedOnOriginSequence( - InotifyReader::Watch fired_watch, - const FilePath::StringType& child, - bool created, - bool deleted, - bool is_dir) { - DCHECK(task_runner()->RunsTasksInCurrentSequence()); - DCHECK(!watches_.empty()); - DCHECK(HasValidWatchVector()); - - // Used below to avoid multiple recursive updates. - bool did_update = false; - - // Find the entry in |watches_| that corresponds to |fired_watch|. - 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 target_changed; - if (watch_entry.subdir.empty()) { - // The fired watch is for a WatchEntry without a subdir. Thus for a given - // |target_| = "/path/to/foo", this is for "foo". Here, check either: - // - the target has no symlink: it is the target and it changed. - // - the target has a symlink, and it matches |child|. - target_changed = (watch_entry.linkname.empty() || - child == watch_entry.linkname); - } else { - // The fired watch is for a WatchEntry with a subdir. Thus for a given - // |target_| = "/path/to/foo", this is for {"/", "/path", "/path/to"}. - // So we can safely access the next WatchEntry since we have not reached - // the end yet. Check |watch_entry| is for "/path/to", i.e. the next - // element is "foo". - bool next_watch_may_be_for_target = watches_[i + 1].subdir.empty(); - if (next_watch_may_be_for_target) { - // The current |watch_entry| is for "/path/to", so check if the |child| - // that changed is "foo". - target_changed = watch_entry.subdir == child; - } else { - // The current |watch_entry| is not for "/path/to", so the next entry - // cannot be "foo". Thus |target_| has not changed. - target_changed = false; - } - } - - // Update watches if a directory component of the |target_| path - // (dis)appears. Note that we don't add the additional restriction of - // 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 && 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, - bool recursive, - const FilePathWatcher::Callback& callback) { - DCHECK(target_.empty()); - - set_task_runner(SequencedTaskRunnerHandle::Get()); - callback_ = callback; - target_ = path; - recursive_ = recursive; - - std::vector comps; - target_.GetComponents(&comps); - DCHECK(!comps.empty()); - 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_) { - // Watch() was never called. - set_cancelled(); - return; - } - - DCHECK(task_runner()->RunsTasksInCurrentSequence()); - DCHECK(!is_cancelled()); - - set_cancelled(); - callback_.Reset(); - - for (size_t i = 0; i < watches_.size(); ++i) - g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this); - watches_.clear(); - target_.clear(); - RemoveRecursiveWatches(); -} - -void FilePathWatcherImpl::UpdateWatches() { - // Ensure this runs on the task_runner() exclusively in order to avoid - // concurrency issues. - DCHECK(task_runner()->RunsTasksInCurrentSequence()); - DCHECK(HasValidWatchVector()); - - // Walk the list of watches and update them as we go. - FilePath path(FILE_PATH_LITERAL("/")); - 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(); - watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this); - if (watch_entry.watch == InotifyReader::kInvalidWatch) { - // Ignore the error code (beyond symlink handling) to attempt to add - // watches on accessible children of unreadable directories. Note that - // this is a best-effort attempt; we may not catch events in this - // scenario. - if (IsLink(path)) - AddWatchForBrokenSymlink(path, &watch_entry); - } - 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) { - DCHECK(HasValidWatchVector()); - - 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) && - fired_watch != watches_.back().watch) { - UpdateRecursiveWatchesForPath(target_); - return; - } - - // Underneath |target_|, only directory changes trigger watch updates. - if (!is_dir) - return; - - const FilePath& changed_dir = - ContainsKey(recursive_paths_by_watch_, fired_watch) ? - recursive_paths_by_watch_[fired_watch] : - target_; - - std::map::iterator start_it = - recursive_watches_by_path_.lower_bound(changed_dir); - std::map::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); - } - } - } -} - -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 (const auto& it : recursive_paths_by_watch_) - g_inotify_reader.Get().RemoveWatch(it.first, this); - - recursive_paths_by_watch_.clear(); - recursive_watches_by_path_.clear(); -} - -void FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path, - WatchEntry* watch_entry) { - DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch); - FilePath link; - if (!ReadSymbolicLink(path, &link)) - return; - - 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; - } - watch_entry->watch = watch; - watch_entry->linkname = link.BaseName().value(); -} - -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_.back().subdir.empty(); -} - -} // namespace - -FilePathWatcher::FilePathWatcher() { - sequence_checker_.DetachFromSequence(); - impl_ = std::make_unique(); -} - -} // namespace base diff --git a/files/file_path_watcher_mac.cc b/files/file_path_watcher_mac.cc deleted file mode 100644 index 4dcf90b6f..000000000 --- a/files/file_path_watcher_mac.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "base/files/file_path_watcher.h" -#include "base/files/file_path_watcher_kqueue.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "build/build_config.h" - -#if !defined(OS_IOS) -#include "base/files/file_path_watcher_fsevents.h" -#endif - -namespace base { - -namespace { - -class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { - public: - FilePathWatcherImpl() = default; - ~FilePathWatcherImpl() override = default; - - 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_ = std::make_unique(); -#endif // OS_IOS - } else { - impl_ = std::make_unique(); - } - DCHECK(impl_.get()); - return impl_->Watch(path, recursive, callback); - } - - void Cancel() override { - if (impl_.get()) - impl_->Cancel(); - set_cancelled(); - } - - private: - std::unique_ptr impl_; - - DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); -}; - -} // namespace - -FilePathWatcher::FilePathWatcher() { - sequence_checker_.DetachFromSequence(); - impl_ = std::make_unique(); -} - -} // namespace base diff --git a/files/file_path_watcher_stub.cc b/files/file_path_watcher_stub.cc deleted file mode 100644 index 93a5babe9..000000000 --- a/files/file_path_watcher_stub.cc +++ /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. - -// This file exists for Unix systems which don't have the inotify headers, and -// thus cannot build file_watcher_inotify.cc - -#include "base/files/file_path_watcher.h" - -#include "base/macros.h" -#include "base/memory/ptr_util.h" - -namespace base { - -namespace { - -class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { - public: - FilePathWatcherImpl() = default; - ~FilePathWatcherImpl() override = default; - - bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override { - return false; - } - - void Cancel() override {} - - private: - DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); -}; - -} // namespace - -FilePathWatcher::FilePathWatcher() { - sequence_checker_.DetachFromSequence(); - impl_ = std::make_unique(); -} - -} // namespace base diff --git a/files/file_path_watcher_unittest.cc b/files/file_path_watcher_unittest.cc deleted file mode 100644 index bc5d90253..000000000 --- a/files/file_path_watcher_unittest.cc +++ /dev/null @@ -1,873 +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/files/file_path_watcher.h" - -#if defined(OS_WIN) -#include -#include -#elif defined(OS_POSIX) -#include -#endif - -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/stl_util.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/waitable_event.h" -#include "base/test/test_file_util.h" -#include "base/test/test_timeouts.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_ANDROID) -#include "base/android/path_utils.h" -#endif // defined(OS_ANDROID) - -#if defined(OS_POSIX) -#include "base/files/file_descriptor_watcher_posix.h" -#endif // defined(OS_POSIX) - -namespace base { - -namespace { - -class TestDelegate; - -// Aggregates notifications from the test delegates and breaks the message loop -// the test thread is waiting on once they all came in. -class NotificationCollector - : public base::RefCountedThreadSafe { - public: - NotificationCollector() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {} - - // Called from the file thread by the delegates. - void OnChange(TestDelegate* delegate) { - task_runner_->PostTask( - FROM_HERE, base::BindOnce(&NotificationCollector::RecordChange, this, - base::Unretained(delegate))); - } - - void Register(TestDelegate* delegate) { - delegates_.insert(delegate); - } - - void Reset(base::OnceClosure signal_closure) { - signal_closure_ = std::move(signal_closure); - signaled_.clear(); - } - - bool Success() { - return signaled_ == delegates_; - } - - private: - friend class base::RefCountedThreadSafe; - ~NotificationCollector() = default; - - void RecordChange(TestDelegate* delegate) { - // Warning: |delegate| is Unretained. Do not dereference. - ASSERT_TRUE(task_runner_->BelongsToCurrentThread()); - ASSERT_TRUE(delegates_.count(delegate)); - signaled_.insert(delegate); - - // Check whether all delegates have been signaled. - if (signal_closure_ && signaled_ == delegates_) - std::move(signal_closure_).Run(); - } - - // Set of registered delegates. - std::set delegates_; - - // Set of signaled delegates. - std::set signaled_; - - // The loop we should break after all delegates signaled. - scoped_refptr task_runner_; - - // Closure to run when all delegates have signaled. - base::OnceClosure signal_closure_; -}; - -class TestDelegateBase : public SupportsWeakPtr { - public: - TestDelegateBase() = default; - virtual ~TestDelegateBase() = default; - - virtual void OnFileChanged(const FilePath& path, bool error) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(TestDelegateBase); -}; - -// A mock class for testing. Gmock is not appropriate because it is not -// thread-safe for setting expectations. Thus the test code cannot safely -// reset expectations while the file watcher is running. -// Instead, TestDelegate gets the notifications from FilePathWatcher and uses -// NotificationCollector to aggregate the results. -class TestDelegate : public TestDelegateBase { - public: - explicit TestDelegate(NotificationCollector* collector) - : collector_(collector) { - collector_->Register(this); - } - ~TestDelegate() override = default; - - void OnFileChanged(const FilePath& path, bool error) override { - if (error) - ADD_FAILURE() << "Error " << path.value(); - else - collector_->OnChange(this); - } - - private: - scoped_refptr collector_; - - DISALLOW_COPY_AND_ASSIGN(TestDelegate); -}; - -class FilePathWatcherTest : public testing::Test { - public: - FilePathWatcherTest() -#if defined(OS_POSIX) - : file_descriptor_watcher_(&loop_) -#endif - { - } - - ~FilePathWatcherTest() override = default; - - protected: - void SetUp() override { -#if defined(OS_ANDROID) - // Watching files is only permitted when all parent directories are - // accessible, which is not the case for the default temp directory - // on Android which is under /data/data. Use /sdcard instead. - // TODO(pauljensen): Remove this when crbug.com/475568 is fixed. - FilePath parent_dir; - ASSERT_TRUE(android::GetExternalStorageDirectory(&parent_dir)); - ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(parent_dir)); -#else // defined(OS_ANDROID) - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); -#endif // defined(OS_ANDROID) - collector_ = new NotificationCollector(); - } - - void TearDown() override { RunLoop().RunUntilIdle(); } - - FilePath test_file() { - return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest"); - } - - FilePath test_link() { - return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest.lnk"); - } - - // Write |content| to |file|. Returns true on success. - bool WriteFile(const FilePath& file, const std::string& content) { - int write_size = ::base::WriteFile(file, content.c_str(), content.length()); - return write_size == static_cast(content.length()); - } - - bool SetupWatch(const FilePath& target, - FilePathWatcher* watcher, - TestDelegateBase* delegate, - bool recursive_watch) WARN_UNUSED_RESULT; - - bool WaitForEvents() WARN_UNUSED_RESULT { - return WaitForEventsWithTimeout(TestTimeouts::action_timeout()); - } - - bool WaitForEventsWithTimeout(TimeDelta timeout) WARN_UNUSED_RESULT { - RunLoop run_loop; - collector_->Reset(run_loop.QuitClosure()); - - // Make sure we timeout if we don't get notified. - ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, run_loop.QuitClosure(), timeout); - run_loop.Run(); - return collector_->Success(); - } - - NotificationCollector* collector() { return collector_.get(); } - - MessageLoopForIO loop_; -#if defined(OS_POSIX) - FileDescriptorWatcher file_descriptor_watcher_; -#endif - - ScopedTempDir temp_dir_; - scoped_refptr collector_; - - private: - DISALLOW_COPY_AND_ASSIGN(FilePathWatcherTest); -}; - -bool FilePathWatcherTest::SetupWatch(const FilePath& target, - FilePathWatcher* watcher, - TestDelegateBase* delegate, - bool recursive_watch) { - return watcher->Watch( - target, recursive_watch, - base::Bind(&TestDelegateBase::OnFileChanged, delegate->AsWeakPtr())); -} - -// Basic test: Create the file and verify that we notice. -TEST_F(FilePathWatcherTest, NewFile) { - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false)); - - ASSERT_TRUE(WriteFile(test_file(), "content")); - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that modifying the file is caught. -TEST_F(FilePathWatcherTest, ModifiedFile) { - ASSERT_TRUE(WriteFile(test_file(), "content")); - - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false)); - - // Now make sure we get notified if the file is modified. - ASSERT_TRUE(WriteFile(test_file(), "new content")); - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that moving the file into place is caught. -TEST_F(FilePathWatcherTest, MovedFile) { - FilePath source_file(temp_dir_.GetPath().AppendASCII("source")); - ASSERT_TRUE(WriteFile(source_file, "content")); - - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false)); - - // Now make sure we get notified if the file is modified. - ASSERT_TRUE(base::Move(source_file, test_file())); - ASSERT_TRUE(WaitForEvents()); -} - -TEST_F(FilePathWatcherTest, DeletedFile) { - ASSERT_TRUE(WriteFile(test_file(), "content")); - - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false)); - - // Now make sure we get notified if the file is deleted. - base::DeleteFile(test_file(), false); - ASSERT_TRUE(WaitForEvents()); -} - -// Used by the DeleteDuringNotify test below. -// Deletes the FilePathWatcher when it's notified. -class Deleter : public TestDelegateBase { - public: - explicit Deleter(base::OnceClosure done_closure) - : watcher_(std::make_unique()), - done_closure_(std::move(done_closure)) {} - ~Deleter() override = default; - - void OnFileChanged(const FilePath&, bool) override { - watcher_.reset(); - std::move(done_closure_).Run(); - } - - FilePathWatcher* watcher() const { return watcher_.get(); } - - private: - std::unique_ptr watcher_; - base::OnceClosure done_closure_; - - DISALLOW_COPY_AND_ASSIGN(Deleter); -}; - -// Verify that deleting a watcher during the callback doesn't crash. -TEST_F(FilePathWatcherTest, DeleteDuringNotify) { - base::RunLoop run_loop; - Deleter deleter(run_loop.QuitClosure()); - ASSERT_TRUE(SetupWatch(test_file(), deleter.watcher(), &deleter, false)); - - ASSERT_TRUE(WriteFile(test_file(), "content")); - run_loop.Run(); - - // We win if we haven't crashed yet. - // Might as well double-check it got deleted, too. - ASSERT_TRUE(deleter.watcher() == nullptr); -} - -// Verify that deleting the watcher works even if there is a pending -// notification. -// Flaky on MacOS (and ARM linux): http://crbug.com/85930 -TEST_F(FilePathWatcherTest, DISABLED_DestroyWithPendingNotification) { - std::unique_ptr delegate(new TestDelegate(collector())); - FilePathWatcher watcher; - ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false)); - ASSERT_TRUE(WriteFile(test_file(), "content")); -} - -TEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) { - FilePathWatcher watcher1, watcher2; - std::unique_ptr delegate1(new TestDelegate(collector())); - std::unique_ptr delegate2(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(test_file(), &watcher1, delegate1.get(), false)); - ASSERT_TRUE(SetupWatch(test_file(), &watcher2, delegate2.get(), false)); - - ASSERT_TRUE(WriteFile(test_file(), "content")); - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that watching a file whose parent directory doesn't exist yet works if -// the directory and file are created eventually. -TEST_F(FilePathWatcherTest, NonExistentDirectory) { - FilePathWatcher watcher; - FilePath dir(temp_dir_.GetPath().AppendASCII("dir")); - FilePath file(dir.AppendASCII("file")); - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false)); - - ASSERT_TRUE(base::CreateDirectory(dir)); - - ASSERT_TRUE(WriteFile(file, "content")); - - VLOG(1) << "Waiting for file creation"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(WriteFile(file, "content v2")); - VLOG(1) << "Waiting for file change"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(base::DeleteFile(file, false)); - VLOG(1) << "Waiting for file deletion"; - ASSERT_TRUE(WaitForEvents()); -} - -// Exercises watch reconfiguration for the case that directories on the path -// are rapidly created. -TEST_F(FilePathWatcherTest, DirectoryChain) { - FilePath path(temp_dir_.GetPath()); - std::vector dir_names; - for (int i = 0; i < 20; i++) { - std::string dir(base::StringPrintf("d%d", i)); - dir_names.push_back(dir); - path = path.AppendASCII(dir); - } - - FilePathWatcher watcher; - FilePath file(path.AppendASCII("file")); - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false)); - - FilePath sub_path(temp_dir_.GetPath()); - for (std::vector::const_iterator d(dir_names.begin()); - d != dir_names.end(); ++d) { - sub_path = sub_path.AppendASCII(*d); - ASSERT_TRUE(base::CreateDirectory(sub_path)); - } - VLOG(1) << "Create File"; - ASSERT_TRUE(WriteFile(file, "content")); - VLOG(1) << "Waiting for file creation"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(WriteFile(file, "content v2")); - VLOG(1) << "Waiting for file modification"; - ASSERT_TRUE(WaitForEvents()); -} - -#if defined(OS_MACOSX) -// http://crbug.com/85930 -#define DisappearingDirectory DISABLED_DisappearingDirectory -#endif -TEST_F(FilePathWatcherTest, DisappearingDirectory) { - FilePathWatcher watcher; - FilePath dir(temp_dir_.GetPath().AppendASCII("dir")); - FilePath file(dir.AppendASCII("file")); - ASSERT_TRUE(base::CreateDirectory(dir)); - ASSERT_TRUE(WriteFile(file, "content")); - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false)); - - ASSERT_TRUE(base::DeleteFile(dir, true)); - ASSERT_TRUE(WaitForEvents()); -} - -// Tests that a file that is deleted and reappears is tracked correctly. -TEST_F(FilePathWatcherTest, DeleteAndRecreate) { - ASSERT_TRUE(WriteFile(test_file(), "content")); - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false)); - - ASSERT_TRUE(base::DeleteFile(test_file(), false)); - VLOG(1) << "Waiting for file deletion"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(WriteFile(test_file(), "content")); - VLOG(1) << "Waiting for file creation"; - ASSERT_TRUE(WaitForEvents()); -} - -TEST_F(FilePathWatcherTest, WatchDirectory) { - FilePathWatcher watcher; - FilePath dir(temp_dir_.GetPath().AppendASCII("dir")); - FilePath file1(dir.AppendASCII("file1")); - FilePath file2(dir.AppendASCII("file2")); - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(), false)); - - ASSERT_TRUE(base::CreateDirectory(dir)); - VLOG(1) << "Waiting for directory creation"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(WriteFile(file1, "content")); - VLOG(1) << "Waiting for file1 creation"; - ASSERT_TRUE(WaitForEvents()); - -#if !defined(OS_MACOSX) - // Mac implementation does not detect files modified in a directory. - ASSERT_TRUE(WriteFile(file1, "content v2")); - VLOG(1) << "Waiting for file1 modification"; - ASSERT_TRUE(WaitForEvents()); -#endif // !OS_MACOSX - - ASSERT_TRUE(base::DeleteFile(file1, false)); - VLOG(1) << "Waiting for file1 deletion"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(WriteFile(file2, "content")); - VLOG(1) << "Waiting for file2 creation"; - ASSERT_TRUE(WaitForEvents()); -} - -TEST_F(FilePathWatcherTest, MoveParent) { - FilePathWatcher file_watcher; - FilePathWatcher subdir_watcher; - FilePath dir(temp_dir_.GetPath().AppendASCII("dir")); - FilePath dest(temp_dir_.GetPath().AppendASCII("dest")); - FilePath subdir(dir.AppendASCII("subdir")); - FilePath file(subdir.AppendASCII("file")); - std::unique_ptr file_delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(file, &file_watcher, file_delegate.get(), false)); - std::unique_ptr subdir_delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(subdir, &subdir_watcher, subdir_delegate.get(), - false)); - - // Setup a directory hierarchy. - ASSERT_TRUE(base::CreateDirectory(subdir)); - ASSERT_TRUE(WriteFile(file, "content")); - VLOG(1) << "Waiting for file creation"; - ASSERT_TRUE(WaitForEvents()); - - // Move the parent directory. - base::Move(dir, dest); - VLOG(1) << "Waiting for directory move"; - ASSERT_TRUE(WaitForEvents()); -} - -TEST_F(FilePathWatcherTest, RecursiveWatch) { - FilePathWatcher watcher; - FilePath dir(temp_dir_.GetPath().AppendASCII("dir")); - std::unique_ptr delegate(new TestDelegate(collector())); - bool setup_result = SetupWatch(dir, &watcher, delegate.get(), true); - if (!FilePathWatcher::RecursiveWatchAvailable()) { - ASSERT_FALSE(setup_result); - return; - } - ASSERT_TRUE(setup_result); - - // Main directory("dir") creation. - ASSERT_TRUE(base::CreateDirectory(dir)); - ASSERT_TRUE(WaitForEvents()); - - // Create "$dir/file1". - FilePath file1(dir.AppendASCII("file1")); - ASSERT_TRUE(WriteFile(file1, "content")); - ASSERT_TRUE(WaitForEvents()); - - // Create "$dir/subdir". - FilePath subdir(dir.AppendASCII("subdir")); - ASSERT_TRUE(base::CreateDirectory(subdir)); - ASSERT_TRUE(WaitForEvents()); - - // Create "$dir/subdir/subdir_file1". - FilePath subdir_file1(subdir.AppendASCII("subdir_file1")); - ASSERT_TRUE(WriteFile(subdir_file1, "content")); - ASSERT_TRUE(WaitForEvents()); - - // Create "$dir/subdir/subdir_child_dir". - FilePath subdir_child_dir(subdir.AppendASCII("subdir_child_dir")); - ASSERT_TRUE(base::CreateDirectory(subdir_child_dir)); - ASSERT_TRUE(WaitForEvents()); - - // Create "$dir/subdir/subdir_child_dir/child_dir_file1". - FilePath child_dir_file1(subdir_child_dir.AppendASCII("child_dir_file1")); - ASSERT_TRUE(WriteFile(child_dir_file1, "content v2")); - ASSERT_TRUE(WaitForEvents()); - - // Write into "$dir/subdir/subdir_child_dir/child_dir_file1". - ASSERT_TRUE(WriteFile(child_dir_file1, "content")); - ASSERT_TRUE(WaitForEvents()); - -// Apps cannot change file attributes on Android in /sdcard as /sdcard uses the -// "fuse" file system, while /data uses "ext4". Running these tests in /data -// would be preferable and allow testing file attributes and symlinks. -// TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places -// the |temp_dir_| in /data. -#if !defined(OS_ANDROID) - // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes. - ASSERT_TRUE(base::MakeFileUnreadable(child_dir_file1)); - ASSERT_TRUE(WaitForEvents()); -#endif - - // Delete "$dir/subdir/subdir_file1". - ASSERT_TRUE(base::DeleteFile(subdir_file1, false)); - ASSERT_TRUE(WaitForEvents()); - - // Delete "$dir/subdir/subdir_child_dir/child_dir_file1". - ASSERT_TRUE(base::DeleteFile(child_dir_file1, false)); - ASSERT_TRUE(WaitForEvents()); -} - -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) -// Apps cannot create symlinks on Android in /sdcard as /sdcard uses the -// "fuse" file system, while /data uses "ext4". Running these tests in /data -// would be preferable and allow testing file attributes and symlinks. -// TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places -// the |temp_dir_| in /data. -// -// This test is disabled on Fuchsia since it doesn't support symlinking. -TEST_F(FilePathWatcherTest, RecursiveWithSymLink) { - if (!FilePathWatcher::RecursiveWatchAvailable()) - return; - - FilePathWatcher watcher; - FilePath test_dir(temp_dir_.GetPath().AppendASCII("test_dir")); - ASSERT_TRUE(base::CreateDirectory(test_dir)); - FilePath symlink(test_dir.AppendASCII("symlink")); - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(symlink, &watcher, delegate.get(), true)); - - // Link creation. - FilePath target1(temp_dir_.GetPath().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_.GetPath().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()); -} -#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) - -TEST_F(FilePathWatcherTest, MoveChild) { - FilePathWatcher file_watcher; - FilePathWatcher subdir_watcher; - FilePath source_dir(temp_dir_.GetPath().AppendASCII("source")); - FilePath source_subdir(source_dir.AppendASCII("subdir")); - FilePath source_file(source_subdir.AppendASCII("file")); - FilePath dest_dir(temp_dir_.GetPath().AppendASCII("dest")); - FilePath dest_subdir(dest_dir.AppendASCII("subdir")); - FilePath dest_file(dest_subdir.AppendASCII("file")); - - // Setup a directory hierarchy. - ASSERT_TRUE(base::CreateDirectory(source_subdir)); - ASSERT_TRUE(WriteFile(source_file, "content")); - - std::unique_ptr file_delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(dest_file, &file_watcher, file_delegate.get(), false)); - std::unique_ptr subdir_delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(dest_subdir, &subdir_watcher, subdir_delegate.get(), - false)); - - // Move the directory into place, s.t. the watched file appears. - ASSERT_TRUE(base::Move(source_dir, dest_dir)); - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that changing attributes on a file is caught -#if defined(OS_ANDROID) -// Apps cannot change file attributes on Android in /sdcard as /sdcard uses the -// "fuse" file system, while /data uses "ext4". Running these tests in /data -// would be preferable and allow testing file attributes and symlinks. -// TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places -// the |temp_dir_| in /data. -#define FileAttributesChanged DISABLED_FileAttributesChanged -#endif // defined(OS_ANDROID -TEST_F(FilePathWatcherTest, FileAttributesChanged) { - ASSERT_TRUE(WriteFile(test_file(), "content")); - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(), false)); - - // Now make sure we get notified if the file is modified. - ASSERT_TRUE(base::MakeFileUnreadable(test_file())); - ASSERT_TRUE(WaitForEvents()); -} - -#if defined(OS_LINUX) - -// Verify that creating a symlink is caught. -TEST_F(FilePathWatcherTest, CreateLink) { - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - // Note that we are watching the symlink - ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false)); - - // Now make sure we get notified if the link is created. - // Note that test_file() doesn't have to exist. - ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link())); - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that deleting a symlink is caught. -TEST_F(FilePathWatcherTest, DeleteLink) { - // Unfortunately this test case only works if the link target exists. - // TODO(craig) fix this as part of crbug.com/91561. - ASSERT_TRUE(WriteFile(test_file(), "content")); - ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link())); - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false)); - - // Now make sure we get notified if the link is deleted. - ASSERT_TRUE(base::DeleteFile(test_link(), false)); - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that modifying a target file that a link is pointing to -// when we are watching the link is caught. -TEST_F(FilePathWatcherTest, ModifiedLinkedFile) { - ASSERT_TRUE(WriteFile(test_file(), "content")); - ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link())); - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - // Note that we are watching the symlink. - ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false)); - - // Now make sure we get notified if the file is modified. - ASSERT_TRUE(WriteFile(test_file(), "new content")); - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that creating a target file that a link is pointing to -// when we are watching the link is caught. -TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) { - ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link())); - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - // Note that we are watching the symlink. - ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false)); - - // Now make sure we get notified if the target file is created. - ASSERT_TRUE(WriteFile(test_file(), "content")); - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that deleting a target file that a link is pointing to -// when we are watching the link is caught. -TEST_F(FilePathWatcherTest, DeleteTargetLinkedFile) { - ASSERT_TRUE(WriteFile(test_file(), "content")); - ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link())); - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - // Note that we are watching the symlink. - ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false)); - - // Now make sure we get notified if the target file is deleted. - ASSERT_TRUE(base::DeleteFile(test_file(), false)); - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that watching a file whose parent directory is a link that -// doesn't exist yet works if the symlink is created eventually. -TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) { - FilePathWatcher watcher; - FilePath dir(temp_dir_.GetPath().AppendASCII("dir")); - FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk")); - FilePath file(dir.AppendASCII("file")); - FilePath linkfile(link_dir.AppendASCII("file")); - std::unique_ptr delegate(new TestDelegate(collector())); - // dir/file should exist. - ASSERT_TRUE(base::CreateDirectory(dir)); - ASSERT_TRUE(WriteFile(file, "content")); - // Note that we are watching dir.lnk/file which doesn't exist yet. - ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false)); - - ASSERT_TRUE(CreateSymbolicLink(dir, link_dir)); - VLOG(1) << "Waiting for link creation"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(WriteFile(file, "content v2")); - VLOG(1) << "Waiting for file change"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(base::DeleteFile(file, false)); - VLOG(1) << "Waiting for file deletion"; - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that watching a file whose parent directory is a -// dangling symlink works if the directory is created eventually. -TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) { - FilePathWatcher watcher; - FilePath dir(temp_dir_.GetPath().AppendASCII("dir")); - FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk")); - FilePath file(dir.AppendASCII("file")); - FilePath linkfile(link_dir.AppendASCII("file")); - std::unique_ptr delegate(new TestDelegate(collector())); - // Now create the link from dir.lnk pointing to dir but - // neither dir nor dir/file exist yet. - ASSERT_TRUE(CreateSymbolicLink(dir, link_dir)); - // Note that we are watching dir.lnk/file. - ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false)); - - ASSERT_TRUE(base::CreateDirectory(dir)); - ASSERT_TRUE(WriteFile(file, "content")); - VLOG(1) << "Waiting for dir/file creation"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(WriteFile(file, "content v2")); - VLOG(1) << "Waiting for file change"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(base::DeleteFile(file, false)); - VLOG(1) << "Waiting for file deletion"; - ASSERT_TRUE(WaitForEvents()); -} - -// Verify that watching a file with a symlink on the path -// to the file works. -TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) { - FilePathWatcher watcher; - FilePath dir(temp_dir_.GetPath().AppendASCII("dir")); - FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk")); - FilePath file(dir.AppendASCII("file")); - FilePath linkfile(link_dir.AppendASCII("file")); - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(base::CreateDirectory(dir)); - ASSERT_TRUE(CreateSymbolicLink(dir, link_dir)); - // Note that we are watching dir.lnk/file but the file doesn't exist yet. - ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false)); - - ASSERT_TRUE(WriteFile(file, "content")); - VLOG(1) << "Waiting for file creation"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(WriteFile(file, "content v2")); - VLOG(1) << "Waiting for file change"; - ASSERT_TRUE(WaitForEvents()); - - ASSERT_TRUE(base::DeleteFile(file, false)); - VLOG(1) << "Waiting for file deletion"; - ASSERT_TRUE(WaitForEvents()); -} - -#endif // OS_LINUX - -enum Permission { - Read, - Write, - Execute -}; - -#if defined(OS_MACOSX) -bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) { - struct stat stat_buf; - - if (stat(path.value().c_str(), &stat_buf) != 0) - return false; - - mode_t mode = 0; - switch (perm) { - case Read: - mode = S_IRUSR | S_IRGRP | S_IROTH; - break; - case Write: - mode = S_IWUSR | S_IWGRP | S_IWOTH; - break; - case Execute: - mode = S_IXUSR | S_IXGRP | S_IXOTH; - break; - default: - ADD_FAILURE() << "unknown perm " << perm; - return false; - } - if (allow) { - stat_buf.st_mode |= mode; - } else { - stat_buf.st_mode &= ~mode; - } - return chmod(path.value().c_str(), stat_buf.st_mode) == 0; -} -#endif // defined(OS_MACOSX) - -#if defined(OS_MACOSX) -// Linux implementation of FilePathWatcher doesn't catch attribute changes. -// http://crbug.com/78043 -// Windows implementation of FilePathWatcher catches attribute changes that -// don't affect the path being watched. -// http://crbug.com/78045 - -// Verify that changing attributes on a directory works. -TEST_F(FilePathWatcherTest, DirAttributesChanged) { - FilePath test_dir1( - temp_dir_.GetPath().AppendASCII("DirAttributesChangedDir1")); - FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2")); - FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile")); - // Setup a directory hierarchy. - ASSERT_TRUE(base::CreateDirectory(test_dir1)); - ASSERT_TRUE(base::CreateDirectory(test_dir2)); - ASSERT_TRUE(WriteFile(test_file, "content")); - - FilePathWatcher watcher; - std::unique_ptr delegate(new TestDelegate(collector())); - ASSERT_TRUE(SetupWatch(test_file, &watcher, delegate.get(), false)); - - // We should not get notified in this case as it hasn't affected our ability - // to access the file. - ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, false)); - ASSERT_FALSE(WaitForEventsWithTimeout(TestTimeouts::tiny_timeout())); - ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, true)); - - // We should get notified in this case because filepathwatcher can no - // longer access the file - ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false)); - ASSERT_TRUE(WaitForEvents()); - ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true)); -} - -#endif // OS_MACOSX -} // namespace - -} // namespace base diff --git a/files/file_path_watcher_win.cc b/files/file_path_watcher_win.cc deleted file mode 100644 index 46147509e..000000000 --- a/files/file_path_watcher_win.cc +++ /dev/null @@ -1,290 +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/files/file_path_watcher.h" - -#include "base/bind.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "base/time/time.h" -#include "base/win/object_watcher.h" - -#include - -namespace base { - -namespace { - -class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, - public base::win::ObjectWatcher::Delegate { - public: - FilePathWatcherImpl() - : handle_(INVALID_HANDLE_VALUE), - recursive_watch_(false) {} - ~FilePathWatcherImpl() override; - - // FilePathWatcher::PlatformDelegate: - bool Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) override; - void Cancel() override; - - // base::win::ObjectWatcher::Delegate: - void OnObjectSignaled(HANDLE object) override; - - private: - // Setup a watch handle for directory |dir|. Set |recursive| to true to watch - // the directory sub trees. Returns true if no fatal error occurs. |handle| - // will receive the handle value if |dir| is watchable, otherwise - // INVALID_HANDLE_VALUE. - static bool SetupWatchHandle(const FilePath& dir, - bool recursive, - HANDLE* handle) WARN_UNUSED_RESULT; - - // (Re-)Initialize the watch handle. - bool UpdateWatch() WARN_UNUSED_RESULT; - - // Destroy the watch handle. - void DestroyWatch(); - - // Callback to notify upon changes. - FilePathWatcher::Callback callback_; - - // Path we're supposed to watch (passed to callback). - FilePath target_; - - // Set to true in the destructor. - bool* was_deleted_ptr_ = nullptr; - - // Handle for FindFirstChangeNotification. - HANDLE handle_; - - // ObjectWatcher to watch handle_ for events. - base::win::ObjectWatcher watcher_; - - // Set to true to watch the sub trees of the specified directory file path. - bool recursive_watch_; - - // Keep track of the last modified time of the file. We use nulltime - // to represent the file not existing. - Time last_modified_; - - // The time at which we processed the first notification with the - // |last_modified_| time stamp. - Time first_notification_; - - DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); -}; - -FilePathWatcherImpl::~FilePathWatcherImpl() { - DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence()); - if (was_deleted_ptr_) - *was_deleted_ptr_ = true; -} - -bool FilePathWatcherImpl::Watch(const FilePath& path, - bool recursive, - const FilePathWatcher::Callback& callback) { - DCHECK(target_.value().empty()); // Can only watch one path. - - set_task_runner(SequencedTaskRunnerHandle::Get()); - callback_ = callback; - target_ = path; - recursive_watch_ = recursive; - - File::Info file_info; - if (GetFileInfo(target_, &file_info)) { - last_modified_ = file_info.last_modified; - first_notification_ = Time::Now(); - } - - if (!UpdateWatch()) - return false; - - watcher_.StartWatchingOnce(handle_, this); - - return true; -} - -void FilePathWatcherImpl::Cancel() { - if (callback_.is_null()) { - // Watch was never called, or the |task_runner_| has already quit. - set_cancelled(); - return; - } - - DCHECK(task_runner()->RunsTasksInCurrentSequence()); - set_cancelled(); - - if (handle_ != INVALID_HANDLE_VALUE) - DestroyWatch(); - - callback_.Reset(); -} - -void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) { - DCHECK(task_runner()->RunsTasksInCurrentSequence()); - DCHECK_EQ(object, handle_); - DCHECK(!was_deleted_ptr_); - - bool was_deleted = false; - was_deleted_ptr_ = &was_deleted; - - if (!UpdateWatch()) { - callback_.Run(target_, true /* error */); - return; - } - - // Check whether the event applies to |target_| and notify the callback. - File::Info file_info; - bool file_exists = GetFileInfo(target_, &file_info); - 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 && 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 - // file modification times against the wall clock is not reliable to find - // out whether the change is recent, since this code might just run too - // late. Moreover, there's no guarantee that file modification time and wall - // clock times come from the same source. - // - // Instead, the time at which the first notification carrying the current - // |last_notified_| time stamp is recorded. Later notifications that find - // the same file modification time only need to be forwarded until wall - // clock has advanced one second from the initial notification. After that - // interval, client code is guaranteed to having seen the current revision - // of the file. - if (Time::Now() - first_notification_ > TimeDelta::FromSeconds(1)) { - // Stop further notifications for this |last_modification_| time stamp. - first_notification_ = Time(); - } - callback_.Run(target_, false); - } else if (!file_exists && !last_modified_.is_null()) { - last_modified_ = Time(); - callback_.Run(target_, false); - } - - // The watch may have been cancelled by the callback. - if (!was_deleted) { - watcher_.StartWatchingOnce(handle_, this); - was_deleted_ptr_ = nullptr; - } -} - -// static -bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir, - bool recursive, - HANDLE* handle) { - *handle = FindFirstChangeNotification( - dir.value().c_str(), - recursive, - FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | - FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | - FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY); - if (*handle != INVALID_HANDLE_VALUE) { - // Make sure the handle we got points to an existing directory. It seems - // that windows sometimes hands out watches to directories that are - // about to go away, but doesn't sent notifications if that happens. - if (!DirectoryExists(dir)) { - FindCloseChangeNotification(*handle); - *handle = INVALID_HANDLE_VALUE; - } - return true; - } - - // If FindFirstChangeNotification failed because the target directory - // doesn't exist, access is denied (happens if the file is already gone but - // there are still handles open), or the target is not a directory, try the - // immediate parent directory instead. - DWORD error_code = GetLastError(); - if (error_code != ERROR_FILE_NOT_FOUND && - error_code != ERROR_PATH_NOT_FOUND && - error_code != ERROR_ACCESS_DENIED && - error_code != ERROR_SHARING_VIOLATION && - error_code != ERROR_DIRECTORY) { - DPLOG(ERROR) << "FindFirstChangeNotification failed for " - << dir.value(); - return false; - } - - return true; -} - -bool FilePathWatcherImpl::UpdateWatch() { - if (handle_ != INVALID_HANDLE_VALUE) - DestroyWatch(); - - // 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. - std::vector child_dirs; - FilePath watched_path(target_); - while (true) { - if (!SetupWatchHandle(watched_path, recursive_watch_, &handle_)) - return false; - - // Break if a valid handle is returned. Try the parent directory otherwise. - if (handle_ != INVALID_HANDLE_VALUE) - break; - - // Abort if we hit the root directory. - child_dirs.push_back(watched_path.BaseName()); - FilePath parent(watched_path.DirName()); - if (parent == watched_path) { - DLOG(ERROR) << "Reached the root directory"; - return false; - } - watched_path = parent; - } - - // At this point, handle_ is valid. However, the bottom-up search that the - // above code performs races against directory creation. So try to walk back - // down and see whether any children appeared in the mean time. - while (!child_dirs.empty()) { - watched_path = watched_path.Append(child_dirs.back()); - child_dirs.pop_back(); - HANDLE temp_handle = INVALID_HANDLE_VALUE; - if (!SetupWatchHandle(watched_path, recursive_watch_, &temp_handle)) - return false; - if (temp_handle == INVALID_HANDLE_VALUE) - break; - FindCloseChangeNotification(handle_); - handle_ = temp_handle; - } - - return true; -} - -void FilePathWatcherImpl::DestroyWatch() { - watcher_.StopWatching(); - FindCloseChangeNotification(handle_); - handle_ = INVALID_HANDLE_VALUE; -} - -} // namespace - -FilePathWatcher::FilePathWatcher() { - sequence_checker_.DetachFromSequence(); - impl_ = std::make_unique(); -} - -} // namespace base diff --git a/files/file_posix.cc b/files/file_posix.cc deleted file mode 100644 index fffec5e5c..000000000 --- a/files/file_posix.cc +++ /dev/null @@ -1,553 +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/files/file.h" - -#include -#include -#include -#include -#include - -#include "base/logging.h" -#include "base/metrics/histogram_functions.h" -#include "base/posix/eintr_wrapper.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_restrictions.h" -#include "build/build_config.h" - -#if defined(OS_ANDROID) -#include "base/os_compat_android.h" -#endif - -namespace base { - -// Make sure our Whence mappings match the system headers. -static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR && - File::FROM_END == SEEK_END, - "whence mapping must match the system headers"); - -namespace { - -#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \ - defined(OS_ANDROID) && __ANDROID_API__ < 21 -int CallFstat(int fd, stat_wrapper_t *sb) { - AssertBlockingAllowed(); - return fstat(fd, sb); -} -#else -int CallFstat(int fd, stat_wrapper_t *sb) { - AssertBlockingAllowed(); - return fstat64(fd, sb); -} -#endif - -// NaCl doesn't provide the following system calls, so either simulate them or -// wrap them in order to minimize the number of #ifdef's in this file. -#if !defined(OS_NACL) && !defined(OS_AIX) -bool IsOpenAppend(PlatformFile file) { - return (fcntl(file, F_GETFL) & O_APPEND) != 0; -} - -int CallFtruncate(PlatformFile file, int64_t length) { - return HANDLE_EINTR(ftruncate(file, length)); -} - -int CallFutimes(PlatformFile file, const struct timeval times[2]) { -#ifdef __USE_XOPEN2K8 - // futimens should be available, but futimes might not be - // http://pubs.opengroup.org/onlinepubs/9699919799/ - - timespec ts_times[2]; - ts_times[0].tv_sec = times[0].tv_sec; - ts_times[0].tv_nsec = times[0].tv_usec * 1000; - ts_times[1].tv_sec = times[1].tv_sec; - ts_times[1].tv_nsec = times[1].tv_usec * 1000; - - return futimens(file, ts_times); -#else - return futimes(file, times); -#endif -} - -#if !defined(OS_FUCHSIA) -File::Error CallFcntlFlock(PlatformFile file, bool do_lock) { - struct flock lock; - lock.l_type = do_lock ? F_WRLCK : F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; // Lock entire file. - if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1) - return File::GetLastFileError(); - return File::FILE_OK; -} -#endif - -#else // defined(OS_NACL) && !defined(OS_AIX) - -bool IsOpenAppend(PlatformFile file) { - // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX - // standard and always appends if the file is opened with O_APPEND, just - // return false here. - return false; -} - -int CallFtruncate(PlatformFile file, int64_t length) { - NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate. - return 0; -} - -int CallFutimes(PlatformFile file, const struct timeval times[2]) { - NOTIMPLEMENTED(); // NaCl doesn't implement futimes. - return 0; -} - -File::Error CallFcntlFlock(PlatformFile file, bool do_lock) { - NOTIMPLEMENTED(); // NaCl doesn't implement flock struct. - return File::FILE_ERROR_INVALID_OPERATION; -} -#endif // defined(OS_NACL) - -} // 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_t last_modified_nsec = stat_info.st_mtim.tv_nsec; - time_t last_accessed_sec = stat_info.st_atim.tv_sec; - int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec; - time_t creation_time_sec = stat_info.st_ctim.tv_sec; - int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec; -#elif defined(OS_ANDROID) - time_t last_modified_sec = stat_info.st_mtime; - int64_t last_modified_nsec = stat_info.st_mtime_nsec; - time_t last_accessed_sec = stat_info.st_atime; - int64_t last_accessed_nsec = stat_info.st_atime_nsec; - time_t creation_time_sec = stat_info.st_ctime; - int64_t 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_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec; - time_t last_accessed_sec = stat_info.st_atimespec.tv_sec; - int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec; - time_t creation_time_sec = stat_info.st_ctimespec.tv_sec; - int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec; -#else - time_t last_modified_sec = stat_info.st_mtime; - int64_t last_modified_nsec = 0; - time_t last_accessed_sec = stat_info.st_atime; - int64_t last_accessed_nsec = 0; - time_t creation_time_sec = stat_info.st_ctime; - int64_t 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); -} - -bool File::IsValid() const { - return file_.is_valid(); -} - -PlatformFile File::GetPlatformFile() const { - return file_.get(); -} - -PlatformFile File::TakePlatformFile() { - return file_.release(); -} - -void File::Close() { - if (!IsValid()) - return; - - SCOPED_FILE_TRACE("Close"); - AssertBlockingAllowed(); - file_.reset(); -} - -int64_t File::Seek(Whence whence, int64_t offset) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - - SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset); - -#if defined(OS_ANDROID) - static_assert(sizeof(int64_t) == sizeof(off64_t), "off64_t must be 64 bits"); - return lseek64(file_.get(), static_cast(offset), - static_cast(whence)); -#else - static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits"); - return lseek(file_.get(), static_cast(offset), - static_cast(whence)); -#endif -} - -int File::Read(int64_t offset, char* data, int size) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - if (size < 0) - return -1; - - SCOPED_FILE_TRACE_WITH_SIZE("Read", size); - - int bytes_read = 0; - int rv; - do { - rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read, - size - bytes_read, offset + bytes_read)); - if (rv <= 0) - break; - - bytes_read += rv; - } while (bytes_read < size); - - return bytes_read ? bytes_read : rv; -} - -int File::ReadAtCurrentPos(char* data, int size) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - if (size < 0) - return -1; - - SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size); - - int bytes_read = 0; - int rv; - do { - rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read)); - if (rv <= 0) - break; - - bytes_read += rv; - } while (bytes_read < size); - - return bytes_read ? bytes_read : rv; -} - -int File::ReadNoBestEffort(int64_t offset, char* data, int size) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - SCOPED_FILE_TRACE_WITH_SIZE("ReadNoBestEffort", size); - return HANDLE_EINTR(pread(file_.get(), data, size, offset)); -} - -int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - if (size < 0) - return -1; - - SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPosNoBestEffort", size); - return HANDLE_EINTR(read(file_.get(), data, size)); -} - -int File::Write(int64_t offset, const char* data, int size) { - AssertBlockingAllowed(); - - if (IsOpenAppend(file_.get())) - return WriteAtCurrentPos(data, size); - - DCHECK(IsValid()); - if (size < 0) - return -1; - - SCOPED_FILE_TRACE_WITH_SIZE("Write", size); - - int bytes_written = 0; - int rv; - do { -#if defined(OS_ANDROID) - // In case __USE_FILE_OFFSET64 is not used, we need to call pwrite64() - // instead of pwrite(). - static_assert(sizeof(int64_t) == sizeof(off64_t), - "off64_t must be 64 bits"); - rv = HANDLE_EINTR(pwrite64(file_.get(), data + bytes_written, - size - bytes_written, offset + bytes_written)); -#else - rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written, - size - bytes_written, offset + bytes_written)); -#endif - if (rv <= 0) - break; - - bytes_written += rv; - } while (bytes_written < size); - - return bytes_written ? bytes_written : rv; -} - -int File::WriteAtCurrentPos(const char* data, int size) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - if (size < 0) - return -1; - - SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size); - - int bytes_written = 0; - int rv; - do { - rv = HANDLE_EINTR(write(file_.get(), data + bytes_written, - size - bytes_written)); - if (rv <= 0) - break; - - bytes_written += rv; - } while (bytes_written < size); - - return bytes_written ? bytes_written : rv; -} - -int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - if (size < 0) - return -1; - - SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size); - return HANDLE_EINTR(write(file_.get(), data, size)); -} - -int64_t File::GetLength() { - DCHECK(IsValid()); - - SCOPED_FILE_TRACE("GetLength"); - - stat_wrapper_t file_info; - if (CallFstat(file_.get(), &file_info)) - return -1; - - return file_info.st_size; -} - -bool File::SetLength(int64_t length) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - - SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length); - return !CallFtruncate(file_.get(), length); -} - -bool File::SetTimes(Time last_access_time, Time last_modified_time) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - - SCOPED_FILE_TRACE("SetTimes"); - - timeval times[2]; - times[0] = last_access_time.ToTimeVal(); - times[1] = last_modified_time.ToTimeVal(); - - return !CallFutimes(file_.get(), times); -} - -bool File::GetInfo(Info* info) { - DCHECK(IsValid()); - - SCOPED_FILE_TRACE("GetInfo"); - - stat_wrapper_t file_info; - if (CallFstat(file_.get(), &file_info)) - return false; - - info->FromStat(file_info); - return true; -} - -#if !defined(OS_FUCHSIA) -File::Error File::Lock() { - SCOPED_FILE_TRACE("Lock"); - return CallFcntlFlock(file_.get(), true); -} - -File::Error File::Unlock() { - SCOPED_FILE_TRACE("Unlock"); - return CallFcntlFlock(file_.get(), false); -} -#endif - -File File::Duplicate() const { - if (!IsValid()) - return File(); - - SCOPED_FILE_TRACE("Duplicate"); - - PlatformFile other_fd = HANDLE_EINTR(dup(GetPlatformFile())); - if (other_fd == -1) - return File(File::GetLastFileError()); - - return File(other_fd, async()); -} - -// Static. -File::Error File::OSErrorToFileError(int saved_errno) { - switch (saved_errno) { - case EACCES: - case EISDIR: - case EROFS: - case EPERM: - return FILE_ERROR_ACCESS_DENIED; - case EBUSY: -#if !defined(OS_NACL) // ETXTBSY not defined by NaCl. - case ETXTBSY: -#endif - return FILE_ERROR_IN_USE; - case EEXIST: - return FILE_ERROR_EXISTS; - case EIO: - return FILE_ERROR_IO; - case ENOENT: - return FILE_ERROR_NOT_FOUND; - case ENFILE: // fallthrough - case EMFILE: - return FILE_ERROR_TOO_MANY_OPENED; - case ENOMEM: - return FILE_ERROR_NO_MEMORY; - case ENOSPC: - return FILE_ERROR_NO_SPACE; - case ENOTDIR: - return FILE_ERROR_NOT_A_DIRECTORY; - default: -#if !defined(OS_NACL) // NaCl build has no metrics code. - UmaHistogramSparse("PlatformFile.UnknownErrors.Posix", saved_errno); -#endif - // This function should only be called for errors. - DCHECK_NE(0, saved_errno); - return FILE_ERROR_FAILED; - } -} - -// NaCl doesn't implement system calls to open files directly. -#if !defined(OS_NACL) -// TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here? -void File::DoInitialize(const FilePath& path, uint32_t flags) { - AssertBlockingAllowed(); - DCHECK(!IsValid()); - - int open_flags = 0; - if (flags & FLAG_CREATE) - open_flags = O_CREAT | O_EXCL; - - created_ = false; - - if (flags & FLAG_CREATE_ALWAYS) { - DCHECK(!open_flags); - DCHECK(flags & FLAG_WRITE); - open_flags = O_CREAT | O_TRUNC; - } - - if (flags & FLAG_OPEN_TRUNCATED) { - DCHECK(!open_flags); - DCHECK(flags & FLAG_WRITE); - open_flags = O_TRUNC; - } - - if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) { - NOTREACHED(); - errno = EOPNOTSUPP; - error_details_ = FILE_ERROR_FAILED; - return; - } - - if (flags & FLAG_WRITE && flags & FLAG_READ) { - open_flags |= O_RDWR; - } else if (flags & FLAG_WRITE) { - open_flags |= O_WRONLY; - } else if (!(flags & FLAG_READ) && - !(flags & FLAG_WRITE_ATTRIBUTES) && - !(flags & FLAG_APPEND) && - !(flags & FLAG_OPEN_ALWAYS)) { - NOTREACHED(); - } - - if (flags & FLAG_TERMINAL_DEVICE) - open_flags |= O_NOCTTY | O_NDELAY; - - if (flags & FLAG_APPEND && flags & FLAG_READ) - open_flags |= O_APPEND | O_RDWR; - else if (flags & FLAG_APPEND) - open_flags |= O_APPEND | O_WRONLY; - - static_assert(O_RDONLY == 0, "O_RDONLY must equal zero"); - - int mode = S_IRUSR | S_IWUSR; -#if defined(OS_CHROMEOS) - mode |= S_IRGRP | S_IROTH; -#endif - - int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode)); - - if (flags & FLAG_OPEN_ALWAYS) { - if (descriptor < 0) { - open_flags |= O_CREAT; - if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE) - open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW - - descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode)); - if (descriptor >= 0) - created_ = true; - } - } - - if (descriptor < 0) { - error_details_ = File::GetLastFileError(); - return; - } - - if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) - created_ = true; - - if (flags & FLAG_DELETE_ON_CLOSE) - unlink(path.value().c_str()); - - async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); - error_details_ = FILE_OK; - file_.reset(descriptor); -} -#endif // !defined(OS_NACL) - -bool File::Flush() { - AssertBlockingAllowed(); - DCHECK(IsValid()); - SCOPED_FILE_TRACE("Flush"); - -#if defined(OS_NACL) - NOTIMPLEMENTED(); // NaCl doesn't implement fsync. - return true; -#elif defined(OS_LINUX) || defined(OS_ANDROID) - return !HANDLE_EINTR(fdatasync(file_.get())); -#else - return !HANDLE_EINTR(fsync(file_.get())); -#endif -} - -void File::SetPlatformFile(PlatformFile file) { - DCHECK(!file_.is_valid()); - file_.reset(file); -} - -// static -File::Error File::GetLastFileError() { - return base::File::OSErrorToFileError(errno); -} - -} // namespace base diff --git a/files/file_proxy.cc b/files/file_proxy.cc deleted file mode 100644 index f16e5940b..000000000 --- a/files/file_proxy.cc +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/file_proxy.h" - -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/location.h" -#include "base/macros.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_(std::move(file)), - error_(File::FILE_ERROR_FAILED), - task_runner_(proxy->task_runner()), - proxy_(AsWeakPtr(proxy)) { - } - - void PassFile() { - if (proxy_) - proxy_->SetFile(std::move(file_)); - else if (file_.IsValid()) - task_runner_->PostTask(FROM_HERE, - BindOnce(&FileDeleter, std::move(file_))); - } - - protected: - File file_; - File::Error error_; - - private: - scoped_refptr task_runner_; - WeakPtr proxy_; - DISALLOW_COPY_AND_ASSIGN(FileHelper); -}; - -namespace { - -class GenericFileHelper : public FileHelper { - public: - GenericFileHelper(FileProxy* proxy, File file) - : FileHelper(proxy, std::move(file)) { - } - - 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_t length) { - if (file_.SetLength(length)) - error_ = File::FILE_OK; - } - - void Flush() { - if (file_.Flush()) - error_ = File::FILE_OK; - } - - void Reply(FileProxy::StatusCallback callback) { - PassFile(); - if (!callback.is_null()) - std::move(callback).Run(error_); - } - - private: - DISALLOW_COPY_AND_ASSIGN(GenericFileHelper); -}; - -class CreateOrOpenHelper : public FileHelper { - public: - CreateOrOpenHelper(FileProxy* proxy, File file) - : FileHelper(proxy, std::move(file)) { - } - - 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(FileProxy::StatusCallback callback) { - DCHECK(!callback.is_null()); - PassFile(); - std::move(callback).Run(error_); - } - - private: - DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper); -}; - -class CreateTemporaryHelper : public FileHelper { - public: - CreateTemporaryHelper(FileProxy* proxy, File file) - : FileHelper(proxy, std::move(file)) { - } - - void RunWork(uint32_t 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_t 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(FileProxy::CreateTemporaryCallback callback) { - DCHECK(!callback.is_null()); - PassFile(); - std::move(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, std::move(file)) { - } - - void RunWork() { - if (file_.GetInfo(&file_info_)) - error_ = File::FILE_OK; - } - - void Reply(FileProxy::GetFileInfoCallback callback) { - PassFile(); - DCHECK(!callback.is_null()); - std::move(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, std::move(file)), - buffer_(new char[bytes_to_read]), - bytes_to_read_(bytes_to_read), - bytes_read_(0) { - } - - void RunWork(int64_t offset) { - bytes_read_ = file_.Read(offset, buffer_.get(), bytes_to_read_); - error_ = (bytes_read_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK; - } - - void Reply(FileProxy::ReadCallback callback) { - PassFile(); - DCHECK(!callback.is_null()); - std::move(callback).Run(error_, buffer_.get(), bytes_read_); - } - - private: - std::unique_ptr 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, std::move(file)), - 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_t offset) { - bytes_written_ = file_.Write(offset, buffer_.get(), bytes_to_write_); - error_ = (bytes_written_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK; - } - - void Reply(FileProxy::WriteCallback callback) { - PassFile(); - if (!callback.is_null()) - std::move(callback).Run(error_, bytes_written_); - } - - private: - std::unique_ptr 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, BindOnce(&FileDeleter, std::move(file_))); -} - -bool FileProxy::CreateOrOpen(const FilePath& file_path, - uint32_t file_flags, - StatusCallback callback) { - DCHECK(!file_.IsValid()); - CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File()); - return task_runner_->PostTaskAndReply( - FROM_HERE, - BindOnce(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path, - file_flags), - BindOnce(&CreateOrOpenHelper::Reply, Owned(helper), std::move(callback))); -} - -bool FileProxy::CreateTemporary(uint32_t additional_file_flags, - CreateTemporaryCallback callback) { - DCHECK(!file_.IsValid()); - CreateTemporaryHelper* helper = new CreateTemporaryHelper(this, File()); - return task_runner_->PostTaskAndReply( - FROM_HERE, - BindOnce(&CreateTemporaryHelper::RunWork, Unretained(helper), - additional_file_flags), - BindOnce(&CreateTemporaryHelper::Reply, Owned(helper), - std::move(callback))); -} - -bool FileProxy::IsValid() const { - return file_.IsValid(); -} - -void FileProxy::SetFile(File file) { - DCHECK(!file_.IsValid()); - file_ = std::move(file); -} - -File FileProxy::TakeFile() { - return std::move(file_); -} - -File FileProxy::DuplicateFile() { - return file_.Duplicate(); -} - -PlatformFile FileProxy::GetPlatformFile() const { - return file_.GetPlatformFile(); -} - -bool FileProxy::Close(StatusCallback callback) { - DCHECK(file_.IsValid()); - GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_)); - return task_runner_->PostTaskAndReply( - FROM_HERE, BindOnce(&GenericFileHelper::Close, Unretained(helper)), - BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback))); -} - -bool FileProxy::GetInfo(GetFileInfoCallback callback) { - DCHECK(file_.IsValid()); - GetInfoHelper* helper = new GetInfoHelper(this, std::move(file_)); - return task_runner_->PostTaskAndReply( - FROM_HERE, BindOnce(&GetInfoHelper::RunWork, Unretained(helper)), - BindOnce(&GetInfoHelper::Reply, Owned(helper), std::move(callback))); -} - -bool FileProxy::Read(int64_t offset, int bytes_to_read, ReadCallback callback) { - DCHECK(file_.IsValid()); - if (bytes_to_read < 0) - return false; - - ReadHelper* helper = new ReadHelper(this, std::move(file_), bytes_to_read); - return task_runner_->PostTaskAndReply( - FROM_HERE, BindOnce(&ReadHelper::RunWork, Unretained(helper), offset), - BindOnce(&ReadHelper::Reply, Owned(helper), std::move(callback))); -} - -bool FileProxy::Write(int64_t offset, - const char* buffer, - int bytes_to_write, - WriteCallback callback) { - DCHECK(file_.IsValid()); - if (bytes_to_write <= 0 || buffer == nullptr) - return false; - - WriteHelper* helper = - new WriteHelper(this, std::move(file_), buffer, bytes_to_write); - return task_runner_->PostTaskAndReply( - FROM_HERE, BindOnce(&WriteHelper::RunWork, Unretained(helper), offset), - BindOnce(&WriteHelper::Reply, Owned(helper), std::move(callback))); -} - -bool FileProxy::SetTimes(Time last_access_time, - Time last_modified_time, - StatusCallback callback) { - DCHECK(file_.IsValid()); - GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_)); - return task_runner_->PostTaskAndReply( - FROM_HERE, - BindOnce(&GenericFileHelper::SetTimes, Unretained(helper), - last_access_time, last_modified_time), - BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback))); -} - -bool FileProxy::SetLength(int64_t length, StatusCallback callback) { - DCHECK(file_.IsValid()); - GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_)); - return task_runner_->PostTaskAndReply( - FROM_HERE, - BindOnce(&GenericFileHelper::SetLength, Unretained(helper), length), - BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback))); -} - -bool FileProxy::Flush(StatusCallback callback) { - DCHECK(file_.IsValid()); - GenericFileHelper* helper = new GenericFileHelper(this, std::move(file_)); - return task_runner_->PostTaskAndReply( - FROM_HERE, BindOnce(&GenericFileHelper::Flush, Unretained(helper)), - BindOnce(&GenericFileHelper::Reply, Owned(helper), std::move(callback))); -} - -} // namespace base diff --git a/files/file_proxy.h b/files/file_proxy.h deleted file mode 100644 index d17e4d3b0..000000000 --- a/files/file_proxy.h +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FILES_FILE_PROXY_H_ -#define BASE_FILES_FILE_PROXY_H_ - -#include - -#include "base/base_export.h" -#include "base/callback_forward.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" - -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 { - 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. - using StatusCallback = OnceCallback; - using CreateTemporaryCallback = - OnceCallback; - using GetFileInfoCallback = - OnceCallback; - using ReadCallback = - OnceCallback; - using WriteCallback = OnceCallback; - - 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_t file_flags, - 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_t additional_file_flags, - 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(); - - // Returns a new File object that is a duplicate of the underlying |file_|. - // See the comment at File::Duplicate for caveats. - File DuplicateFile(); - - PlatformFile GetPlatformFile() const; - - // Proxies File::Close. The callback can be null. - // This returns false if task posting to |task_runner| has failed. - bool Close(StatusCallback callback); - - // Proxies File::GetInfo. The callback can't be null. - // This returns false if task posting to |task_runner| has failed. - bool GetInfo(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_t offset, int bytes_to_read, 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_t offset, - const char* buffer, - int bytes_to_write, - 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, - StatusCallback callback); - - // Proxies File::SetLength. The callback can be null. - // This returns false if task posting to |task_runner| has failed. - bool SetLength(int64_t length, StatusCallback callback); - - // Proxies File::Flush. The callback can be null. - // This returns false if task posting to |task_runner| has failed. - bool Flush(StatusCallback callback); - - private: - friend class FileHelper; - TaskRunner* task_runner() { return task_runner_.get(); } - - scoped_refptr task_runner_; - File file_; - DISALLOW_COPY_AND_ASSIGN(FileProxy); -}; - -} // namespace base - -#endif // BASE_FILES_FILE_PROXY_H_ diff --git a/files/file_proxy_unittest.cc b/files/file_proxy_unittest.cc deleted file mode 100644 index cb689db2f..000000000 --- a/files/file_proxy_unittest.cc +++ /dev/null @@ -1,401 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/file_proxy.h" - -#include -#include - -#include - -#include "base/bind.h" -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/threading/thread.h" -#include "base/threading/thread_restrictions.h" -#include "build/build_config.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) {} - - void SetUp() override { - ASSERT_TRUE(dir_.CreateUniqueTempDir()); - ASSERT_TRUE(file_thread_.Start()); - } - - void DidFinish(File::Error error) { - error_ = error; - RunLoop::QuitCurrentWhenIdleDeprecated(); - } - - void DidCreateOrOpen(File::Error error) { - error_ = error; - RunLoop::QuitCurrentWhenIdleDeprecated(); - } - - void DidCreateTemporary(File::Error error, - const FilePath& path) { - error_ = error; - path_ = path; - RunLoop::QuitCurrentWhenIdleDeprecated(); - } - - void DidGetFileInfo(File::Error error, - const File::Info& file_info) { - error_ = error; - file_info_ = file_info; - RunLoop::QuitCurrentWhenIdleDeprecated(); - } - - void DidRead(File::Error error, - const char* data, - int bytes_read) { - error_ = error; - buffer_.resize(bytes_read); - memcpy(&buffer_[0], data, bytes_read); - RunLoop::QuitCurrentWhenIdleDeprecated(); - } - - void DidWrite(File::Error error, - int bytes_written) { - error_ = error; - bytes_written_ = bytes_written; - RunLoop::QuitCurrentWhenIdleDeprecated(); - } - - protected: - void CreateProxy(uint32_t flags, FileProxy* proxy) { - proxy->CreateOrOpen( - TestPath(), flags, - BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); - RunLoop().Run(); - EXPECT_TRUE(proxy->IsValid()); - } - - TaskRunner* file_task_runner() const { - return file_thread_.task_runner().get(); - } - const FilePath& TestDirPath() const { return dir_.GetPath(); } - const FilePath TestPath() const { return dir_.GetPath().AppendASCII("test"); } - - ScopedTempDir dir_; - MessageLoopForIO message_loop_; - Thread file_thread_; - - File::Error error_; - FilePath path_; - File::Info file_info_; - std::vector buffer_; - int bytes_written_; - WeakPtrFactory weak_factory_; -}; - -TEST_F(FileProxyTest, CreateOrOpen_Create) { - FileProxy proxy(file_task_runner()); - proxy.CreateOrOpen( - TestPath(), File::FLAG_CREATE | File::FLAG_READ, - BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); - RunLoop().Run(); - - EXPECT_EQ(File::FILE_OK, error_); - EXPECT_TRUE(proxy.IsValid()); - EXPECT_TRUE(proxy.created()); - EXPECT_TRUE(PathExists(TestPath())); -} - -TEST_F(FileProxyTest, CreateOrOpen_Open) { - // Creates a file. - base::WriteFile(TestPath(), nullptr, 0); - ASSERT_TRUE(PathExists(TestPath())); - - // Opens the created file. - FileProxy proxy(file_task_runner()); - proxy.CreateOrOpen( - TestPath(), File::FLAG_OPEN | File::FLAG_READ, - BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); - RunLoop().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( - TestPath(), File::FLAG_OPEN | File::FLAG_READ, - BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); - RunLoop().Run(); - EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, error_); - EXPECT_FALSE(proxy.IsValid()); - EXPECT_FALSE(proxy.created()); - EXPECT_FALSE(PathExists(TestPath())); -} - -TEST_F(FileProxyTest, CreateOrOpen_AbandonedCreate) { - bool prev = ThreadRestrictions::SetIOAllowed(false); - { - FileProxy proxy(file_task_runner()); - proxy.CreateOrOpen( - TestPath(), File::FLAG_CREATE | File::FLAG_READ, - BindOnce(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr())); - } - RunLoop().Run(); - ThreadRestrictions::SetIOAllowed(prev); - - EXPECT_TRUE(PathExists(TestPath())); -} - -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(TestPath(), TestDirPath().AppendASCII("new"))); -#endif - - proxy.Close(BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); - RunLoop().Run(); - EXPECT_EQ(File::FILE_OK, error_); - EXPECT_FALSE(proxy.IsValid()); - - // Now it should pass on all platforms. - EXPECT_TRUE(base::Move(TestPath(), TestDirPath().AppendASCII("new"))); -} - -TEST_F(FileProxyTest, CreateTemporary) { - { - FileProxy proxy(file_task_runner()); - proxy.CreateTemporary(0 /* additional_file_flags */, - BindOnce(&FileProxyTest::DidCreateTemporary, - weak_factory_.GetWeakPtr())); - RunLoop().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, - BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr())); - RunLoop().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(TestPath(), File::FLAG_CREATE | File::FLAG_READ); - ASSERT_TRUE(file.IsValid()); - FileProxy proxy(file_task_runner()); - EXPECT_FALSE(proxy.IsValid()); - proxy.SetFile(std::move(file)); - EXPECT_TRUE(proxy.IsValid()); - EXPECT_FALSE(file.IsValid()); - - file = proxy.TakeFile(); - EXPECT_FALSE(proxy.IsValid()); - EXPECT_TRUE(file.IsValid()); -} - -TEST_F(FileProxyTest, DuplicateFile) { - FileProxy proxy(file_task_runner()); - CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy); - ASSERT_TRUE(proxy.IsValid()); - - base::File duplicate = proxy.DuplicateFile(); - EXPECT_TRUE(proxy.IsValid()); - EXPECT_TRUE(duplicate.IsValid()); - - FileProxy invalid_proxy(file_task_runner()); - ASSERT_FALSE(invalid_proxy.IsValid()); - - base::File invalid_duplicate = invalid_proxy.DuplicateFile(); - EXPECT_FALSE(invalid_proxy.IsValid()); - EXPECT_FALSE(invalid_duplicate.IsValid()); -} - -TEST_F(FileProxyTest, GetInfo) { - // Setup. - ASSERT_EQ(4, base::WriteFile(TestPath(), "test", 4)); - File::Info expected_info; - GetFileInfo(TestPath(), &expected_info); - - // Run. - FileProxy proxy(file_task_runner()); - CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy); - proxy.GetInfo( - BindOnce(&FileProxyTest::DidGetFileInfo, weak_factory_.GetWeakPtr())); - RunLoop().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(TestPath(), expected_data, expected_bytes)); - - // Run. - FileProxy proxy(file_task_runner()); - CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy); - - proxy.Read(0, 128, - BindOnce(&FileProxyTest::DidRead, weak_factory_.GetWeakPtr())); - RunLoop().Run(); - - // Verify. - EXPECT_EQ(File::FILE_OK, error_); - EXPECT_EQ(expected_bytes, static_cast(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(data); - proxy.Write(0, data, data_bytes, - BindOnce(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr())); - RunLoop().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(BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); - RunLoop().Run(); - EXPECT_EQ(File::FILE_OK, error_); - - // Verify the written data. - char buffer[10]; - EXPECT_EQ(data_bytes, base::ReadFile(TestPath(), buffer, data_bytes)); - for (int i = 0; i < data_bytes; ++i) { - EXPECT_EQ(data[i], buffer[i]); - } -} - -#if defined(OS_ANDROID) || defined(OS_FUCHSIA) -// Flaky on Android, see http://crbug.com/489602 -// TODO(crbug.com/851734): Implementation depends on stat, which is not -// implemented on Fuchsia -#define MAYBE_SetTimes DISABLED_SetTimes -#else -#define MAYBE_SetTimes SetTimes -#endif -TEST_F(FileProxyTest, MAYBE_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, - BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); - RunLoop().Run(); - EXPECT_EQ(File::FILE_OK, error_); - - File::Info info; - GetFileInfo(TestPath(), &info); - - // The returned values may only have the seconds precision, so we cast - // the double values to int here. - EXPECT_EQ(static_cast(last_modified_time.ToDoubleT()), - static_cast(info.last_modified.ToDoubleT())); - EXPECT_EQ(static_cast(last_accessed_time.ToDoubleT()), - static_cast(info.last_accessed.ToDoubleT())); -} - -TEST_F(FileProxyTest, SetLength_Shrink) { - // Setup. - const char kTestData[] = "0123456789"; - ASSERT_EQ(10, base::WriteFile(TestPath(), kTestData, 10)); - File::Info info; - GetFileInfo(TestPath(), &info); - ASSERT_EQ(10, info.size); - - // Run. - FileProxy proxy(file_task_runner()); - CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy); - proxy.SetLength( - 7, BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); - RunLoop().Run(); - - // Verify. - GetFileInfo(TestPath(), &info); - ASSERT_EQ(7, info.size); - - char buffer[7]; - EXPECT_EQ(7, base::ReadFile(TestPath(), 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(TestPath(), kTestData, 10)); - File::Info info; - GetFileInfo(TestPath(), &info); - ASSERT_EQ(10, info.size); - - // Run. - FileProxy proxy(file_task_runner()); - CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy); - proxy.SetLength( - 53, BindOnce(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr())); - RunLoop().Run(); - - // Verify. - GetFileInfo(TestPath(), &info); - ASSERT_EQ(53, info.size); - - char buffer[53]; - EXPECT_EQ(53, base::ReadFile(TestPath(), 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/files/file_tracing.cc b/files/file_tracing.cc deleted file mode 100644 index 48f57412f..000000000 --- a/files/file_tracing.cc +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/file_tracing.h" - -#include "base/atomicops.h" -#include "base/files/file.h" - -using base::subtle::AtomicWord; - -namespace base { - -namespace { -AtomicWord g_provider; -} - -FileTracing::Provider* GetProvider() { - AtomicWord provider = base::subtle::Acquire_Load(&g_provider); - return reinterpret_cast(provider); -} - -// static -bool FileTracing::IsCategoryEnabled() { - FileTracing::Provider* provider = GetProvider(); - return provider && provider->FileTracingCategoryIsEnabled(); -} - -// static -void FileTracing::SetProvider(FileTracing::Provider* provider) { - base::subtle::Release_Store(&g_provider, - reinterpret_cast(provider)); -} - -FileTracing::ScopedEnabler::ScopedEnabler() { - FileTracing::Provider* provider = GetProvider(); - if (provider) - provider->FileTracingEnable(this); -} - -FileTracing::ScopedEnabler::~ScopedEnabler() { - FileTracing::Provider* provider = GetProvider(); - if (provider) - provider->FileTracingDisable(this); -} - -FileTracing::ScopedTrace::ScopedTrace() : id_(nullptr) {} - -FileTracing::ScopedTrace::~ScopedTrace() { - if (id_) { - FileTracing::Provider* provider = GetProvider(); - if (provider) - provider->FileTracingEventEnd(name_, id_); - } -} - -void FileTracing::ScopedTrace::Initialize(const char* name, - const File* file, - int64_t size) { - id_ = &file->trace_enabler_; - name_ = name; - GetProvider()->FileTracingEventBegin(name_, id_, file->tracing_path_, size); -} - -} // namespace base diff --git a/files/file_tracing.h b/files/file_tracing.h deleted file mode 100644 index 1fbfcd449..000000000 --- a/files/file_tracing.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FILES_FILE_TRACING_H_ -#define BASE_FILES_FILE_TRACING_H_ - -#include - -#include "base/base_export.h" -#include "base/macros.h" - -#define FILE_TRACING_PREFIX "File" - -#define SCOPED_FILE_TRACE_WITH_SIZE(name, size) \ - FileTracing::ScopedTrace scoped_file_trace; \ - if (FileTracing::IsCategoryEnabled()) \ - scoped_file_trace.Initialize(FILE_TRACING_PREFIX "::" name, this, size) - -#define SCOPED_FILE_TRACE(name) SCOPED_FILE_TRACE_WITH_SIZE(name, 0) - -namespace base { - -class File; -class FilePath; - -class BASE_EXPORT FileTracing { - public: - // Whether the file tracing category is enabled. - static bool IsCategoryEnabled(); - - class Provider { - public: - virtual ~Provider() = default; - - // Whether the file tracing category is currently enabled. - virtual bool FileTracingCategoryIsEnabled() const = 0; - - // Enables file tracing for |id|. Must be called before recording events. - virtual void FileTracingEnable(const void* id) = 0; - - // Disables file tracing for |id|. - virtual void FileTracingDisable(const void* id) = 0; - - // Begins an event for |id| with |name|. |path| tells where in the directory - // structure the event is happening (and may be blank). |size| is the number - // of bytes involved in the event. - virtual void FileTracingEventBegin(const char* name, - const void* id, - const FilePath& path, - int64_t size) = 0; - - // Ends an event for |id| with |name|. - virtual void FileTracingEventEnd(const char* name, const void* id) = 0; - }; - - // Sets a global file tracing provider to query categories and record events. - static void SetProvider(Provider* provider); - - // Enables file tracing while in scope. - class ScopedEnabler { - public: - ScopedEnabler(); - ~ScopedEnabler(); - }; - - class ScopedTrace { - public: - ScopedTrace(); - ~ScopedTrace(); - - // Called only if the tracing category is enabled. |name| is the name of the - // event to trace (e.g. "Read", "Write") and must have an application - // lifetime (e.g. static or literal). |file| is the file being traced; must - // outlive this class. |size| is the size (in bytes) of this event. - void Initialize(const char* name, const File* file, int64_t size); - - private: - // The ID of this trace. Based on the |file| passed to |Initialize()|. Must - // outlive this class. - const void* id_; - - // The name of the event to trace (e.g. "Read", "Write"). Prefixed with - // "File". - const char* name_; - - DISALLOW_COPY_AND_ASSIGN(ScopedTrace); - }; - - DISALLOW_COPY_AND_ASSIGN(FileTracing); -}; - -} // namespace base - -#endif // BASE_FILES_FILE_TRACING_H_ diff --git a/files/file_unittest.cc b/files/file_unittest.cc deleted file mode 100644 index 8a5732280..000000000 --- a/files/file_unittest.cc +++ /dev/null @@ -1,762 +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/files/file.h" - -#include - -#include - -#include "base/files/file_util.h" -#include "base/files/memory_mapped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::File; -using base::FilePath; - -TEST(FileTest, Create) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().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_details()); - EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, base::File::GetLastFileError()); - } - - { - // Open or create a file. - 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_details()); - } - - { - // Open an existing file. - 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_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(); - EXPECT_FALSE(file.IsValid()); - } - - { - // Create a file that exists. - 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_details()); - EXPECT_EQ(base::File::FILE_ERROR_EXISTS, base::File::GetLastFileError()); - } - - { - // Create or overwrite a file. - File file(file_path, - 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_details()); - } - - { - // Create a delete-on-close file. - file_path = temp_dir.GetPath().AppendASCII("create_file_2"); - File file(file_path, - base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ | - base::File::FLAG_DELETE_ON_CLOSE); - EXPECT_TRUE(file.IsValid()); - EXPECT_TRUE(file.created()); - EXPECT_EQ(base::File::FILE_OK, file.error_details()); - } - - EXPECT_FALSE(base::PathExists(file_path)); -} - -TEST(FileTest, SelfSwap) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("create_file_1"); - File file(file_path, - base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_DELETE_ON_CLOSE); - std::swap(file, file); - EXPECT_TRUE(file.IsValid()); -} - -TEST(FileTest, Async) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().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.GetPath().AppendASCII("create_file_1"); - - // Create a file. - File file(file_path, - base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ | - base::File::FLAG_SHARE_DELETE); - EXPECT_TRUE(file.IsValid()); - EXPECT_TRUE(file.created()); - 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, - base::File::FLAG_OPEN | base::File::FLAG_DELETE_ON_CLOSE | - base::File::FLAG_READ); - EXPECT_TRUE(file.IsValid()); - EXPECT_FALSE(same_file.created()); - EXPECT_EQ(base::File::FILE_OK, same_file.error_details()); - - // Close both handles and check that the file is gone. - file.Close(); - same_file.Close(); - EXPECT_FALSE(base::PathExists(file_path)); -} - -TEST(FileTest, ReadWrite) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("read_write_file"); - File file(file_path, - base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE); - ASSERT_TRUE(file.IsValid()); - - char data_to_write[] = "test"; - const int kTestDataSize = 4; - - // Write 0 bytes to the file. - int bytes_written = file.Write(0, data_to_write, 0); - EXPECT_EQ(0, bytes_written); - - // Write 0 bytes, with buf=nullptr. - bytes_written = file.Write(0, nullptr, 0); - EXPECT_EQ(0, bytes_written); - - // Write "test" to the file. - bytes_written = file.Write(0, data_to_write, kTestDataSize); - EXPECT_EQ(kTestDataSize, bytes_written); - - // Read from EOF. - char data_read_1[32]; - int bytes_read = file.Read(kTestDataSize, data_read_1, kTestDataSize); - EXPECT_EQ(0, bytes_read); - - // Read from somewhere in the middle of the file. - const int kPartialReadOffset = 1; - bytes_read = file.Read(kPartialReadOffset, data_read_1, kTestDataSize); - EXPECT_EQ(kTestDataSize - kPartialReadOffset, bytes_read); - for (int i = 0; i < bytes_read; i++) - EXPECT_EQ(data_to_write[i + kPartialReadOffset], data_read_1[i]); - - // Read 0 bytes. - bytes_read = file.Read(0, data_read_1, 0); - EXPECT_EQ(0, bytes_read); - - // Read the entire file. - bytes_read = file.Read(0, data_read_1, kTestDataSize); - EXPECT_EQ(kTestDataSize, bytes_read); - for (int i = 0; i < bytes_read; i++) - EXPECT_EQ(data_to_write[i], data_read_1[i]); - - // Read again, but using the trivial native wrapper. - bytes_read = file.ReadNoBestEffort(0, data_read_1, kTestDataSize); - EXPECT_LE(bytes_read, kTestDataSize); - for (int i = 0; i < bytes_read; i++) - EXPECT_EQ(data_to_write[i], data_read_1[i]); - - // Write past the end of the file. - const int kOffsetBeyondEndOfFile = 10; - const int kPartialWriteLength = 2; - bytes_written = file.Write(kOffsetBeyondEndOfFile, - data_to_write, kPartialWriteLength); - EXPECT_EQ(kPartialWriteLength, bytes_written); - - // Make sure the file was extended. - int64_t file_size = 0; - EXPECT_TRUE(GetFileSize(file_path, &file_size)); - EXPECT_EQ(kOffsetBeyondEndOfFile + kPartialWriteLength, file_size); - - // Make sure the file was zero-padded. - char data_read_2[32]; - bytes_read = file.Read(0, data_read_2, static_cast(file_size)); - EXPECT_EQ(file_size, bytes_read); - for (int i = 0; i < kTestDataSize; i++) - EXPECT_EQ(data_to_write[i], data_read_2[i]); - for (int i = kTestDataSize; i < kOffsetBeyondEndOfFile; i++) - EXPECT_EQ(0, data_read_2[i]); - for (int i = kOffsetBeyondEndOfFile; i < file_size; i++) - EXPECT_EQ(data_to_write[i - kOffsetBeyondEndOfFile], data_read_2[i]); -} - -TEST(FileTest, GetLastFileError) { -#if defined(OS_WIN) - ::SetLastError(ERROR_ACCESS_DENIED); -#else - errno = EACCES; -#endif - EXPECT_EQ(File::FILE_ERROR_ACCESS_DENIED, File::GetLastFileError()); - - base::ScopedTempDir temp_dir; - EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); - - FilePath nonexistent_path(temp_dir.GetPath().AppendASCII("nonexistent")); - File file(nonexistent_path, File::FLAG_OPEN | File::FLAG_READ); - File::Error last_error = File::GetLastFileError(); - EXPECT_FALSE(file.IsValid()); - EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, file.error_details()); - EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, last_error); -} - -TEST(FileTest, Append) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("append_file"); - File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_APPEND); - ASSERT_TRUE(file.IsValid()); - - char data_to_write[] = "test"; - const int kTestDataSize = 4; - - // Write 0 bytes to the file. - int bytes_written = file.Write(0, data_to_write, 0); - EXPECT_EQ(0, bytes_written); - - // Write 0 bytes, with buf=nullptr. - bytes_written = file.Write(0, nullptr, 0); - EXPECT_EQ(0, bytes_written); - - // Write "test" to the file. - bytes_written = file.Write(0, data_to_write, kTestDataSize); - EXPECT_EQ(kTestDataSize, bytes_written); - - file.Close(); - File file2(file_path, - base::File::FLAG_OPEN | base::File::FLAG_READ | - base::File::FLAG_APPEND); - ASSERT_TRUE(file2.IsValid()); - - // Test passing the file around. - file = std::move(file2); - EXPECT_FALSE(file2.IsValid()); - ASSERT_TRUE(file.IsValid()); - - char append_data_to_write[] = "78"; - const int kAppendDataSize = 2; - - // Append "78" to the file. - bytes_written = file.Write(0, append_data_to_write, kAppendDataSize); - EXPECT_EQ(kAppendDataSize, bytes_written); - - // Read the entire file. - char data_read_1[32]; - int bytes_read = file.Read(0, data_read_1, - kTestDataSize + kAppendDataSize); - EXPECT_EQ(kTestDataSize + kAppendDataSize, bytes_read); - for (int i = 0; i < kTestDataSize; i++) - EXPECT_EQ(data_to_write[i], data_read_1[i]); - for (int i = 0; i < kAppendDataSize; i++) - EXPECT_EQ(append_data_to_write[i], data_read_1[kTestDataSize + i]); -} - - -TEST(FileTest, Length) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("truncate_file"); - File file(file_path, - 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"; - int kTestDataSize = 4; - int bytes_written = file.Write(0, data_to_write, kTestDataSize); - EXPECT_EQ(kTestDataSize, bytes_written); - - // Extend the file. - const int kExtendedFileLength = 10; - int64_t file_size = 0; - EXPECT_TRUE(file.SetLength(kExtendedFileLength)); - EXPECT_EQ(kExtendedFileLength, file.GetLength()); - EXPECT_TRUE(GetFileSize(file_path, &file_size)); - EXPECT_EQ(kExtendedFileLength, file_size); - - // Make sure the file was zero-padded. - char data_read[32]; - int bytes_read = file.Read(0, data_read, static_cast(file_size)); - EXPECT_EQ(file_size, bytes_read); - for (int i = 0; i < kTestDataSize; i++) - EXPECT_EQ(data_to_write[i], data_read[i]); - for (int i = kTestDataSize; i < file_size; i++) - EXPECT_EQ(0, data_read[i]); - - // Truncate the file. - const int kTruncatedFileLength = 2; - EXPECT_TRUE(file.SetLength(kTruncatedFileLength)); - EXPECT_EQ(kTruncatedFileLength, file.GetLength()); - EXPECT_TRUE(GetFileSize(file_path, &file_size)); - EXPECT_EQ(kTruncatedFileLength, file_size); - - // Make sure the file was truncated. - bytes_read = file.Read(0, data_read, kTestDataSize); - EXPECT_EQ(file_size, bytes_read); - for (int i = 0; i < file_size; i++) - EXPECT_EQ(data_to_write[i], data_read[i]); - - // Close the file and reopen with base::File::FLAG_CREATE_ALWAYS, and make - // sure the file is empty (old file was overridden). - file.Close(); - file.Initialize(file_path, - base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); - EXPECT_EQ(0, file.GetLength()); -} - -// Flakily fails: http://crbug.com/86494 -#if defined(OS_ANDROID) -TEST(FileTest, TouchGetInfo) { -#else -TEST(FileTest, DISABLED_TouchGetInfo) { -#endif - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - File file(temp_dir.GetPath().AppendASCII("touch_get_info_file"), - base::File::FLAG_CREATE | base::File::FLAG_WRITE | - base::File::FLAG_WRITE_ATTRIBUTES); - ASSERT_TRUE(file.IsValid()); - - // Get info for a newly created file. - base::File::Info info; - EXPECT_TRUE(file.GetInfo(&info)); - - // Add 2 seconds to account for possible rounding errors on - // filesystems that use a 1s or 2s timestamp granularity. - base::Time now = base::Time::Now() + base::TimeDelta::FromSeconds(2); - EXPECT_EQ(0, info.size); - EXPECT_FALSE(info.is_directory); - EXPECT_FALSE(info.is_symbolic_link); - EXPECT_LE(info.last_accessed.ToInternalValue(), now.ToInternalValue()); - EXPECT_LE(info.last_modified.ToInternalValue(), now.ToInternalValue()); - EXPECT_LE(info.creation_time.ToInternalValue(), now.ToInternalValue()); - base::Time creation_time = info.creation_time; - - // Write "test" to the file. - char data[] = "test"; - const int kTestDataSize = 4; - int bytes_written = file.Write(0, data, kTestDataSize); - EXPECT_EQ(kTestDataSize, bytes_written); - - // Change the last_accessed and last_modified dates. - // It's best to add values that are multiples of 2 (in seconds) - // to the current last_accessed and last_modified times, because - // FATxx uses a 2s timestamp granularity. - base::Time new_last_accessed = - info.last_accessed + base::TimeDelta::FromSeconds(234); - base::Time new_last_modified = - info.last_modified + base::TimeDelta::FromMinutes(567); - - EXPECT_TRUE(file.SetTimes(new_last_accessed, new_last_modified)); - - // Make sure the file info was updated accordingly. - EXPECT_TRUE(file.GetInfo(&info)); - EXPECT_EQ(info.size, kTestDataSize); - EXPECT_FALSE(info.is_directory); - EXPECT_FALSE(info.is_symbolic_link); - - // ext2/ext3 and HPS/HPS+ seem to have a timestamp granularity of 1s. -#if defined(OS_POSIX) - EXPECT_EQ(info.last_accessed.ToTimeVal().tv_sec, - new_last_accessed.ToTimeVal().tv_sec); - EXPECT_EQ(info.last_modified.ToTimeVal().tv_sec, - new_last_modified.ToTimeVal().tv_sec); -#else - EXPECT_EQ(info.last_accessed.ToInternalValue(), - new_last_accessed.ToInternalValue()); - EXPECT_EQ(info.last_modified.ToInternalValue(), - new_last_modified.ToInternalValue()); -#endif - - EXPECT_EQ(info.creation_time.ToInternalValue(), - creation_time.ToInternalValue()); -} - -TEST(FileTest, ReadAtCurrentPosition) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = - temp_dir.GetPath().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 = sizeof(kData) - 1; - EXPECT_EQ(kDataSize, file.Write(0, kData, kDataSize)); - - EXPECT_EQ(0, file.Seek(base::File::FROM_BEGIN, 0)); - - char buffer[kDataSize]; - int first_chunk_size = kDataSize / 2; - EXPECT_EQ(first_chunk_size, file.ReadAtCurrentPos(buffer, first_chunk_size)); - 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)); -} - -TEST(FileTest, WriteAtCurrentPosition) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = - temp_dir.GetPath().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)); -} - -TEST(FileTest, Seek) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("seek_file"); - File file(file_path, - base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE); - ASSERT_TRUE(file.IsValid()); - - const int64_t kOffset = 10; - EXPECT_EQ(kOffset, file.Seek(base::File::FROM_BEGIN, kOffset)); - EXPECT_EQ(2 * kOffset, file.Seek(base::File::FROM_CURRENT, kOffset)); - EXPECT_EQ(kOffset, file.Seek(base::File::FROM_CURRENT, -kOffset)); - EXPECT_TRUE(file.SetLength(kOffset * 2)); - EXPECT_EQ(kOffset, file.Seek(base::File::FROM_END, -kOffset)); -} - -TEST(FileTest, Duplicate) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - File file(file_path,(base::File::FLAG_CREATE | - base::File::FLAG_READ | - base::File::FLAG_WRITE)); - ASSERT_TRUE(file.IsValid()); - - File file2(file.Duplicate()); - ASSERT_TRUE(file2.IsValid()); - - // Write through one handle, close it, read through the other. - static const char kData[] = "now is a good time."; - static const int kDataLen = sizeof(kData) - 1; - - ASSERT_EQ(0, file.Seek(base::File::FROM_CURRENT, 0)); - ASSERT_EQ(0, file2.Seek(base::File::FROM_CURRENT, 0)); - ASSERT_EQ(kDataLen, file.WriteAtCurrentPos(kData, kDataLen)); - ASSERT_EQ(kDataLen, file.Seek(base::File::FROM_CURRENT, 0)); - ASSERT_EQ(kDataLen, file2.Seek(base::File::FROM_CURRENT, 0)); - file.Close(); - char buf[kDataLen]; - ASSERT_EQ(kDataLen, file2.Read(0, &buf[0], kDataLen)); - ASSERT_EQ(std::string(kData, kDataLen), std::string(&buf[0], kDataLen)); -} - -TEST(FileTest, DuplicateDeleteOnClose) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - File file(file_path,(base::File::FLAG_CREATE | - base::File::FLAG_READ | - base::File::FLAG_WRITE | - base::File::FLAG_DELETE_ON_CLOSE)); - ASSERT_TRUE(file.IsValid()); - File file2(file.Duplicate()); - ASSERT_TRUE(file2.IsValid()); - file.Close(); - file2.Close(); - ASSERT_FALSE(base::PathExists(file_path)); -} - -#if defined(OS_WIN) -// Flakily times out on Windows, see http://crbug.com/846276. -#define MAYBE_WriteDataToLargeOffset DISABLED_WriteDataToLargeOffset -#else -#define MAYBE_WriteDataToLargeOffset WriteDataToLargeOffset -#endif -TEST(FileTest, MAYBE_WriteDataToLargeOffset) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - File file(file_path, - (base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE)); - ASSERT_TRUE(file.IsValid()); - - const char kData[] = "this file is sparse."; - const int kDataLen = sizeof(kData) - 1; - const int64_t kLargeFileOffset = (1LL << 31); - - // If the file fails to write, it is probably we are running out of disk space - // and the file system doesn't support sparse file. - if (file.Write(kLargeFileOffset - kDataLen - 1, kData, kDataLen) < 0) - return; - - ASSERT_EQ(kDataLen, file.Write(kLargeFileOffset + 1, kData, kDataLen)); -} - -#if defined(OS_WIN) -TEST(FileTest, GetInfoForDirectory) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath empty_dir = - temp_dir.GetPath().Append(FILE_PATH_LITERAL("gpfi_test")); - ASSERT_TRUE(CreateDirectory(empty_dir)); - - base::File dir( - ::CreateFile(empty_dir.value().c_str(), - GENERIC_READ | GENERIC_WRITE, - 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); -} - -TEST(FileTest, DeleteNoop) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - - // Creating and closing a file with DELETE perms should do nothing special. - File file(file_path, - (base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE)); - ASSERT_TRUE(file.IsValid()); - file.Close(); - ASSERT_TRUE(base::PathExists(file_path)); -} - -TEST(FileTest, Delete) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - - // Creating a file with DELETE and then marking for delete on close should - // delete it. - File file(file_path, - (base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE)); - ASSERT_TRUE(file.IsValid()); - ASSERT_TRUE(file.DeleteOnClose(true)); - file.Close(); - ASSERT_FALSE(base::PathExists(file_path)); -} - -TEST(FileTest, DeleteThenRevoke) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - - // Creating a file with DELETE, marking it for delete, then clearing delete on - // close should not delete it. - File file(file_path, - (base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE)); - ASSERT_TRUE(file.IsValid()); - ASSERT_TRUE(file.DeleteOnClose(true)); - ASSERT_TRUE(file.DeleteOnClose(false)); - file.Close(); - ASSERT_TRUE(base::PathExists(file_path)); -} - -TEST(FileTest, IrrevokableDeleteOnClose) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - - // DELETE_ON_CLOSE cannot be revoked by this opener. - File file( - file_path, - (base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE | - base::File::FLAG_SHARE_DELETE | base::File::FLAG_CAN_DELETE_ON_CLOSE)); - ASSERT_TRUE(file.IsValid()); - // https://msdn.microsoft.com/library/windows/desktop/aa364221.aspx says that - // setting the dispositon has no effect if the handle was opened with - // FLAG_DELETE_ON_CLOSE. Do not make the test's success dependent on whether - // or not SetFileInformationByHandle indicates success or failure. (It happens - // to indicate success on Windows 10.) - file.DeleteOnClose(false); - file.Close(); - ASSERT_FALSE(base::PathExists(file_path)); -} - -TEST(FileTest, IrrevokableDeleteOnCloseOther) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - - // DELETE_ON_CLOSE cannot be revoked by another opener. - File file( - file_path, - (base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE | base::File::FLAG_DELETE_ON_CLOSE | - base::File::FLAG_SHARE_DELETE | base::File::FLAG_CAN_DELETE_ON_CLOSE)); - ASSERT_TRUE(file.IsValid()); - - File file2( - file_path, - (base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE | - base::File::FLAG_SHARE_DELETE | base::File::FLAG_CAN_DELETE_ON_CLOSE)); - ASSERT_TRUE(file2.IsValid()); - - file2.DeleteOnClose(false); - file2.Close(); - ASSERT_TRUE(base::PathExists(file_path)); - file.Close(); - ASSERT_FALSE(base::PathExists(file_path)); -} - -TEST(FileTest, DeleteWithoutPermission) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - - // It should not be possible to mark a file for deletion when it was not - // created/opened with DELETE. - File file(file_path, (base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE)); - ASSERT_TRUE(file.IsValid()); - ASSERT_FALSE(file.DeleteOnClose(true)); - file.Close(); - ASSERT_TRUE(base::PathExists(file_path)); -} - -TEST(FileTest, UnsharedDeleteOnClose) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - - // Opening with DELETE_ON_CLOSE when a previous opener hasn't enabled sharing - // will fail. - File file(file_path, (base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE)); - ASSERT_TRUE(file.IsValid()); - File file2( - file_path, - (base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE | - base::File::FLAG_DELETE_ON_CLOSE | base::File::FLAG_SHARE_DELETE)); - ASSERT_FALSE(file2.IsValid()); - - file.Close(); - ASSERT_TRUE(base::PathExists(file_path)); -} - -TEST(FileTest, NoDeleteOnCloseWithMappedFile) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - - // Mapping a file into memory blocks DeleteOnClose. - File file(file_path, - (base::File::FLAG_CREATE | base::File::FLAG_READ | - base::File::FLAG_WRITE | base::File::FLAG_CAN_DELETE_ON_CLOSE)); - ASSERT_TRUE(file.IsValid()); - ASSERT_EQ(5, file.WriteAtCurrentPos("12345", 5)); - - { - base::MemoryMappedFile mapping; - ASSERT_TRUE(mapping.Initialize(file.Duplicate())); - ASSERT_EQ(5U, mapping.length()); - - EXPECT_FALSE(file.DeleteOnClose(true)); - } - - file.Close(); - ASSERT_TRUE(base::PathExists(file_path)); -} - -// Check that we handle the async bit being set incorrectly in a sane way. -TEST(FileTest, UseSyncApiWithAsyncFile) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("file"); - - File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE | - base::File::FLAG_ASYNC); - File lying_file(file.TakePlatformFile(), false /* async */); - ASSERT_TRUE(lying_file.IsValid()); - - ASSERT_EQ(lying_file.WriteAtCurrentPos("12345", 5), -1); -} -#endif // defined(OS_WIN) diff --git a/files/file_util.cc b/files/file_util.cc deleted file mode 100644 index 109cb229c..000000000 --- a/files/file_util.cc +++ /dev/null @@ -1,288 +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/files/file_util.h" - -#if defined(OS_WIN) -#include -#endif -#include - -#include -#include - -#include "base/files/file_enumerator.h" -#include "base/files/file_path.h" -#include "base/logging.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 "build/build_config.h" - -namespace base { - -#if !defined(OS_NACL_NONSFI) -namespace { - -// The maximum number of 'uniquified' files we will try to create. -// This is used when the filename we're trying to download is already in use, -// so we create a new unique filename by appending " (nnn)" before the -// extension, where 1 <= nnn <= kMaxUniqueFiles. -// Also used by code that cleans up said files. -static const int kMaxUniqueFiles = 100; - -} // namespace - -int64_t ComputeDirectorySize(const FilePath& root_path) { - int64_t running_size = 0; - FileEnumerator file_iter(root_path, true, FileEnumerator::FILES); - while (!file_iter.Next().empty()) - running_size += file_iter.GetInfo().GetSize(); - return running_size; -} - -bool Move(const FilePath& from_path, const FilePath& to_path) { - if (from_path.ReferencesParent() || to_path.ReferencesParent()) - return false; - return internal::MoveUnsafe(from_path, to_path); -} - -bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { - // We open the file in binary format even if they are text files because - // we are just comparing that bytes are exactly same in both files and not - // doing anything smart with text formatting. - std::ifstream file1(filename1.value().c_str(), - std::ios::in | std::ios::binary); - std::ifstream file2(filename2.value().c_str(), - std::ios::in | std::ios::binary); - - // Even if both files aren't openable (and thus, in some sense, "equal"), - // any unusable file yields a result of "false". - if (!file1.is_open() || !file2.is_open()) - return false; - - const int BUFFER_SIZE = 2056; - char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE]; - do { - file1.read(buffer1, BUFFER_SIZE); - file2.read(buffer2, BUFFER_SIZE); - - if ((file1.eof() != file2.eof()) || - (file1.gcount() != file2.gcount()) || - (memcmp(buffer1, buffer2, static_cast(file1.gcount())))) { - file1.close(); - file2.close(); - return false; - } - } while (!file1.eof() || !file2.eof()); - - file1.close(); - file2.close(); - return true; -} - -bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) { - std::ifstream file1(filename1.value().c_str(), std::ios::in); - std::ifstream file2(filename2.value().c_str(), std::ios::in); - - // Even if both files aren't openable (and thus, in some sense, "equal"), - // any unusable file yields a result of "false". - if (!file1.is_open() || !file2.is_open()) - return false; - - do { - std::string line1, line2; - getline(file1, line1); - getline(file2, line2); - - // Check for mismatched EOF states, or any error state. - if ((file1.eof() != file2.eof()) || - file1.bad() || file2.bad()) { - return false; - } - - // Trim all '\r' and '\n' characters from the end of the line. - std::string::size_type end1 = line1.find_last_not_of("\r\n"); - if (end1 == std::string::npos) - line1.clear(); - else if (end1 + 1 < line1.length()) - line1.erase(end1 + 1); - - std::string::size_type end2 = line2.find_last_not_of("\r\n"); - if (end2 == std::string::npos) - line2.clear(); - else if (end2 + 1 < line2.length()) - line2.erase(end2 + 1); - - if (line1 != line2) - return false; - } while (!file1.eof() || !file2.eof()); - - return true; -} -#endif // !defined(OS_NACL_NONSFI) - -bool ReadFileToStringWithMaxSize(const FilePath& path, - std::string* contents, - size_t max_size) { - if (contents) - contents->clear(); - if (path.ReferencesParent()) - return false; - FILE* file = OpenFile(path, "rb"); - if (!file) { - return false; - } - - // Many files supplied in |path| have incorrect size (proc files etc). - // Hence, the file is read sequentially as opposed to a one-shot read, using - // file size as a hint for chunk size if available. - constexpr int64_t kDefaultChunkSize = 1 << 16; - int64_t chunk_size; -#if !defined(OS_NACL_NONSFI) - if (!GetFileSize(path, &chunk_size) || chunk_size <= 0) - chunk_size = kDefaultChunkSize - 1; - // We need to attempt to read at EOF for feof flag to be set so here we - // use |chunk_size| + 1. - chunk_size = std::min(chunk_size, max_size) + 1; -#else - chunk_size = kDefaultChunkSize; -#endif // !defined(OS_NACL_NONSFI) - size_t bytes_read_this_pass; - size_t bytes_read_so_far = 0; - bool read_status = true; - std::string local_contents; - local_contents.resize(chunk_size); - - while ((bytes_read_this_pass = fread(&local_contents[bytes_read_so_far], 1, - chunk_size, file)) > 0) { - if ((max_size - bytes_read_so_far) < bytes_read_this_pass) { - // Read more than max_size bytes, bail out. - bytes_read_so_far = max_size; - read_status = false; - break; - } - // In case EOF was not reached, iterate again but revert to the default - // chunk size. - if (bytes_read_so_far == 0) - chunk_size = kDefaultChunkSize; - - bytes_read_so_far += bytes_read_this_pass; - // Last fread syscall (after EOF) can be avoided via feof, which is just a - // flag check. - if (feof(file)) - break; - local_contents.resize(bytes_read_so_far + chunk_size); - } - read_status = read_status && !ferror(file); - CloseFile(file); - if (contents) { - contents->swap(local_contents); - contents->resize(bytes_read_so_far); - } - - return read_status; -} - -bool ReadFileToString(const FilePath& path, std::string* contents) { - return ReadFileToStringWithMaxSize(path, contents, - std::numeric_limits::max()); -} - -#if !defined(OS_NACL_NONSFI) -bool IsDirectoryEmpty(const FilePath& dir_path) { - FileEnumerator files(dir_path, false, - FileEnumerator::FILES | FileEnumerator::DIRECTORIES); - if (files.Next().empty()) - return true; - return false; -} - -FILE* CreateAndOpenTemporaryFile(FilePath* path) { - FilePath directory; - if (!GetTempDir(&directory)) - return nullptr; - - return CreateAndOpenTemporaryFileInDir(directory, path); -} - -bool CreateDirectory(const FilePath& full_path) { - return CreateDirectoryAndGetError(full_path, nullptr); -} - -bool GetFileSize(const FilePath& file_path, int64_t* file_size) { - File::Info info; - if (!GetFileInfo(file_path, &info)) - return false; - *file_size = info.size; - return true; -} - -bool TouchFile(const FilePath& path, - const Time& last_accessed, - const Time& last_modified) { - 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 |= File::FLAG_BACKUP_SEMANTICS; -#endif // OS_WIN - - File file(path, flags); - if (!file.IsValid()) - return false; - - return file.SetTimes(last_accessed, last_modified); -} -#endif // !defined(OS_NACL_NONSFI) - -bool CloseFile(FILE* file) { - if (file == nullptr) - return true; - return fclose(file) == 0; -} - -#if !defined(OS_NACL_NONSFI) -bool TruncateFile(FILE* file) { - if (file == nullptr) - return false; - long current_offset = ftell(file); - if (current_offset == -1) - return false; -#if defined(OS_WIN) - int fd = _fileno(file); - if (_chsize(fd, current_offset) != 0) - return false; -#else - int fd = fileno(file); - if (ftruncate(fd, current_offset) != 0) - return false; -#endif - return true; -} - -int GetUniquePathNumber(const FilePath& path, - const FilePath::StringType& suffix) { - bool have_suffix = !suffix.empty(); - if (!PathExists(path) && - (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) { - return 0; - } - - FilePath new_path; - for (int count = 1; count <= kMaxUniqueFiles; ++count) { - new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count)); - if (!PathExists(new_path) && - (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) { - return count; - } - } - - return -1; -} -#endif // !defined(OS_NACL_NONSFI) - -} // namespace base diff --git a/files/file_util.h b/files/file_util.h deleted file mode 100644 index 456962a42..000000000 --- a/files/file_util.h +++ /dev/null @@ -1,498 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains utility functions for dealing with the local -// filesystem. - -#ifndef BASE_FILES_FILE_UTIL_H_ -#define BASE_FILES_FILE_UTIL_H_ - -#include -#include -#include - -#include -#include -#include - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#include -#endif - -#include "base/base_export.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/strings/string16.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/win/windows_types.h" -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#include "base/file_descriptor_posix.h" -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#endif - -namespace base { - -class Environment; -class Time; - -//----------------------------------------------------------------------------- -// Functions that involve filesystem access or modification: - -// Returns an absolute version of a relative path. Returns an empty path on -// error. On POSIX, this function fails if the path does not exist. This -// function can result in I/O so it can be slow. -BASE_EXPORT FilePath MakeAbsoluteFilePath(const FilePath& input); - -// Returns the total number of bytes used by all the files under |root_path|. -// If the path does not exist the function returns 0. -// -// This function is implemented using the FileEnumerator class so it is not -// particularly speedy in any platform. -BASE_EXPORT int64_t ComputeDirectorySize(const FilePath& root_path); - -// Deletes the given path, whether it's a file or a directory. -// If it's a directory, it's perfectly happy to delete all of the -// directory's contents. Passing true to recursive deletes -// subdirectories and their contents as well. -// Returns true if successful, false otherwise. It is considered successful -// to attempt to delete a file that does not exist. -// -// In posix environment and if |path| is a symbolic link, this deletes only -// the symlink. (even if the symlink points to a non-existent file) -// -// WARNING: USING THIS WITH recursive==true IS EQUIVALENT -// TO "rm -rf", SO USE WITH CAUTION. -BASE_EXPORT bool DeleteFile(const FilePath& path, bool recursive); - -#if defined(OS_WIN) -// Schedules to delete the given path, whether it's a file or a directory, until -// the operating system is restarted. -// Note: -// 1) The file/directory to be deleted should exist in a temp folder. -// 2) The directory to be deleted must be empty. -BASE_EXPORT bool DeleteFileAfterReboot(const FilePath& path); -#endif - -// Moves the given path, whether it's a file or a directory. -// If a simple rename is not possible, such as in the case where the paths are -// on different volumes, this will attempt to copy and delete. Returns -// true for success. -// This function fails if either path contains traversal components ('..'). -BASE_EXPORT bool Move(const FilePath& from_path, const FilePath& to_path); - -// Renames file |from_path| to |to_path|. Both paths must be on the same -// volume, or the function will fail. Destination file will be created -// if it doesn't exist. Prefer this function over Move when dealing with -// temporary files. On Windows it preserves attributes of the target file. -// Returns true on success, leaving *error unchanged. -// 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, - File::Error* error); - -// Copies a single file. Use CopyDirectory() to copy directories. -// This function fails if either path contains traversal components ('..'). -// This function also fails if |to_path| is a directory. -// -// On POSIX, if |to_path| is a symlink, CopyFile() will follow the symlink. This -// may have security implications. Use with care. -// -// If |to_path| already exists and is a regular file, it will be overwritten, -// though its permissions will stay the same. -// -// If |to_path| does not exist, it will be created. The new file's permissions -// varies per platform: -// -// - This function keeps the metadata on Windows. The read only bit is not kept. -// - On Mac and iOS, |to_path| retains |from_path|'s permissions, except user -// read/write permissions are always set. -// - On Linux and Android, |to_path| has user read/write permissions only. i.e. -// Always 0600. -// - On ChromeOS, |to_path| has user read/write permissions and group/others -// read permissions. i.e. Always 0644. -BASE_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path); - -// Copies the given path, and optionally all subdirectories and their contents -// as well. -// -// 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 has the same metadata behavior as CopyFile(). -// -// 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, - bool recursive); - -// Like CopyDirectory() except trying to overwrite an existing file will not -// work and will return false. -BASE_EXPORT bool CopyDirectoryExcl(const FilePath& from_path, - const FilePath& to_path, - bool recursive); - -// Returns true if the given path exists on the local filesystem, -// false otherwise. -BASE_EXPORT bool PathExists(const FilePath& path); - -// Returns true if the given path is writable by the user, false otherwise. -BASE_EXPORT bool PathIsWritable(const FilePath& path); - -// Returns true if the given path exists and is a directory, false otherwise. -BASE_EXPORT bool DirectoryExists(const FilePath& path); - -// Returns true if the contents of the two files given are equal, false -// otherwise. If either file can't be read, returns false. -BASE_EXPORT bool ContentsEqual(const FilePath& filename1, - const FilePath& filename2); - -// Returns true if the contents of the two text files given are equal, false -// otherwise. This routine treats "\r\n" and "\n" as equivalent. -BASE_EXPORT bool TextContentsEqual(const FilePath& filename1, - const FilePath& filename2); - -// 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 ReadFileToStringWithMaxSize(const FilePath& path, - std::string* contents, - size_t max_size); - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - -// Read exactly |bytes| bytes from file descriptor |fd|, storing the result -// in |buffer|. This function is protected against EINTR and partial reads. -// Returns true iff |bytes| bytes have been successfully read from |fd|. -BASE_EXPORT bool ReadFromFD(int fd, char* buffer, size_t bytes); - -// Performs the same function as CreateAndOpenTemporaryFileInDir(), but returns -// the file-descriptor directly, rather than wrapping it into a FILE. Returns -// -1 on failure. -BASE_EXPORT int CreateAndOpenFdForTemporaryFileInDir(const FilePath& dir, - FilePath* path); - -#endif // OS_POSIX || OS_FUCHSIA - -#if defined(OS_POSIX) - -// Creates a symbolic link at |symlink| pointing to |target|. Returns -// false on failure. -BASE_EXPORT bool CreateSymbolicLink(const FilePath& target, - const FilePath& symlink); - -// Reads the given |symlink| and returns where it points to in |target|. -// Returns false upon failure. -BASE_EXPORT bool ReadSymbolicLink(const FilePath& symlink, FilePath* target); - -// Bits and masks of the file permission. -enum FilePermissionBits { - FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO, - FILE_PERMISSION_USER_MASK = S_IRWXU, - FILE_PERMISSION_GROUP_MASK = S_IRWXG, - FILE_PERMISSION_OTHERS_MASK = S_IRWXO, - - FILE_PERMISSION_READ_BY_USER = S_IRUSR, - FILE_PERMISSION_WRITE_BY_USER = S_IWUSR, - FILE_PERMISSION_EXECUTE_BY_USER = S_IXUSR, - FILE_PERMISSION_READ_BY_GROUP = S_IRGRP, - FILE_PERMISSION_WRITE_BY_GROUP = S_IWGRP, - FILE_PERMISSION_EXECUTE_BY_GROUP = S_IXGRP, - FILE_PERMISSION_READ_BY_OTHERS = S_IROTH, - FILE_PERMISSION_WRITE_BY_OTHERS = S_IWOTH, - FILE_PERMISSION_EXECUTE_BY_OTHERS = S_IXOTH, -}; - -// Reads the permission of the given |path|, storing the file permission -// bits in |mode|. If |path| is symbolic link, |mode| is the permission of -// a file which the symlink points to. -BASE_EXPORT bool GetPosixFilePermissions(const FilePath& path, int* mode); -// Sets the permission of the given |path|. If |path| is symbolic link, sets -// the permission of a file which the symlink points to. -BASE_EXPORT bool SetPosixFilePermissions(const FilePath& path, int mode); - -// Returns true iff |executable| can be found in any directory specified by the -// environment variable in |env|. -BASE_EXPORT bool ExecutableExistsInPath(Environment* env, - const FilePath::StringType& executable); - -#if defined(OS_LINUX) || defined(OS_AIX) -// Determine if files under a given |path| can be mapped and then mprotect'd -// PROT_EXEC. This depends on the mount options used for |path|, which vary -// among different Linux distributions and possibly local configuration. It also -// depends on details of kernel--ChromeOS uses the noexec option for /dev/shm -// but its kernel allows mprotect with PROT_EXEC anyway. -BASE_EXPORT bool IsPathExecutable(const FilePath& path); -#endif // OS_LINUX || OS_AIX - -#endif // OS_POSIX - -// Returns true if the given directory is empty -BASE_EXPORT bool IsDirectoryEmpty(const FilePath& dir_path); - -// Get the temporary directory provided by the system. -// -// WARNING: In general, you should use CreateTemporaryFile variants below -// instead of this function. Those variants will ensure that the proper -// permissions are set so that other users on the system can't edit them while -// they're open (which can lead to security issues). -BASE_EXPORT bool GetTempDir(FilePath* path); - -// Get the home directory. This is more complicated than just getenv("HOME") -// as it knows to fall back on getpwent() etc. -// -// 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(); - -// 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 -// be empty and all handles closed after this function returns. -BASE_EXPORT bool CreateTemporaryFile(FilePath* path); - -// Same as CreateTemporaryFile but the file is created in |dir|. -BASE_EXPORT bool CreateTemporaryFileInDir(const FilePath& dir, - FilePath* temp_file); - -// Create and open a temporary file. File is opened for read/write. -// The full path is placed in |path|. -// Returns a handle to the opened file or NULL if an error occurred. -BASE_EXPORT FILE* CreateAndOpenTemporaryFile(FilePath* path); - -// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|. -BASE_EXPORT FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, - FilePath* path); - -// Create a new directory. If prefix is provided, the new directory name is in -// the format of prefixyyyy. -// NOTE: prefix is ignored in the POSIX implementation. -// If success, return true and output the full path of the directory created. -BASE_EXPORT bool CreateNewTempDirectory(const FilePath::StringType& prefix, - FilePath* new_temp_path); - -// Create a directory within another directory. -// Extra characters will be appended to |prefix| to ensure that the -// new directory does not have the same name as an existing directory. -BASE_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir, - const FilePath::StringType& prefix, - FilePath* new_dir); - -// Creates a directory, as well as creating any parent directories, if they -// don't exist. Returns 'true' on successful creation, or if the directory -// already exists. The directory is only readable by the current user. -// 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, - File::Error* error); - -// Backward-compatible convenience method for the above. -BASE_EXPORT bool CreateDirectory(const FilePath& full_path); - -// Returns the file size. Returns true on success. -BASE_EXPORT bool GetFileSize(const FilePath& file_path, int64_t* file_size); - -// Sets |real_path| to |path| with symbolic links and junctions expanded. -// On windows, make sure the path starts with a lettered drive. -// |path| must reference a file. Function will fail if |path| points to -// a directory or to a nonexistent path. On windows, this function will -// fail if |path| is a junction or symlink that points to an empty file, -// or if |real_path| would be longer than MAX_PATH characters. -BASE_EXPORT bool NormalizeFilePath(const FilePath& path, FilePath* real_path); - -#if defined(OS_WIN) - -// Given a path in NT native form ("\Device\HarddiskVolumeXX\..."), -// return in |drive_letter_path| the equivalent path that starts with -// a drive letter ("C:\..."). Return false if no such path exists. -BASE_EXPORT bool DevicePathToDriveLetterPath(const FilePath& device_path, - FilePath* drive_letter_path); - -// Given an existing file in |path|, set |real_path| to the path -// in native NT format, of the form "\Device\HarddiskVolumeXX\..". -// Returns false if the path can not be found. Empty files cannot -// be resolved with this function. -BASE_EXPORT bool NormalizeToNativeFilePath(const FilePath& path, - FilePath* nt_path); -#endif - -// This function will return if the given file is a symlink or not. -BASE_EXPORT bool IsLink(const FilePath& file_path); - -// Returns information about the given file path. -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, - const Time& last_accessed, - const Time& last_modified); - -// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. The -// underlying file descriptor (POSIX) or handle (Windows) is unconditionally -// configured to not be propagated to child processes. -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 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 FilePath& filename, const char* data, - int size); - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -// Appends |data| to |fd|. Does not close |fd| when done. Returns true iff -// |size| bytes of |data| were written to |fd|. -BASE_EXPORT bool WriteFileDescriptor(const int fd, const char* data, int size); -#endif - -// Appends |data| to |filename|. Returns true iff |size| bytes of |data| were -// written to |filename|. -BASE_EXPORT bool AppendToFile(const FilePath& filename, - const char* data, - int size); - -// Gets the current working directory for the process. -BASE_EXPORT bool GetCurrentDirectory(FilePath* path); - -// Sets the current working directory for the process. -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 FilePath& path, - const FilePath::StringType& suffix); - -// Sets the given |fd| to non-blocking mode. -// Returns true if it was able to set it in the non-blocking mode, otherwise -// false. -BASE_EXPORT bool SetNonBlocking(int fd); - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -// Creates a non-blocking, close-on-exec pipe. -// This creates a non-blocking pipe that is not intended to be shared with any -// child process. This will be done atomically if the operating system supports -// it. Returns true if it was able to create the pipe, otherwise false. -BASE_EXPORT bool CreateLocalNonBlockingPipe(int fds[2]); - -// Sets the given |fd| to close-on-exec mode. -// Returns true if it was able to set it in the close-on-exec mode, otherwise -// false. -BASE_EXPORT bool SetCloseOnExec(int fd); - -// Test that |path| can only be changed by a given user and members of -// a given set of groups. -// Specifically, test that all parts of |path| under (and including) |base|: -// * Exist. -// * Are owned by a specific user. -// * Are not writable by all users. -// * Are owned by a member of a given set of groups, or are not writable by -// their group. -// * Are not symbolic links. -// This is useful for checking that a config file is administrator-controlled. -// |base| must contain |path|. -BASE_EXPORT bool VerifyPathControlledByUser(const base::FilePath& base, - const base::FilePath& path, - uid_t owner_uid, - const std::set& group_gids); -#endif // defined(OS_POSIX) || defined(OS_FUCHSIA) - -#if defined(OS_MACOSX) && !defined(OS_IOS) -// Is |path| writable only by a user with administrator privileges? -// This function uses Mac OS conventions. The super user is assumed to have -// uid 0, and the administrator group is assumed to be named "admin". -// Testing that |path|, and every parent directory including the root of -// the filesystem, are owned by the superuser, controlled by the group -// "admin", are not writable by all users, and contain no symbolic links. -// Will return false if |path| does not exist. -BASE_EXPORT bool VerifyPathControlledByAdmin(const base::FilePath& path); -#endif // defined(OS_MACOSX) && !defined(OS_IOS) - -// Returns the maximum length of path component on the volume containing -// the directory |path|, in the number of FilePath::CharType, or -1 on failure. -BASE_EXPORT int GetMaximumPathComponentLength(const base::FilePath& path); - -#if defined(OS_LINUX) || defined(OS_AIX) -// Broad categories of file systems as returned by statfs() on Linux. -enum FileSystemType { - FILE_SYSTEM_UNKNOWN, // statfs failed. - FILE_SYSTEM_0, // statfs.f_type == 0 means unknown, may indicate AFS. - FILE_SYSTEM_ORDINARY, // on-disk filesystem like ext2 - FILE_SYSTEM_NFS, - FILE_SYSTEM_SMB, - FILE_SYSTEM_CODA, - FILE_SYSTEM_MEMORY, // in-memory file system - FILE_SYSTEM_CGROUP, // cgroup control. - FILE_SYSTEM_OTHER, // any other value. - FILE_SYSTEM_TYPE_COUNT -}; - -// Attempts determine the FileSystemType for |path|. -// Returns false if |path| doesn't exist. -BASE_EXPORT bool GetFileSystemType(const FilePath& path, FileSystemType* type); -#endif - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -// 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 - -// Internal -------------------------------------------------------------------- - -namespace internal { - -// Same as Move but allows paths with traversal components. -// Use only with extreme care. -BASE_EXPORT bool MoveUnsafe(const FilePath& from_path, - const FilePath& to_path); - -#if defined(OS_WIN) -// Copy from_path to to_path recursively and then delete from_path recursively. -// Returns true if all operations succeed. -// This function simulates Move(), but unlike Move() it works across volumes. -// This function is not transactional. -BASE_EXPORT bool CopyAndDeleteDirectory(const FilePath& from_path, - const FilePath& to_path); -#endif // defined(OS_WIN) - -} // namespace internal -} // namespace base - -#endif // BASE_FILES_FILE_UTIL_H_ diff --git a/files/file_util_android.cc b/files/file_util_android.cc deleted file mode 100644 index b8b3b3720..000000000 --- a/files/file_util_android.cc +++ /dev/null @@ -1,16 +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/files/file_util.h" - -#include "base/files/file_path.h" -#include "base/path_service.h" - -namespace base { - -bool GetShmemTempDir(bool executable, base::FilePath* path) { - return PathService::Get(base::DIR_CACHE, path); -} - -} // namespace base diff --git a/files/file_util_linux.cc b/files/file_util_linux.cc deleted file mode 100644 index b230fd964..000000000 --- a/files/file_util_linux.cc +++ /dev/null @@ -1,63 +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/files/file_util.h" - -#include -#include -#include - -#include "base/files/file_path.h" - -namespace base { - -bool GetFileSystemType(const FilePath& path, FileSystemType* type) { - struct statfs statfs_buf; - if (statfs(path.value().c_str(), &statfs_buf) < 0) { - if (errno == ENOENT) - return false; - *type = FILE_SYSTEM_UNKNOWN; - return true; - } - - // 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 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 0x3153464A: // JFS - *type = FILE_SYSTEM_ORDINARY; - break; - case NFS_SUPER_MAGIC: - *type = FILE_SYSTEM_NFS; - break; - case SMB_SUPER_MAGIC: - case 0xFF534D42: // CIFS - *type = FILE_SYSTEM_SMB; - break; - case CODA_SUPER_MAGIC: - *type = FILE_SYSTEM_CODA; - break; - case HUGETLBFS_MAGIC: - case RAMFS_MAGIC: - case TMPFS_MAGIC: - *type = FILE_SYSTEM_MEMORY; - break; - case CGROUP_SUPER_MAGIC: - *type = FILE_SYSTEM_CGROUP; - break; - default: - *type = FILE_SYSTEM_OTHER; - } - return true; -} - -} // namespace base diff --git a/files/file_util_mac.mm b/files/file_util_mac.mm deleted file mode 100644 index 392fbcef3..000000000 --- a/files/file_util_mac.mm +++ /dev/null @@ -1,66 +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/files/file_util.h" - -#import -#include -#include -#include - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/mac/foundation_util.h" -#include "base/strings/string_util.h" -#include "base/threading/thread_restrictions.h" - -namespace base { - -bool CopyFile(const FilePath& from_path, const FilePath& to_path) { - AssertBlockingAllowed(); - if (from_path.ReferencesParent() || to_path.ReferencesParent()) - return false; - return (copyfile(from_path.value().c_str(), - to_path.value().c_str(), NULL, COPYFILE_DATA) == 0); -} - -bool GetTempDir(base::FilePath* path) { - // In order to facilitate hermetic runs on macOS, first check - // $MAC_CHROMIUM_TMPDIR. We check this instead of $TMPDIR because external - // programs currently set $TMPDIR with no effect, but when we respect it - // directly it can cause crashes (like crbug.com/698759). - const char* env_tmpdir = getenv("MAC_CHROMIUM_TMPDIR"); - if (env_tmpdir) { - DCHECK_LT(strlen(env_tmpdir), 50u) - << "too-long TMPDIR causes socket name length issues."; - *path = base::FilePath(env_tmpdir); - return true; - } - - // If we didn't find it, fall back to the native function. - NSString* tmp = NSTemporaryDirectory(); - if (tmp == nil) - return false; - *path = base::mac::NSStringToFilePath(tmp); - return true; -} - -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/files/file_util_posix.cc b/files/file_util_posix.cc deleted file mode 100644 index 7049faef8..000000000 --- a/files/file_util_posix.cc +++ /dev/null @@ -1,1082 +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/files/file_util.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/containers/stack.h" -#include "base/environment.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/macros.h" -#include "base/memory/singleton.h" -#include "base/path_service.h" -#include "base/posix/eintr_wrapper.h" -#include "base/stl_util.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "base/sys_info.h" -#include "base/threading/thread_restrictions.h" -#include "base/time/time.h" -#include "build/build_config.h" - -#if defined(OS_MACOSX) -#include -#include "base/mac/foundation_util.h" -#endif - -#if defined(OS_ANDROID) -#include "base/android/content_uri_utils.h" -#include "base/os_compat_android.h" -#endif - -#if !defined(OS_IOS) -#include -#endif - -// We need to do this on AIX due to some inconsistencies in how AIX -// handles XOPEN_SOURCE and ALL_SOURCE. -#if defined(OS_AIX) -extern "C" char* mkdtemp(char* path); -#endif - -namespace base { - -namespace { - -#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \ - defined(OS_ANDROID) && __ANDROID_API__ < 21 -int CallStat(const char* path, stat_wrapper_t* sb) { - AssertBlockingAllowed(); - return stat(path, sb); -} -int CallLstat(const char* path, stat_wrapper_t* sb) { - AssertBlockingAllowed(); - return lstat(path, sb); -} -#else -int CallStat(const char* path, stat_wrapper_t* sb) { - AssertBlockingAllowed(); - return stat64(path, sb); -} -int CallLstat(const char* path, stat_wrapper_t* sb) { - AssertBlockingAllowed(); - return lstat64(path, sb); -} -#endif - -#if !defined(OS_NACL_NONSFI) -// Helper for VerifyPathControlledByUser. -bool VerifySpecificPathControlledByUser(const FilePath& path, - uid_t owner_uid, - const std::set& group_gids) { - stat_wrapper_t stat_info; - if (CallLstat(path.value().c_str(), &stat_info) != 0) { - DPLOG(ERROR) << "Failed to get information on path " - << path.value(); - return false; - } - - if (S_ISLNK(stat_info.st_mode)) { - DLOG(ERROR) << "Path " << path.value() << " is a symbolic link."; - return false; - } - - if (stat_info.st_uid != owner_uid) { - DLOG(ERROR) << "Path " << path.value() << " is owned by the wrong user."; - return false; - } - - if ((stat_info.st_mode & S_IWGRP) && - !ContainsKey(group_gids, stat_info.st_gid)) { - DLOG(ERROR) << "Path " << path.value() - << " is writable by an unprivileged group."; - return false; - } - - if (stat_info.st_mode & S_IWOTH) { - DLOG(ERROR) << "Path " << path.value() << " is writable by any user."; - return false; - } - - return true; -} - -std::string TempFileName() { -#if defined(OS_MACOSX) - return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID()); -#endif - -#if defined(GOOGLE_CHROME_BUILD) - return std::string(".com.google.Chrome.XXXXXX"); -#else - return std::string(".org.chromium.Chromium.XXXXXX"); -#endif -} - -bool AdvanceEnumeratorWithStat(FileEnumerator* traversal, - FilePath* out_next_path, - struct stat* out_next_stat) { - DCHECK(out_next_path); - DCHECK(out_next_stat); - *out_next_path = traversal->Next(); - if (out_next_path->empty()) - return false; - - *out_next_stat = traversal->GetInfo().stat(); - return true; -} - -bool CopyFileContents(File* infile, File* outfile) { - static constexpr size_t kBufferSize = 32768; - std::vector buffer(kBufferSize); - - for (;;) { - ssize_t bytes_read = infile->ReadAtCurrentPos(buffer.data(), buffer.size()); - if (bytes_read < 0) - return false; - if (bytes_read == 0) - return true; - // Allow for partial writes - ssize_t bytes_written_per_read = 0; - do { - ssize_t bytes_written_partial = outfile->WriteAtCurrentPos( - &buffer[bytes_written_per_read], bytes_read - bytes_written_per_read); - if (bytes_written_partial < 0) - return false; - - bytes_written_per_read += bytes_written_partial; - } while (bytes_written_per_read < bytes_read); - } - - NOTREACHED(); - return false; -} - -bool DoCopyDirectory(const FilePath& from_path, - const FilePath& to_path, - bool recursive, - bool open_exclusive) { - AssertBlockingAllowed(); - // 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. - DCHECK(to_path.value().find('*') == std::string::npos); - DCHECK(from_path.value().find('*') == std::string::npos); - - if (from_path.value().size() >= PATH_MAX) { - 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); - 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 == real_from_path || real_from_path.IsParent(real_to_path)) - return false; - - int traverse_type = FileEnumerator::FILES | FileEnumerator::SHOW_SYM_LINKS; - if (recursive) - traverse_type |= FileEnumerator::DIRECTORIES; - FileEnumerator traversal(from_path, recursive, traverse_type); - - // We have to mimic windows behavior here. |to_path| may not exist yet, - // start the loop with |to_path|. - struct stat from_stat; - FilePath current = from_path; - if (stat(from_path.value().c_str(), &from_stat) < 0) { - DPLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " - << from_path.value(); - return false; - } - 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 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)); - - do { - // 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 && - !from_path_base.AppendRelativePath(current, &target_path)) { - return false; - } - - if (S_ISDIR(from_stat.st_mode)) { - mode_t mode = (from_stat.st_mode & 01777) | S_IRUSR | S_IXUSR | S_IWUSR; - if (mkdir(target_path.value().c_str(), mode) == 0) - continue; - if (errno == EEXIST && !open_exclusive) - continue; - - DPLOG(ERROR) << "CopyDirectory() couldn't create directory: " - << target_path.value(); - return false; - } - - if (!S_ISREG(from_stat.st_mode)) { - DLOG(WARNING) << "CopyDirectory() skipping non-regular file: " - << current.value(); - continue; - } - - // Add O_NONBLOCK so we can't block opening a pipe. - File infile(open(current.value().c_str(), O_RDONLY | O_NONBLOCK)); - if (!infile.IsValid()) { - DPLOG(ERROR) << "CopyDirectory() couldn't open file: " << current.value(); - return false; - } - - struct stat stat_at_use; - if (fstat(infile.GetPlatformFile(), &stat_at_use) < 0) { - DPLOG(ERROR) << "CopyDirectory() couldn't stat file: " << current.value(); - return false; - } - - if (!S_ISREG(stat_at_use.st_mode)) { - DLOG(WARNING) << "CopyDirectory() skipping non-regular file: " - << current.value(); - continue; - } - - int open_flags = O_WRONLY | O_CREAT; - // If |open_exclusive| is set then we should always create the destination - // file, so O_NONBLOCK is not necessary to ensure we don't block on the - // open call for the target file below, and since the destination will - // always be a regular file it wouldn't affect the behavior of the - // subsequent write calls anyway. - if (open_exclusive) - open_flags |= O_EXCL; - else - open_flags |= O_TRUNC | O_NONBLOCK; - // Each platform has different default file opening modes for CopyFile which - // we want to replicate here. On OS X, we use copyfile(3) which takes the - // source file's permissions into account. On the other platforms, we just - // use the base::File constructor. On Chrome OS, base::File uses a different - // set of permissions than it does on other POSIX platforms. -#if defined(OS_MACOSX) - int mode = 0600 | (stat_at_use.st_mode & 0177); -#elif defined(OS_CHROMEOS) - int mode = 0644; -#else - int mode = 0600; -#endif - File outfile(open(target_path.value().c_str(), open_flags, mode)); - if (!outfile.IsValid()) { - DPLOG(ERROR) << "CopyDirectory() couldn't create file: " - << target_path.value(); - return false; - } - - if (!CopyFileContents(&infile, &outfile)) { - DLOG(ERROR) << "CopyDirectory() couldn't copy file: " << current.value(); - return false; - } - } while (AdvanceEnumeratorWithStat(&traversal, ¤t, &from_stat)); - - return true; -} -#endif // !defined(OS_NACL_NONSFI) - -#if !defined(OS_MACOSX) -// Appends |mode_char| to |mode| before the optional character set encoding; see -// https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for -// details. -std::string AppendModeCharacter(StringPiece mode, char mode_char) { - std::string result(mode.as_string()); - size_t comma_pos = result.find(','); - result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1, - mode_char); - return result; -} -#endif - -} // namespace - -#if !defined(OS_NACL_NONSFI) -FilePath MakeAbsoluteFilePath(const FilePath& input) { - AssertBlockingAllowed(); - char full_path[PATH_MAX]; - if (realpath(input.value().c_str(), full_path) == nullptr) - return FilePath(); - return FilePath(full_path); -} - -// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*" -// which works both with and without the recursive flag. I'm not sure we need -// that functionality. If not, remove from file_util_win.cc, otherwise add it -// here. -bool DeleteFile(const FilePath& path, bool recursive) { - AssertBlockingAllowed(); - const char* path_str = path.value().c_str(); - stat_wrapper_t file_info; - if (CallLstat(path_str, &file_info) != 0) { - // The Windows version defines this condition as success. - return (errno == ENOENT || errno == ENOTDIR); - } - if (!S_ISDIR(file_info.st_mode)) - return (unlink(path_str) == 0); - if (!recursive) - return (rmdir(path_str) == 0); - - bool success = true; - stack directories; - directories.push(path.value()); - FileEnumerator traversal(path, true, - FileEnumerator::FILES | FileEnumerator::DIRECTORIES | - FileEnumerator::SHOW_SYM_LINKS); - for (FilePath current = traversal.Next(); !current.empty(); - current = traversal.Next()) { - if (traversal.GetInfo().IsDirectory()) - directories.push(current.value()); - else - success &= (unlink(current.value().c_str()) == 0); - } - - while (!directories.empty()) { - FilePath dir = FilePath(directories.top()); - directories.pop(); - success &= (rmdir(dir.value().c_str()) == 0); - } - return success; -} - -bool ReplaceFile(const FilePath& from_path, - const FilePath& to_path, - File::Error* error) { - AssertBlockingAllowed(); - if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) - return true; - if (error) - *error = File::GetLastFileError(); - return false; -} - -bool CopyDirectory(const FilePath& from_path, - const FilePath& to_path, - bool recursive) { - return DoCopyDirectory(from_path, to_path, recursive, false); -} - -bool CopyDirectoryExcl(const FilePath& from_path, - const FilePath& to_path, - bool recursive) { - return DoCopyDirectory(from_path, to_path, recursive, true); -} -#endif // !defined(OS_NACL_NONSFI) - -bool CreateLocalNonBlockingPipe(int fds[2]) { -#if defined(OS_LINUX) - return pipe2(fds, O_CLOEXEC | O_NONBLOCK) == 0; -#else - int raw_fds[2]; - if (pipe(raw_fds) != 0) - return false; - ScopedFD fd_out(raw_fds[0]); - ScopedFD fd_in(raw_fds[1]); - if (!SetCloseOnExec(fd_out.get())) - return false; - if (!SetCloseOnExec(fd_in.get())) - return false; - if (!SetNonBlocking(fd_out.get())) - return false; - if (!SetNonBlocking(fd_in.get())) - return false; - fds[0] = fd_out.release(); - fds[1] = fd_in.release(); - return true; -#endif -} - -bool SetNonBlocking(int fd) { - const int flags = fcntl(fd, F_GETFL); - if (flags == -1) - return false; - if (flags & O_NONBLOCK) - return true; - if (HANDLE_EINTR(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1) - return false; - return true; -} - -bool SetCloseOnExec(int fd) { -#if defined(OS_NACL_NONSFI) - const int flags = 0; -#else - const int flags = fcntl(fd, F_GETFD); - if (flags == -1) - return false; - if (flags & FD_CLOEXEC) - return true; -#endif // defined(OS_NACL_NONSFI) - if (HANDLE_EINTR(fcntl(fd, F_SETFD, flags | FD_CLOEXEC)) == -1) - return false; - return true; -} - -bool PathExists(const FilePath& path) { - AssertBlockingAllowed(); -#if defined(OS_ANDROID) - if (path.IsContentUri()) { - return ContentUriExists(path); - } -#endif - return access(path.value().c_str(), F_OK) == 0; -} - -#if !defined(OS_NACL_NONSFI) -bool PathIsWritable(const FilePath& path) { - AssertBlockingAllowed(); - return access(path.value().c_str(), W_OK) == 0; -} -#endif // !defined(OS_NACL_NONSFI) - -bool DirectoryExists(const FilePath& path) { - AssertBlockingAllowed(); - stat_wrapper_t file_info; - if (CallStat(path.value().c_str(), &file_info) != 0) - return false; - return S_ISDIR(file_info.st_mode); -} - -bool ReadFromFD(int fd, char* buffer, size_t bytes) { - size_t total_read = 0; - while (total_read < bytes) { - ssize_t bytes_read = - HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); - if (bytes_read <= 0) - break; - total_read += bytes_read; - } - return total_read == bytes; -} - -#if !defined(OS_NACL_NONSFI) - -int CreateAndOpenFdForTemporaryFileInDir(const FilePath& directory, - FilePath* path) { - AssertBlockingAllowed(); // For call to mkstemp(). - *path = directory.Append(TempFileName()); - const std::string& tmpdir_string = path->value(); - // this should be OK since mkstemp just replaces characters in place - char* buffer = const_cast(tmpdir_string.c_str()); - - return HANDLE_EINTR(mkstemp(buffer)); -} - -#if !defined(OS_FUCHSIA) -bool CreateSymbolicLink(const FilePath& target_path, - const FilePath& symlink_path) { - DCHECK(!symlink_path.empty()); - DCHECK(!target_path.empty()); - return ::symlink(target_path.value().c_str(), - symlink_path.value().c_str()) != -1; -} - -bool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) { - DCHECK(!symlink_path.empty()); - DCHECK(target_path); - char buf[PATH_MAX]; - ssize_t count = ::readlink(symlink_path.value().c_str(), buf, arraysize(buf)); - - if (count <= 0) { - target_path->clear(); - return false; - } - - *target_path = FilePath(FilePath::StringType(buf, count)); - return true; -} - -bool GetPosixFilePermissions(const FilePath& path, int* mode) { - AssertBlockingAllowed(); - DCHECK(mode); - - stat_wrapper_t file_info; - // Uses stat(), because on symbolic link, lstat() does not return valid - // permission bits in st_mode - if (CallStat(path.value().c_str(), &file_info) != 0) - return false; - - *mode = file_info.st_mode & FILE_PERMISSION_MASK; - return true; -} - -bool SetPosixFilePermissions(const FilePath& path, - int mode) { - AssertBlockingAllowed(); - DCHECK_EQ(mode & ~FILE_PERMISSION_MASK, 0); - - // Calls stat() so that we can preserve the higher bits like S_ISGID. - stat_wrapper_t stat_buf; - if (CallStat(path.value().c_str(), &stat_buf) != 0) - return false; - - // Clears the existing permission bits, and adds the new ones. - mode_t updated_mode_bits = stat_buf.st_mode & ~FILE_PERMISSION_MASK; - updated_mode_bits |= mode & FILE_PERMISSION_MASK; - - if (HANDLE_EINTR(chmod(path.value().c_str(), updated_mode_bits)) != 0) - return false; - - return true; -} - -bool ExecutableExistsInPath(Environment* env, - const FilePath::StringType& executable) { - std::string path; - if (!env->GetVar("PATH", &path)) { - LOG(ERROR) << "No $PATH variable. Assuming no " << executable << "."; - return false; - } - - for (const StringPiece& cur_path : - SplitStringPiece(path, ":", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY)) { - FilePath file(cur_path); - int permissions; - if (GetPosixFilePermissions(file.Append(executable), &permissions) && - (permissions & FILE_PERMISSION_EXECUTE_BY_USER)) - return true; - } - return false; -} - -#endif // !OS_FUCHSIA - -#if !defined(OS_MACOSX) -// This is implemented in file_util_mac.mm for Mac. -bool GetTempDir(FilePath* path) { - const char* tmp = getenv("TMPDIR"); - if (tmp) { - *path = FilePath(tmp); - return true; - } - -#if defined(OS_ANDROID) - return PathService::Get(DIR_CACHE, path); -#else - *path = FilePath("/tmp"); - return true; -#endif -} -#endif // !defined(OS_MACOSX) - -#if !defined(OS_MACOSX) // Mac implementation is in file_util_mac.mm. -FilePath GetHomeDir() { -#if defined(OS_CHROMEOS) - 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"); - if (home_dir && home_dir[0]) - return FilePath(home_dir); - -#if defined(OS_ANDROID) - DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented."; -#endif - - FilePath rv; - if (GetTempDir(&rv)) - return rv; - - // Last resort. - return FilePath("/tmp"); -} -#endif // !defined(OS_MACOSX) - -bool CreateTemporaryFile(FilePath* path) { - AssertBlockingAllowed(); // For call to close(). - FilePath directory; - if (!GetTempDir(&directory)) - return false; - int fd = CreateAndOpenFdForTemporaryFileInDir(directory, path); - if (fd < 0) - return false; - close(fd); - return true; -} - -FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { - int fd = CreateAndOpenFdForTemporaryFileInDir(dir, path); - if (fd < 0) - return nullptr; - - FILE* file = fdopen(fd, "a+"); - if (!file) - close(fd); - return file; -} - -bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { - AssertBlockingAllowed(); // For call to close(). - int fd = CreateAndOpenFdForTemporaryFileInDir(dir, temp_file); - return ((fd >= 0) && !IGNORE_EINTR(close(fd))); -} - -static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir, - const FilePath::StringType& name_tmpl, - FilePath* new_dir) { - AssertBlockingAllowed(); // For call to mkdtemp(). - DCHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos) - << "Directory name template must contain \"XXXXXX\"."; - - FilePath sub_dir = base_dir.Append(name_tmpl); - std::string sub_dir_string = sub_dir.value(); - - // this should be OK since mkdtemp just replaces characters in place - char* buffer = const_cast(sub_dir_string.c_str()); - char* dtemp = mkdtemp(buffer); - if (!dtemp) { - DPLOG(ERROR) << "mkdtemp"; - return false; - } - *new_dir = FilePath(dtemp); - return true; -} - -bool CreateTemporaryDirInDir(const FilePath& base_dir, - const FilePath::StringType& prefix, - FilePath* new_dir) { - FilePath::StringType mkdtemp_template = prefix; - mkdtemp_template.append(FILE_PATH_LITERAL("XXXXXX")); - return CreateTemporaryDirInDirImpl(base_dir, mkdtemp_template, new_dir); -} - -bool CreateNewTempDirectory(const FilePath::StringType& prefix, - FilePath* new_temp_path) { - FilePath tmpdir; - if (!GetTempDir(&tmpdir)) - return false; - - return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path); -} - -bool CreateDirectoryAndGetError(const FilePath& full_path, - File::Error* error) { - AssertBlockingAllowed(); // For call to mkdir(). - std::vector subpaths; - - // Collect a list of all parent directories. - FilePath last_path = full_path; - subpaths.push_back(full_path); - for (FilePath path = full_path.DirName(); - path.value() != last_path.value(); path = path.DirName()) { - subpaths.push_back(path); - last_path = path; - } - - // Iterate through the parents and create the missing ones. - for (std::vector::reverse_iterator i = subpaths.rbegin(); - i != subpaths.rend(); ++i) { - if (DirectoryExists(*i)) - continue; - if (mkdir(i->value().c_str(), 0700) == 0) - continue; - // Mkdir failed, but it might have failed with EEXIST, or some other error - // due to the the directory appearing out of thin air. This can occur if - // two processes are trying to create the same file system tree at the same - // time. Check to see if it exists and make sure it is a directory. - int saved_errno = errno; - if (!DirectoryExists(*i)) { - if (error) - *error = File::OSErrorToFileError(saved_errno); - return false; - } - } - return true; -} - -bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) { - FilePath real_path_result = MakeAbsoluteFilePath(path); - if (real_path_result.empty()) - return false; - - // To be consistant with windows, fail if |real_path_result| is a - // directory. - if (DirectoryExists(real_path_result)) - return false; - - *normalized_path = real_path_result; - return true; -} - -// TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks -// correctly. http://code.google.com/p/chromium-os/issues/detail?id=15948 -bool IsLink(const FilePath& file_path) { - stat_wrapper_t st; - // If we can't lstat the file, it's safe to assume that the file won't at - // least be a 'followable' link. - if (CallLstat(file_path.value().c_str(), &st) != 0) - return false; - return S_ISLNK(st.st_mode); -} - -bool GetFileInfo(const FilePath& file_path, File::Info* results) { - stat_wrapper_t file_info; -#if defined(OS_ANDROID) - if (file_path.IsContentUri()) { - 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) - return false; -#if defined(OS_ANDROID) - } -#endif // defined(OS_ANDROID) - - results->FromStat(file_info); - return true; -} -#endif // !defined(OS_NACL_NONSFI) - -FILE* OpenFile(const FilePath& filename, const char* mode) { - // 'e' is unconditionally added below, so be sure there is not one already - // present before a comma in |mode|. - DCHECK( - strchr(mode, 'e') == nullptr || - (strchr(mode, ',') != nullptr && strchr(mode, 'e') > strchr(mode, ','))); - AssertBlockingAllowed(); - FILE* result = nullptr; -#if defined(OS_MACOSX) - // macOS does not provide a mode character to set O_CLOEXEC; see - // https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/fopen.3.html. - const char* the_mode = mode; -#else - std::string mode_with_e(AppendModeCharacter(mode, 'e')); - const char* the_mode = mode_with_e.c_str(); -#endif - do { - result = fopen(filename.value().c_str(), the_mode); - } while (!result && errno == EINTR); -#if defined(OS_MACOSX) - // Mark the descriptor as close-on-exec. - if (result) - SetCloseOnExec(fileno(result)); -#endif - return result; -} - -// 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) { - AssertBlockingAllowed(); - 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, max_size)); - if (IGNORE_EINTR(close(fd)) < 0) - return -1; - return bytes_read; -} - -int WriteFile(const FilePath& filename, const char* data, int size) { - AssertBlockingAllowed(); - int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666)); - if (fd < 0) - return -1; - - int bytes_written = WriteFileDescriptor(fd, data, size) ? size : -1; - if (IGNORE_EINTR(close(fd)) < 0) - return -1; - return bytes_written; -} - -bool WriteFileDescriptor(const int fd, const char* data, int size) { - // Allow for partial writes. - ssize_t bytes_written_total = 0; - for (ssize_t bytes_written_partial = 0; bytes_written_total < size; - bytes_written_total += bytes_written_partial) { - bytes_written_partial = - HANDLE_EINTR(write(fd, data + bytes_written_total, - size - bytes_written_total)); - if (bytes_written_partial < 0) - return false; - } - - return true; -} - -#if !defined(OS_NACL_NONSFI) - -bool AppendToFile(const FilePath& filename, const char* data, int size) { - AssertBlockingAllowed(); - bool ret = true; - int fd = HANDLE_EINTR(open(filename.value().c_str(), O_WRONLY | O_APPEND)); - if (fd < 0) { - VPLOG(1) << "Unable to create file " << filename.value(); - return false; - } - - // This call will either write all of the data or return false. - if (!WriteFileDescriptor(fd, data, size)) { - VPLOG(1) << "Error while writing to file " << filename.value(); - ret = false; - } - - if (IGNORE_EINTR(close(fd)) < 0) { - VPLOG(1) << "Error while closing file " << filename.value(); - return false; - } - - return ret; -} - -bool GetCurrentDirectory(FilePath* dir) { - // getcwd can return ENOENT, which implies it checks against the disk. - AssertBlockingAllowed(); - - char system_buffer[PATH_MAX] = ""; - if (!getcwd(system_buffer, sizeof(system_buffer))) { - NOTREACHED(); - return false; - } - *dir = FilePath(system_buffer); - return true; -} - -bool SetCurrentDirectory(const FilePath& path) { - AssertBlockingAllowed(); - return chdir(path.value().c_str()) == 0; -} - -bool VerifyPathControlledByUser(const FilePath& base, - const FilePath& path, - uid_t owner_uid, - const std::set& group_gids) { - if (base != path && !base.IsParent(path)) { - DLOG(ERROR) << "|base| must be a subdirectory of |path|. base = \"" - << base.value() << "\", path = \"" << path.value() << "\""; - return false; - } - - std::vector base_components; - std::vector path_components; - - base.GetComponents(&base_components); - path.GetComponents(&path_components); - - std::vector::const_iterator ib, ip; - for (ib = base_components.begin(), ip = path_components.begin(); - ib != base_components.end(); ++ib, ++ip) { - // |base| must be a subpath of |path|, so all components should match. - // If these CHECKs fail, look at the test that base is a parent of - // path at the top of this function. - DCHECK(ip != path_components.end()); - DCHECK(*ip == *ib); - } - - FilePath current_path = base; - if (!VerifySpecificPathControlledByUser(current_path, owner_uid, group_gids)) - return false; - - for (; ip != path_components.end(); ++ip) { - current_path = current_path.Append(*ip); - if (!VerifySpecificPathControlledByUser( - current_path, owner_uid, group_gids)) - return false; - } - return true; -} - -#if defined(OS_MACOSX) && !defined(OS_IOS) -bool VerifyPathControlledByAdmin(const FilePath& path) { - const unsigned kRootUid = 0; - const FilePath kFileSystemRoot("/"); - - // The name of the administrator group on mac os. - const char* const kAdminGroupNames[] = { - "admin", - "wheel" - }; - - // Reading the groups database may touch the file system. - AssertBlockingAllowed(); - - std::set allowed_group_ids; - for (int i = 0, ie = arraysize(kAdminGroupNames); i < ie; ++i) { - struct group *group_record = getgrnam(kAdminGroupNames[i]); - if (!group_record) { - DPLOG(ERROR) << "Could not get the group ID of group \"" - << kAdminGroupNames[i] << "\"."; - continue; - } - - allowed_group_ids.insert(group_record->gr_gid); - } - - return VerifyPathControlledByUser( - kFileSystemRoot, path, kRootUid, allowed_group_ids); -} -#endif // defined(OS_MACOSX) && !defined(OS_IOS) - -int GetMaximumPathComponentLength(const FilePath& path) { -#if defined(OS_FUCHSIA) - // Return a value we do not expect anyone ever to reach, but which is small - // enough to guard against e.g. bugs causing multi-megabyte paths. - return 1024; -#else - AssertBlockingAllowed(); - return pathconf(path.value().c_str(), _PC_NAME_MAX); -#endif -} - -#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) || defined(OS_AIX) - bool disable_dev_shm = false; -#if !defined(OS_CHROMEOS) - disable_dev_shm = CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableDevShmUsage); -#endif - bool use_dev_shm = true; - if (executable) { - static const bool s_dev_shm_executable = - IsPathExecutable(FilePath("/dev/shm")); - use_dev_shm = s_dev_shm_executable; - } - if (use_dev_shm && !disable_dev_shm) { - *path = FilePath("/dev/shm"); - return true; - } -#endif // defined(OS_LINUX) || defined(OS_AIX) - return GetTempDir(path); -} -#endif // !defined(OS_ANDROID) - -#if !defined(OS_MACOSX) -// Mac has its own implementation, this is for all other Posix systems. -bool CopyFile(const FilePath& from_path, const FilePath& to_path) { - AssertBlockingAllowed(); - File infile; -#if defined(OS_ANDROID) - if (from_path.IsContentUri()) { - infile = OpenContentUriForRead(from_path); - } else { - infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ); - } -#else - infile = File(from_path, File::FLAG_OPEN | File::FLAG_READ); -#endif - if (!infile.IsValid()) - return false; - - File outfile(to_path, File::FLAG_WRITE | File::FLAG_CREATE_ALWAYS); - if (!outfile.IsValid()) - return false; - - return CopyFileContents(&infile, &outfile); -} -#endif // !defined(OS_MACOSX) - -// ----------------------------------------------------------------------------- - -namespace internal { - -bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { - AssertBlockingAllowed(); - // Windows compatibility: if |to_path| exists, |from_path| and |to_path| - // must be the same type, either both files, or both directories. - stat_wrapper_t to_file_info; - if (CallStat(to_path.value().c_str(), &to_file_info) == 0) { - stat_wrapper_t from_file_info; - if (CallStat(from_path.value().c_str(), &from_file_info) != 0) - return false; - if (S_ISDIR(to_file_info.st_mode) != S_ISDIR(from_file_info.st_mode)) - return false; - } - - if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) - return true; - - if (!CopyDirectory(from_path, to_path, true)) - return false; - - DeleteFile(from_path, true); - return true; -} - -} // namespace internal - -#endif // !defined(OS_NACL_NONSFI) - -#if defined(OS_LINUX) || defined(OS_AIX) -BASE_EXPORT bool IsPathExecutable(const FilePath& path) { - bool result = false; - FilePath tmp_file_path; - - ScopedFD fd(CreateAndOpenFdForTemporaryFileInDir(path, &tmp_file_path)); - if (fd.is_valid()) { - DeleteFile(tmp_file_path, false); - long sysconf_result = sysconf(_SC_PAGESIZE); - CHECK_GE(sysconf_result, 0); - size_t pagesize = static_cast(sysconf_result); - CHECK_GE(sizeof(pagesize), sizeof(sysconf_result)); - void* mapping = mmap(nullptr, pagesize, PROT_READ, MAP_SHARED, fd.get(), 0); - if (mapping != MAP_FAILED) { - if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0) - result = true; - munmap(mapping, pagesize); - } - } - return result; -} -#endif // defined(OS_LINUX) || defined(OS_AIX) - -} // namespace base diff --git a/files/file_util_unittest.cc b/files/file_util_unittest.cc deleted file mode 100644 index 05a38966f..000000000 --- a/files/file_util_unittest.cc +++ /dev/null @@ -1,3749 +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 -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "base/base_paths.h" -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/environment.h" -#include "base/files/file.h" -#include "base/files/file_enumerator.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/guid.h" -#include "base/macros.h" -#include "base/path_service.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/test/multiprocess_test.h" -#include "base/test/scoped_environment_variable_override.h" -#include "base/test/test_file_util.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/multiprocess_func_list.h" -#include "testing/platform_test.h" - -#if defined(OS_WIN) -#include -#include -#include -#include -#include -#include "base/strings/string_number_conversions.h" -#include "base/win/scoped_handle.h" -#include "base/win/win_util.h" -#endif - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#include -#include -#include -#include -#include -#endif - -#if defined(OS_LINUX) -#include -#endif - -#if defined(OS_ANDROID) -#include "base/android/content_uri_utils.h" -#endif - -// This macro helps avoid wrapped lines in the test structs. -#define FPL(x) FILE_PATH_LITERAL(x) - -namespace base { - -namespace { - -const size_t kLargeFileSize = (1 << 16) + 3; - -// To test that NormalizeFilePath() deals with NTFS reparse points correctly, -// we need functions to create and delete reparse points. -#if defined(OS_WIN) -typedef struct _REPARSE_DATA_BUFFER { - ULONG ReparseTag; - USHORT ReparseDataLength; - USHORT Reserved; - union { - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct { - UCHAR DataBuffer[1]; - } GenericReparseBuffer; - }; -} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; - -// Sets a reparse point. |source| will now point to |target|. Returns true if -// the call succeeds, false otherwise. -bool SetReparsePoint(HANDLE source, const FilePath& target_path) { - std::wstring kPathPrefix = L"\\??\\"; - std::wstring target_str; - // The juction will not work if the target path does not start with \??\ . - if (kPathPrefix != target_path.value().substr(0, kPathPrefix.size())) - target_str += kPathPrefix; - target_str += target_path.value(); - const wchar_t* target = target_str.c_str(); - USHORT size_target = static_cast(wcslen(target)) * sizeof(target[0]); - char buffer[2000] = {0}; - DWORD returned; - - REPARSE_DATA_BUFFER* data = reinterpret_cast(buffer); - - data->ReparseTag = 0xa0000003; - memcpy(data->MountPointReparseBuffer.PathBuffer, target, size_target + 2); - - data->MountPointReparseBuffer.SubstituteNameLength = size_target; - data->MountPointReparseBuffer.PrintNameOffset = size_target + 2; - data->ReparseDataLength = size_target + 4 + 8; - - int data_size = data->ReparseDataLength + 8; - - if (!DeviceIoControl(source, FSCTL_SET_REPARSE_POINT, &buffer, data_size, - NULL, 0, &returned, NULL)) { - return false; - } - return true; -} - -// Delete the reparse point referenced by |source|. Returns true if the call -// succeeds, false otherwise. -bool DeleteReparsePoint(HANDLE source) { - DWORD returned; - REPARSE_DATA_BUFFER data = {0}; - data.ReparseTag = 0xa0000003; - if (!DeviceIoControl(source, FSCTL_DELETE_REPARSE_POINT, &data, 8, NULL, 0, - &returned, NULL)) { - return false; - } - return true; -} - -// Manages a reparse point for a test. -class ReparsePoint { - public: - // Creates a reparse point from |source| (an empty directory) to |target|. - ReparsePoint(const FilePath& source, const FilePath& target) { - dir_.Set( - ::CreateFile(source.value().c_str(), - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory. - NULL)); - created_ = dir_.IsValid() && SetReparsePoint(dir_.Get(), target); - } - - ~ReparsePoint() { - if (created_) - DeleteReparsePoint(dir_.Get()); - } - - bool IsValid() { return created_; } - - private: - win::ScopedHandle dir_; - bool created_; - DISALLOW_COPY_AND_ASSIGN(ReparsePoint); -}; - -#endif - -// Fuchsia doesn't support file permissions. -#if !defined(OS_FUCHSIA) -#if defined(OS_POSIX) -// Provide a simple way to change the permissions bits on |path| in tests. -// ASSERT failures will return, but not stop the test. Caller should wrap -// calls to this function in ASSERT_NO_FATAL_FAILURE(). -void ChangePosixFilePermissions(const FilePath& path, - int mode_bits_to_set, - int mode_bits_to_clear) { - ASSERT_FALSE(mode_bits_to_set & mode_bits_to_clear) - << "Can't set and clear the same bits."; - - int mode = 0; - ASSERT_TRUE(GetPosixFilePermissions(path, &mode)); - mode |= mode_bits_to_set; - mode &= ~mode_bits_to_clear; - ASSERT_TRUE(SetPosixFilePermissions(path, mode)); -} -#endif // defined(OS_POSIX) - -// Sets the source file to read-only. -void SetReadOnly(const FilePath& path, bool read_only) { -#if defined(OS_WIN) - // On Windows, it involves setting/removing the 'readonly' bit. - DWORD attrs = GetFileAttributes(path.value().c_str()); - ASSERT_NE(INVALID_FILE_ATTRIBUTES, attrs); - ASSERT_TRUE(SetFileAttributes( - path.value().c_str(), read_only ? (attrs | FILE_ATTRIBUTE_READONLY) - : (attrs & ~FILE_ATTRIBUTE_READONLY))); - - DWORD expected = - read_only - ? ((attrs & (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY)) | - FILE_ATTRIBUTE_READONLY) - : (attrs & (FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DIRECTORY)); - - // Ignore FILE_ATTRIBUTE_NOT_CONTENT_INDEXED if present. - attrs = GetFileAttributes(path.value().c_str()) & - ~FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; - ASSERT_EQ(expected, attrs); -#else - // On all other platforms, it involves removing/setting the write bit. - mode_t mode = read_only ? S_IRUSR : (S_IRUSR | S_IWUSR); - EXPECT_TRUE(SetPosixFilePermissions( - path, DirectoryExists(path) ? (mode | S_IXUSR) : mode)); -#endif // defined(OS_WIN) -} - -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 // defined(OS_WIN) -} - -#endif // defined(OS_FUCHSIA) - -const wchar_t bogus_content[] = L"I'm cannon fodder."; - -const int FILES_AND_DIRECTORIES = - FileEnumerator::FILES | FileEnumerator::DIRECTORIES; - -// file_util winds up using autoreleased objects on the Mac, so this needs -// to be a PlatformTest -class FileUtilTest : public PlatformTest { - protected: - void SetUp() override { - PlatformTest::SetUp(); - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - } - - ScopedTempDir temp_dir_; -}; - -// Collects all the results from the given file enumerator, and provides an -// interface to query whether a given file is present. -class FindResultCollector { - public: - explicit FindResultCollector(FileEnumerator* enumerator) { - FilePath cur_file; - while (!(cur_file = enumerator->Next()).value().empty()) { - FilePath::StringType path = cur_file.value(); - // The file should not be returned twice. - EXPECT_TRUE(files_.end() == files_.find(path)) - << "Same file returned twice"; - - // Save for later. - files_.insert(path); - } - } - - // Returns true if the enumerator found the file. - bool HasFile(const FilePath& file) const { - return files_.find(file.value()) != files_.end(); - } - - int size() { - return static_cast(files_.size()); - } - - private: - std::set files_; -}; - -// Simple function to dump some text into a new file. -void CreateTextFile(const FilePath& filename, - const std::wstring& contents) { - std::wofstream file; - file.open(filename.value().c_str()); - ASSERT_TRUE(file.is_open()); - file << contents; - file.close(); -} - -// Simple function to take out some text from a file. -std::wstring ReadTextFile(const FilePath& filename) { - wchar_t contents[64]; - std::wifstream file; - file.open(filename.value().c_str()); - EXPECT_TRUE(file.is_open()); - file.getline(contents, arraysize(contents)); - file.close(); - return std::wstring(contents); -} - -// Sets |is_inheritable| to indicate whether or not |stream| is set up to be -// inerhited into child processes (i.e., HANDLE_FLAG_INHERIT is set on the -// underlying handle on Windows, or FD_CLOEXEC is not set on the underlying file -// descriptor on POSIX). Calls to this function must be wrapped with -// ASSERT_NO_FATAL_FAILURE to properly abort tests in case of fatal failure. -void GetIsInheritable(FILE* stream, bool* is_inheritable) { -#if defined(OS_WIN) - HANDLE handle = reinterpret_cast(_get_osfhandle(_fileno(stream))); - ASSERT_NE(INVALID_HANDLE_VALUE, handle); - - DWORD info = 0; - ASSERT_EQ(TRUE, ::GetHandleInformation(handle, &info)); - *is_inheritable = ((info & HANDLE_FLAG_INHERIT) != 0); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - int fd = fileno(stream); - ASSERT_NE(-1, fd); - int flags = fcntl(fd, F_GETFD, 0); - ASSERT_NE(-1, flags); - *is_inheritable = ((flags & FD_CLOEXEC) == 0); -#else -#error Not implemented -#endif -} - -TEST_F(FileUtilTest, FileAndDirectorySize) { - // Create three files of 20, 30 and 3 chars (utf8). ComputeDirectorySize - // should return 53 bytes. - FilePath file_01 = temp_dir_.GetPath().Append(FPL("The file 01.txt")); - CreateTextFile(file_01, L"12345678901234567890"); - int64_t size_f1 = 0; - ASSERT_TRUE(GetFileSize(file_01, &size_f1)); - EXPECT_EQ(20ll, size_f1); - - FilePath subdir_path = temp_dir_.GetPath().Append(FPL("Level2")); - CreateDirectory(subdir_path); - - FilePath file_02 = subdir_path.Append(FPL("The file 02.txt")); - CreateTextFile(file_02, L"123456789012345678901234567890"); - int64_t size_f2 = 0; - ASSERT_TRUE(GetFileSize(file_02, &size_f2)); - EXPECT_EQ(30ll, size_f2); - - FilePath subsubdir_path = subdir_path.Append(FPL("Level3")); - CreateDirectory(subsubdir_path); - - FilePath file_03 = subsubdir_path.Append(FPL("The file 03.txt")); - CreateTextFile(file_03, L"123"); - - int64_t computed_size = ComputeDirectorySize(temp_dir_.GetPath()); - EXPECT_EQ(size_f1 + size_f2 + 3, computed_size); -} - -TEST_F(FileUtilTest, NormalizeFilePathBasic) { - // Create a directory under the test dir. Because we create it, - // we know it is not a link. - FilePath file_a_path = temp_dir_.GetPath().Append(FPL("file_a")); - FilePath dir_path = temp_dir_.GetPath().Append(FPL("dir")); - FilePath file_b_path = dir_path.Append(FPL("file_b")); - CreateDirectory(dir_path); - - FilePath normalized_file_a_path, normalized_file_b_path; - ASSERT_FALSE(PathExists(file_a_path)); - ASSERT_FALSE(NormalizeFilePath(file_a_path, &normalized_file_a_path)) - << "NormalizeFilePath() should fail on nonexistent paths."; - - CreateTextFile(file_a_path, bogus_content); - ASSERT_TRUE(PathExists(file_a_path)); - ASSERT_TRUE(NormalizeFilePath(file_a_path, &normalized_file_a_path)); - - CreateTextFile(file_b_path, bogus_content); - ASSERT_TRUE(PathExists(file_b_path)); - ASSERT_TRUE(NormalizeFilePath(file_b_path, &normalized_file_b_path)); - - // Beacuse this test created |dir_path|, we know it is not a link - // or junction. So, the real path of the directory holding file a - // must be the parent of the path holding file b. - ASSERT_TRUE(normalized_file_a_path.DirName() - .IsParent(normalized_file_b_path.DirName())); -} - -#if defined(OS_WIN) - -TEST_F(FileUtilTest, NormalizeFilePathReparsePoints) { - // Build the following directory structure: - // - // temp_dir - // |-> base_a - // | |-> sub_a - // | |-> file.txt - // | |-> long_name___... (Very long name.) - // | |-> sub_long - // | |-> deep.txt - // |-> base_b - // |-> to_sub_a (reparse point to temp_dir\base_a\sub_a) - // |-> to_base_b (reparse point to temp_dir\base_b) - // |-> to_sub_long (reparse point to temp_dir\sub_a\long_name_\sub_long) - - FilePath base_a = temp_dir_.GetPath().Append(FPL("base_a")); -#if defined(OS_WIN) - // TEMP can have a lower case drive letter. - string16 temp_base_a = base_a.value(); - ASSERT_FALSE(temp_base_a.empty()); - *temp_base_a.begin() = ToUpperASCII(*temp_base_a.begin()); - base_a = FilePath(temp_base_a); -#endif - ASSERT_TRUE(CreateDirectory(base_a)); - - FilePath sub_a = base_a.Append(FPL("sub_a")); - ASSERT_TRUE(CreateDirectory(sub_a)); - - FilePath file_txt = sub_a.Append(FPL("file.txt")); - CreateTextFile(file_txt, bogus_content); - - // Want a directory whose name is long enough to make the path to the file - // inside just under MAX_PATH chars. This will be used to test that when - // a junction expands to a path over MAX_PATH chars in length, - // NormalizeFilePath() fails without crashing. - FilePath sub_long_rel(FPL("sub_long")); - FilePath deep_txt(FPL("deep.txt")); - - int target_length = MAX_PATH; - target_length -= (sub_a.value().length() + 1); // +1 for the sepperator '\'. - target_length -= (sub_long_rel.Append(deep_txt).value().length() + 1); - // Without making the path a bit shorter, CreateDirectory() fails. - // the resulting path is still long enough to hit the failing case in - // NormalizePath(). - const int kCreateDirLimit = 4; - target_length -= kCreateDirLimit; - FilePath::StringType long_name_str = FPL("long_name_"); - long_name_str.resize(target_length, '_'); - - FilePath long_name = sub_a.Append(FilePath(long_name_str)); - FilePath deep_file = long_name.Append(sub_long_rel).Append(deep_txt); - ASSERT_EQ(static_cast(MAX_PATH - kCreateDirLimit), - deep_file.value().length()); - - FilePath sub_long = deep_file.DirName(); - ASSERT_TRUE(CreateDirectory(sub_long)); - CreateTextFile(deep_file, bogus_content); - - FilePath base_b = temp_dir_.GetPath().Append(FPL("base_b")); - ASSERT_TRUE(CreateDirectory(base_b)); - - FilePath to_sub_a = base_b.Append(FPL("to_sub_a")); - ASSERT_TRUE(CreateDirectory(to_sub_a)); - FilePath normalized_path; - { - ReparsePoint reparse_to_sub_a(to_sub_a, sub_a); - ASSERT_TRUE(reparse_to_sub_a.IsValid()); - - FilePath to_base_b = base_b.Append(FPL("to_base_b")); - ASSERT_TRUE(CreateDirectory(to_base_b)); - ReparsePoint reparse_to_base_b(to_base_b, base_b); - ASSERT_TRUE(reparse_to_base_b.IsValid()); - - FilePath to_sub_long = base_b.Append(FPL("to_sub_long")); - ASSERT_TRUE(CreateDirectory(to_sub_long)); - ReparsePoint reparse_to_sub_long(to_sub_long, sub_long); - ASSERT_TRUE(reparse_to_sub_long.IsValid()); - - // Normalize a junction free path: base_a\sub_a\file.txt . - ASSERT_TRUE(NormalizeFilePath(file_txt, &normalized_path)); - ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str()); - - // Check that the path base_b\to_sub_a\file.txt can be normalized to exclude - // the junction to_sub_a. - ASSERT_TRUE(NormalizeFilePath(to_sub_a.Append(FPL("file.txt")), - &normalized_path)); - ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str()); - - // Check that the path base_b\to_base_b\to_base_b\to_sub_a\file.txt can be - // normalized to exclude junctions to_base_b and to_sub_a . - ASSERT_TRUE(NormalizeFilePath(base_b.Append(FPL("to_base_b")) - .Append(FPL("to_base_b")) - .Append(FPL("to_sub_a")) - .Append(FPL("file.txt")), - &normalized_path)); - ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str()); - - // A long enough path will cause NormalizeFilePath() to fail. Make a long - // path using to_base_b many times, and check that paths long enough to fail - // do not cause a crash. - FilePath long_path = base_b; - const int kLengthLimit = MAX_PATH + 200; - while (long_path.value().length() <= kLengthLimit) { - long_path = long_path.Append(FPL("to_base_b")); - } - long_path = long_path.Append(FPL("to_sub_a")) - .Append(FPL("file.txt")); - - ASSERT_FALSE(NormalizeFilePath(long_path, &normalized_path)); - - // Normalizing the junction to deep.txt should fail, because the expanded - // path to deep.txt is longer than MAX_PATH. - ASSERT_FALSE(NormalizeFilePath(to_sub_long.Append(deep_txt), - &normalized_path)); - - // Delete the reparse points, and see that NormalizeFilePath() fails - // to traverse them. - } - - ASSERT_FALSE(NormalizeFilePath(to_sub_a.Append(FPL("file.txt")), - &normalized_path)); -} - -TEST_F(FileUtilTest, DevicePathToDriveLetter) { - // Get a drive letter. - string16 real_drive_letter = - ToUpperASCII(temp_dir_.GetPath().value().substr(0, 2)); - if (!isalpha(real_drive_letter[0]) || ':' != real_drive_letter[1]) { - LOG(ERROR) << "Can't get a drive letter to test with."; - return; - } - - // Get the NT style path to that drive. - wchar_t device_path[MAX_PATH] = {'\0'}; - ASSERT_TRUE( - ::QueryDosDevice(real_drive_letter.c_str(), device_path, MAX_PATH)); - FilePath actual_device_path(device_path); - FilePath win32_path; - - // Run DevicePathToDriveLetterPath() on the NT style path we got from - // QueryDosDevice(). Expect the drive letter we started with. - ASSERT_TRUE(DevicePathToDriveLetterPath(actual_device_path, &win32_path)); - ASSERT_EQ(real_drive_letter, win32_path.value()); - - // Add some directories to the path. Expect those extra path componenets - // to be preserved. - FilePath kRelativePath(FPL("dir1\\dir2\\file.txt")); - ASSERT_TRUE(DevicePathToDriveLetterPath( - actual_device_path.Append(kRelativePath), - &win32_path)); - EXPECT_EQ(FilePath(real_drive_letter + L"\\").Append(kRelativePath).value(), - win32_path.value()); - - // Deform the real path so that it is invalid by removing the last four - // characters. The way windows names devices that are hard disks - // (\Device\HardDiskVolume${NUMBER}) guarantees that the string is longer - // than three characters. The only way the truncated string could be a - // real drive is if more than 10^3 disks are mounted: - // \Device\HardDiskVolume10000 would be truncated to \Device\HardDiskVolume1 - // Check that DevicePathToDriveLetterPath fails. - int path_length = actual_device_path.value().length(); - int new_length = path_length - 4; - ASSERT_LT(0, new_length); - FilePath prefix_of_real_device_path( - actual_device_path.value().substr(0, new_length)); - ASSERT_FALSE(DevicePathToDriveLetterPath(prefix_of_real_device_path, - &win32_path)); - - ASSERT_FALSE(DevicePathToDriveLetterPath( - prefix_of_real_device_path.Append(kRelativePath), - &win32_path)); - - // Deform the real path so that it is invalid by adding some characters. For - // example, if C: maps to \Device\HardDiskVolume8, then we simulate a - // request for the drive letter whose native path is - // \Device\HardDiskVolume812345 . We assume such a device does not exist, - // because drives are numbered in order and mounting 112345 hard disks will - // never happen. - const FilePath::StringType kExtraChars = FPL("12345"); - - FilePath real_device_path_plus_numbers( - actual_device_path.value() + kExtraChars); - - ASSERT_FALSE(DevicePathToDriveLetterPath( - real_device_path_plus_numbers, - &win32_path)); - - ASSERT_FALSE(DevicePathToDriveLetterPath( - real_device_path_plus_numbers.Append(kRelativePath), - &win32_path)); -} - -TEST_F(FileUtilTest, CreateTemporaryFileInDirLongPathTest) { - // Test that CreateTemporaryFileInDir() creates a path and returns a long path - // if it is available. This test requires that: - // - the filesystem at |temp_dir_| supports long filenames. - // - the account has FILE_LIST_DIRECTORY permission for all ancestor - // directories of |temp_dir_|. - const FilePath::CharType kLongDirName[] = FPL("A long path"); - const FilePath::CharType kTestSubDirName[] = FPL("test"); - FilePath long_test_dir = temp_dir_.GetPath().Append(kLongDirName); - ASSERT_TRUE(CreateDirectory(long_test_dir)); - - // kLongDirName is not a 8.3 component. So GetShortName() should give us a - // different short name. - WCHAR path_buffer[MAX_PATH]; - DWORD path_buffer_length = GetShortPathName(long_test_dir.value().c_str(), - path_buffer, MAX_PATH); - ASSERT_LT(path_buffer_length, DWORD(MAX_PATH)); - ASSERT_NE(DWORD(0), path_buffer_length); - FilePath short_test_dir(path_buffer); - ASSERT_STRNE(kLongDirName, short_test_dir.BaseName().value().c_str()); - - FilePath temp_file; - ASSERT_TRUE(CreateTemporaryFileInDir(short_test_dir, &temp_file)); - EXPECT_STREQ(kLongDirName, temp_file.DirName().BaseName().value().c_str()); - EXPECT_TRUE(PathExists(temp_file)); - - // Create a subdirectory of |long_test_dir| and make |long_test_dir| - // unreadable. We should still be able to create a temp file in the - // subdirectory, but we won't be able to determine the long path for it. This - // mimics the environment that some users run where their user profiles reside - // in a location where the don't have full access to the higher level - // directories. (Note that this assumption is true for NTFS, but not for some - // network file systems. E.g. AFS). - FilePath access_test_dir = long_test_dir.Append(kTestSubDirName); - ASSERT_TRUE(CreateDirectory(access_test_dir)); - FilePermissionRestorer long_test_dir_restorer(long_test_dir); - ASSERT_TRUE(MakeFileUnreadable(long_test_dir)); - - // Use the short form of the directory to create a temporary filename. - ASSERT_TRUE(CreateTemporaryFileInDir( - short_test_dir.Append(kTestSubDirName), &temp_file)); - EXPECT_TRUE(PathExists(temp_file)); - EXPECT_TRUE(short_test_dir.IsParent(temp_file.DirName())); - - // Check that the long path can't be determined for |temp_file|. - path_buffer_length = GetLongPathName(temp_file.value().c_str(), - path_buffer, MAX_PATH); - EXPECT_EQ(DWORD(0), path_buffer_length); -} - -#endif // defined(OS_WIN) - -#if defined(OS_POSIX) - -TEST_F(FileUtilTest, CreateAndReadSymlinks) { - FilePath link_from = temp_dir_.GetPath().Append(FPL("from_file")); - FilePath link_to = temp_dir_.GetPath().Append(FPL("to_file")); - CreateTextFile(link_to, bogus_content); - - ASSERT_TRUE(CreateSymbolicLink(link_to, link_from)) - << "Failed to create file symlink."; - - // If we created the link properly, we should be able to read the contents - // through it. - EXPECT_EQ(bogus_content, ReadTextFile(link_from)); - - FilePath result; - ASSERT_TRUE(ReadSymbolicLink(link_from, &result)); - EXPECT_EQ(link_to.value(), result.value()); - - // Link to a directory. - link_from = temp_dir_.GetPath().Append(FPL("from_dir")); - link_to = temp_dir_.GetPath().Append(FPL("to_dir")); - ASSERT_TRUE(CreateDirectory(link_to)); - ASSERT_TRUE(CreateSymbolicLink(link_to, link_from)) - << "Failed to create directory symlink."; - - // Test failures. - EXPECT_FALSE(CreateSymbolicLink(link_to, link_to)); - EXPECT_FALSE(ReadSymbolicLink(link_to, &result)); - FilePath missing = temp_dir_.GetPath().Append(FPL("missing")); - EXPECT_FALSE(ReadSymbolicLink(missing, &result)); -} - -// The following test of NormalizeFilePath() require that we create a symlink. -// This can not be done on Windows before Vista. On Vista, creating a symlink -// requires privilege "SeCreateSymbolicLinkPrivilege". -// TODO(skerner): Investigate the possibility of giving base_unittests the -// privileges required to create a symlink. -TEST_F(FileUtilTest, NormalizeFilePathSymlinks) { - // Link one file to another. - FilePath link_from = temp_dir_.GetPath().Append(FPL("from_file")); - FilePath link_to = temp_dir_.GetPath().Append(FPL("to_file")); - CreateTextFile(link_to, bogus_content); - - ASSERT_TRUE(CreateSymbolicLink(link_to, link_from)) - << "Failed to create file symlink."; - - // Check that NormalizeFilePath sees the link. - FilePath normalized_path; - ASSERT_TRUE(NormalizeFilePath(link_from, &normalized_path)); - EXPECT_NE(link_from, link_to); - EXPECT_EQ(link_to.BaseName().value(), normalized_path.BaseName().value()); - EXPECT_EQ(link_to.BaseName().value(), normalized_path.BaseName().value()); - - // Link to a directory. - link_from = temp_dir_.GetPath().Append(FPL("from_dir")); - link_to = temp_dir_.GetPath().Append(FPL("to_dir")); - ASSERT_TRUE(CreateDirectory(link_to)); - ASSERT_TRUE(CreateSymbolicLink(link_to, link_from)) - << "Failed to create directory symlink."; - - EXPECT_FALSE(NormalizeFilePath(link_from, &normalized_path)) - << "Links to directories should return false."; - - // Test that a loop in the links causes NormalizeFilePath() to return false. - link_from = temp_dir_.GetPath().Append(FPL("link_a")); - link_to = temp_dir_.GetPath().Append(FPL("link_b")); - ASSERT_TRUE(CreateSymbolicLink(link_to, link_from)) - << "Failed to create loop symlink a."; - ASSERT_TRUE(CreateSymbolicLink(link_from, link_to)) - << "Failed to create loop symlink b."; - - // Infinite loop! - EXPECT_FALSE(NormalizeFilePath(link_from, &normalized_path)); -} - -TEST_F(FileUtilTest, DeleteSymlinkToExistentFile) { - // Create a file. - FilePath file_name = temp_dir_.GetPath().Append(FPL("Test DeleteFile 2.txt")); - CreateTextFile(file_name, bogus_content); - ASSERT_TRUE(PathExists(file_name)); - - // Create a symlink to the file. - FilePath file_link = temp_dir_.GetPath().Append("file_link_2"); - ASSERT_TRUE(CreateSymbolicLink(file_name, file_link)) - << "Failed to create symlink."; - - // Delete the symbolic link. - EXPECT_TRUE(DeleteFile(file_link, false)); - - // Make sure original file is not deleted. - EXPECT_FALSE(PathExists(file_link)); - EXPECT_TRUE(PathExists(file_name)); -} - -TEST_F(FileUtilTest, DeleteSymlinkToNonExistentFile) { - // Create a non-existent file path. - FilePath non_existent = - temp_dir_.GetPath().Append(FPL("Test DeleteFile 3.txt")); - EXPECT_FALSE(PathExists(non_existent)); - - // Create a symlink to the non-existent file. - FilePath file_link = temp_dir_.GetPath().Append("file_link_3"); - ASSERT_TRUE(CreateSymbolicLink(non_existent, file_link)) - << "Failed to create symlink."; - - // Make sure the symbolic link is exist. - EXPECT_TRUE(IsLink(file_link)); - EXPECT_FALSE(PathExists(file_link)); - - // Delete the symbolic link. - EXPECT_TRUE(DeleteFile(file_link, false)); - - // Make sure the symbolic link is deleted. - EXPECT_FALSE(IsLink(file_link)); -} - -TEST_F(FileUtilTest, CopyFileFollowsSymlinks) { - FilePath link_from = temp_dir_.GetPath().Append(FPL("from_file")); - FilePath link_to = temp_dir_.GetPath().Append(FPL("to_file")); - CreateTextFile(link_to, bogus_content); - - ASSERT_TRUE(CreateSymbolicLink(link_to, link_from)); - - // If we created the link properly, we should be able to read the contents - // through it. - EXPECT_EQ(bogus_content, ReadTextFile(link_from)); - - FilePath result; - ASSERT_TRUE(ReadSymbolicLink(link_from, &result)); - EXPECT_EQ(link_to.value(), result.value()); - - // Create another file and copy it to |link_from|. - FilePath src_file = temp_dir_.GetPath().Append(FPL("src.txt")); - const std::wstring file_contents(L"Gooooooooooooooooooooogle"); - CreateTextFile(src_file, file_contents); - ASSERT_TRUE(CopyFile(src_file, link_from)); - - // Make sure |link_from| is still a symlink, and |link_to| has been written to - // by CopyFile(). - EXPECT_TRUE(IsLink(link_from)); - EXPECT_EQ(file_contents, ReadTextFile(link_from)); - EXPECT_EQ(file_contents, ReadTextFile(link_to)); -} - -TEST_F(FileUtilTest, ChangeFilePermissionsAndRead) { - // Create a file path. - FilePath file_name = - temp_dir_.GetPath().Append(FPL("Test Readable File.txt")); - EXPECT_FALSE(PathExists(file_name)); - - static constexpr char kData[] = "hello"; - static constexpr int kDataSize = sizeof(kData) - 1; - char buffer[kDataSize]; - - // Write file. - EXPECT_EQ(kDataSize, WriteFile(file_name, kData, kDataSize)); - EXPECT_TRUE(PathExists(file_name)); - - // Make sure the file is readable. - int32_t mode = 0; - EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode)); - EXPECT_TRUE(mode & FILE_PERMISSION_READ_BY_USER); - - // Get rid of the read permission. - EXPECT_TRUE(SetPosixFilePermissions(file_name, 0u)); - EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode)); - EXPECT_FALSE(mode & FILE_PERMISSION_READ_BY_USER); - // Make sure the file can't be read. - EXPECT_EQ(-1, ReadFile(file_name, buffer, kDataSize)); - - // Give the read permission. - EXPECT_TRUE(SetPosixFilePermissions(file_name, FILE_PERMISSION_READ_BY_USER)); - EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode)); - EXPECT_TRUE(mode & FILE_PERMISSION_READ_BY_USER); - // Make sure the file can be read. - EXPECT_EQ(kDataSize, ReadFile(file_name, buffer, kDataSize)); - - // Delete the file. - EXPECT_TRUE(DeleteFile(file_name, false)); - EXPECT_FALSE(PathExists(file_name)); -} - -TEST_F(FileUtilTest, ChangeFilePermissionsAndWrite) { - // Create a file path. - FilePath file_name = - temp_dir_.GetPath().Append(FPL("Test Readable File.txt")); - EXPECT_FALSE(PathExists(file_name)); - - const std::string kData("hello"); - - // Write file. - EXPECT_EQ(static_cast(kData.length()), - WriteFile(file_name, kData.data(), kData.length())); - EXPECT_TRUE(PathExists(file_name)); - - // Make sure the file is writable. - int mode = 0; - EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode)); - EXPECT_TRUE(mode & FILE_PERMISSION_WRITE_BY_USER); - EXPECT_TRUE(PathIsWritable(file_name)); - - // Get rid of the write permission. - EXPECT_TRUE(SetPosixFilePermissions(file_name, 0u)); - 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, WriteFile(file_name, kData.data(), kData.length())); - EXPECT_FALSE(PathIsWritable(file_name)); - - // Give read permission. - EXPECT_TRUE(SetPosixFilePermissions(file_name, - FILE_PERMISSION_WRITE_BY_USER)); - EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode)); - EXPECT_TRUE(mode & FILE_PERMISSION_WRITE_BY_USER); - // Make sure the file can be write. - EXPECT_EQ(static_cast(kData.length()), - WriteFile(file_name, kData.data(), kData.length())); - EXPECT_TRUE(PathIsWritable(file_name)); - - // Delete the file. - EXPECT_TRUE(DeleteFile(file_name, false)); - EXPECT_FALSE(PathExists(file_name)); -} - -TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) { - // Create a directory path. - FilePath subdir_path = temp_dir_.GetPath().Append(FPL("PermissionTest1")); - CreateDirectory(subdir_path); - ASSERT_TRUE(PathExists(subdir_path)); - - // Create a dummy file to enumerate. - FilePath file_name = subdir_path.Append(FPL("Test Readable File.txt")); - EXPECT_FALSE(PathExists(file_name)); - const std::string kData("hello"); - EXPECT_EQ(static_cast(kData.length()), - WriteFile(file_name, kData.data(), kData.length())); - EXPECT_TRUE(PathExists(file_name)); - - // Make sure the directory has the all permissions. - int mode = 0; - EXPECT_TRUE(GetPosixFilePermissions(subdir_path, &mode)); - EXPECT_EQ(FILE_PERMISSION_USER_MASK, mode & FILE_PERMISSION_USER_MASK); - - // Get rid of the permissions from the directory. - EXPECT_TRUE(SetPosixFilePermissions(subdir_path, 0u)); - EXPECT_TRUE(GetPosixFilePermissions(subdir_path, &mode)); - EXPECT_FALSE(mode & FILE_PERMISSION_USER_MASK); - - // Make sure the file in the directory can't be enumerated. - FileEnumerator f1(subdir_path, true, FileEnumerator::FILES); - EXPECT_TRUE(PathExists(subdir_path)); - FindResultCollector c1(&f1); - EXPECT_EQ(0, c1.size()); - EXPECT_FALSE(GetPosixFilePermissions(file_name, &mode)); - - // Give the permissions to the directory. - EXPECT_TRUE(SetPosixFilePermissions(subdir_path, FILE_PERMISSION_USER_MASK)); - EXPECT_TRUE(GetPosixFilePermissions(subdir_path, &mode)); - EXPECT_EQ(FILE_PERMISSION_USER_MASK, mode & FILE_PERMISSION_USER_MASK); - - // Make sure the file in the directory can be enumerated. - FileEnumerator f2(subdir_path, true, FileEnumerator::FILES); - FindResultCollector c2(&f2); - EXPECT_TRUE(c2.HasFile(file_name)); - EXPECT_EQ(1, c2.size()); - - // Delete the file. - EXPECT_TRUE(DeleteFile(subdir_path, true)); - EXPECT_FALSE(PathExists(subdir_path)); -} - -TEST_F(FileUtilTest, ExecutableExistsInPath) { - // Create two directories that we will put in our PATH - const FilePath::CharType kDir1[] = FPL("dir1"); - const FilePath::CharType kDir2[] = FPL("dir2"); - - FilePath dir1 = temp_dir_.GetPath().Append(kDir1); - FilePath dir2 = temp_dir_.GetPath().Append(kDir2); - ASSERT_TRUE(CreateDirectory(dir1)); - ASSERT_TRUE(CreateDirectory(dir2)); - - test::ScopedEnvironmentVariableOverride scoped_env( - "PATH", dir1.value() + ":" + dir2.value()); - ASSERT_TRUE(scoped_env.IsOverridden()); - - const FilePath::CharType kRegularFileName[] = FPL("regular_file"); - const FilePath::CharType kExeFileName[] = FPL("exe"); - const FilePath::CharType kDneFileName[] = FPL("does_not_exist"); - - const FilePath kExePath = dir1.Append(kExeFileName); - const FilePath kRegularFilePath = dir2.Append(kRegularFileName); - - // Write file. - const std::string kData("hello"); - ASSERT_EQ(static_cast(kData.length()), - WriteFile(kExePath, kData.data(), kData.length())); - ASSERT_TRUE(PathExists(kExePath)); - ASSERT_EQ(static_cast(kData.length()), - WriteFile(kRegularFilePath, kData.data(), kData.length())); - ASSERT_TRUE(PathExists(kRegularFilePath)); - - ASSERT_TRUE(SetPosixFilePermissions(dir1.Append(kExeFileName), - FILE_PERMISSION_EXECUTE_BY_USER)); - - EXPECT_TRUE(ExecutableExistsInPath(scoped_env.GetEnv(), kExeFileName)); - EXPECT_FALSE(ExecutableExistsInPath(scoped_env.GetEnv(), kRegularFileName)); - EXPECT_FALSE(ExecutableExistsInPath(scoped_env.GetEnv(), kDneFileName)); -} - -TEST_F(FileUtilTest, CopyDirectoryPermissions) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create some regular files under the directory with various permissions. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Reggy-1.txt")); - CreateTextFile(file_name_from, L"Mordecai"); - ASSERT_TRUE(PathExists(file_name_from)); - ASSERT_TRUE(SetPosixFilePermissions(file_name_from, 0755)); - - FilePath file2_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Reggy-2.txt")); - CreateTextFile(file2_name_from, L"Rigby"); - ASSERT_TRUE(PathExists(file2_name_from)); - ASSERT_TRUE(SetPosixFilePermissions(file2_name_from, 0777)); - - FilePath file3_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Reggy-3.txt")); - CreateTextFile(file3_name_from, L"Benson"); - ASSERT_TRUE(PathExists(file3_name_from)); - ASSERT_TRUE(SetPosixFilePermissions(file3_name_from, 0400)); - - // Copy the directory recursively. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Reggy-1.txt")); - FilePath file2_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Reggy-2.txt")); - FilePath file3_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Reggy-3.txt")); - - ASSERT_FALSE(PathExists(dir_name_to)); - - EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, true)); - ASSERT_TRUE(PathExists(file_name_to)); - ASSERT_TRUE(PathExists(file2_name_to)); - ASSERT_TRUE(PathExists(file3_name_to)); - - int mode = 0; - int expected_mode; - ASSERT_TRUE(GetPosixFilePermissions(file_name_to, &mode)); -#if defined(OS_MACOSX) - expected_mode = 0755; -#elif defined(OS_CHROMEOS) - expected_mode = 0644; -#else - expected_mode = 0600; -#endif - EXPECT_EQ(expected_mode, mode); - - ASSERT_TRUE(GetPosixFilePermissions(file2_name_to, &mode)); -#if defined(OS_MACOSX) - expected_mode = 0755; -#elif defined(OS_CHROMEOS) - expected_mode = 0644; -#else - expected_mode = 0600; -#endif - EXPECT_EQ(expected_mode, mode); - - ASSERT_TRUE(GetPosixFilePermissions(file3_name_to, &mode)); -#if defined(OS_MACOSX) - expected_mode = 0600; -#elif defined(OS_CHROMEOS) - expected_mode = 0644; -#else - expected_mode = 0600; -#endif - EXPECT_EQ(expected_mode, mode); -} - -TEST_F(FileUtilTest, CopyDirectoryPermissionsOverExistingFile) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Reggy-1.txt")); - CreateTextFile(file_name_from, L"Mordecai"); - ASSERT_TRUE(PathExists(file_name_from)); - ASSERT_TRUE(SetPosixFilePermissions(file_name_from, 0644)); - - // Create a directory. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - CreateDirectory(dir_name_to); - ASSERT_TRUE(PathExists(dir_name_to)); - - // Create a file under the directory with wider permissions. - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Reggy-1.txt")); - CreateTextFile(file_name_to, L"Rigby"); - ASSERT_TRUE(PathExists(file_name_to)); - ASSERT_TRUE(SetPosixFilePermissions(file_name_to, 0777)); - - // Ensure that when we copy the directory, the file contents are copied - // but the permissions on the destination are left alone. - EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, false)); - ASSERT_TRUE(PathExists(file_name_to)); - ASSERT_EQ(L"Mordecai", ReadTextFile(file_name_to)); - - int mode = 0; - ASSERT_TRUE(GetPosixFilePermissions(file_name_to, &mode)); - EXPECT_EQ(0777, mode); -} - -TEST_F(FileUtilTest, CopyDirectoryExclDoesNotOverwrite) { - // Create source directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Reggy-1.txt")); - CreateTextFile(file_name_from, L"Mordecai"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Create destination directory. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - CreateDirectory(dir_name_to); - ASSERT_TRUE(PathExists(dir_name_to)); - - // Create a file under the directory with the same name. - FilePath file_name_to = dir_name_to.Append(FILE_PATH_LITERAL("Reggy-1.txt")); - CreateTextFile(file_name_to, L"Rigby"); - ASSERT_TRUE(PathExists(file_name_to)); - - // Ensure that copying failed and the file was not overwritten. - EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false)); - ASSERT_TRUE(PathExists(file_name_to)); - ASSERT_EQ(L"Rigby", ReadTextFile(file_name_to)); -} - -TEST_F(FileUtilTest, CopyDirectoryExclDirectoryOverExistingFile) { - // Create source directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a subdirectory. - FilePath subdir_name_from = dir_name_from.Append(FILE_PATH_LITERAL("Subsub")); - CreateDirectory(subdir_name_from); - ASSERT_TRUE(PathExists(subdir_name_from)); - - // Create destination directory. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - CreateDirectory(dir_name_to); - ASSERT_TRUE(PathExists(dir_name_to)); - - // Create a regular file under the directory with the same name. - FilePath file_name_to = dir_name_to.Append(FILE_PATH_LITERAL("Subsub")); - CreateTextFile(file_name_to, L"Rigby"); - ASSERT_TRUE(PathExists(file_name_to)); - - // Ensure that copying failed and the file was not overwritten. - EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false)); - ASSERT_TRUE(PathExists(file_name_to)); - ASSERT_EQ(L"Rigby", ReadTextFile(file_name_to)); -} - -TEST_F(FileUtilTest, CopyDirectoryExclDirectoryOverExistingDirectory) { - // Create source directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a subdirectory. - FilePath subdir_name_from = dir_name_from.Append(FILE_PATH_LITERAL("Subsub")); - CreateDirectory(subdir_name_from); - ASSERT_TRUE(PathExists(subdir_name_from)); - - // Create destination directory. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - CreateDirectory(dir_name_to); - ASSERT_TRUE(PathExists(dir_name_to)); - - // Create a subdirectory under the directory with the same name. - FilePath subdir_name_to = dir_name_to.Append(FILE_PATH_LITERAL("Subsub")); - CreateDirectory(subdir_name_to); - ASSERT_TRUE(PathExists(subdir_name_to)); - - // Ensure that copying failed and the file was not overwritten. - EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false)); -} - -TEST_F(FileUtilTest, CopyFileExecutablePermission) { - FilePath src = temp_dir_.GetPath().Append(FPL("src.txt")); - const std::wstring file_contents(L"Gooooooooooooooooooooogle"); - CreateTextFile(src, file_contents); - - ASSERT_TRUE(SetPosixFilePermissions(src, 0755)); - int mode = 0; - ASSERT_TRUE(GetPosixFilePermissions(src, &mode)); - EXPECT_EQ(0755, mode); - - FilePath dst = temp_dir_.GetPath().Append(FPL("dst.txt")); - ASSERT_TRUE(CopyFile(src, dst)); - EXPECT_EQ(file_contents, ReadTextFile(dst)); - - ASSERT_TRUE(GetPosixFilePermissions(dst, &mode)); - int expected_mode; -#if defined(OS_MACOSX) - expected_mode = 0755; -#elif defined(OS_CHROMEOS) - expected_mode = 0644; -#else - expected_mode = 0600; -#endif - EXPECT_EQ(expected_mode, mode); - ASSERT_TRUE(DeleteFile(dst, false)); - - ASSERT_TRUE(SetPosixFilePermissions(src, 0777)); - ASSERT_TRUE(GetPosixFilePermissions(src, &mode)); - EXPECT_EQ(0777, mode); - - ASSERT_TRUE(CopyFile(src, dst)); - EXPECT_EQ(file_contents, ReadTextFile(dst)); - - ASSERT_TRUE(GetPosixFilePermissions(dst, &mode)); -#if defined(OS_MACOSX) - expected_mode = 0755; -#elif defined(OS_CHROMEOS) - expected_mode = 0644; -#else - expected_mode = 0600; -#endif - EXPECT_EQ(expected_mode, mode); - ASSERT_TRUE(DeleteFile(dst, false)); - - ASSERT_TRUE(SetPosixFilePermissions(src, 0400)); - ASSERT_TRUE(GetPosixFilePermissions(src, &mode)); - EXPECT_EQ(0400, mode); - - ASSERT_TRUE(CopyFile(src, dst)); - EXPECT_EQ(file_contents, ReadTextFile(dst)); - - ASSERT_TRUE(GetPosixFilePermissions(dst, &mode)); -#if defined(OS_MACOSX) - expected_mode = 0600; -#elif defined(OS_CHROMEOS) - expected_mode = 0644; -#else - expected_mode = 0600; -#endif - EXPECT_EQ(expected_mode, mode); - - // This time, do not delete |dst|. Instead set its permissions to 0777. - ASSERT_TRUE(SetPosixFilePermissions(dst, 0777)); - ASSERT_TRUE(GetPosixFilePermissions(dst, &mode)); - EXPECT_EQ(0777, mode); - - // Overwrite it and check the permissions again. - ASSERT_TRUE(CopyFile(src, dst)); - EXPECT_EQ(file_contents, ReadTextFile(dst)); - ASSERT_TRUE(GetPosixFilePermissions(dst, &mode)); - EXPECT_EQ(0777, mode); -} - -#endif // defined(OS_POSIX) - -#if !defined(OS_FUCHSIA) - -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 - // CopyFile(). - FilePath src = temp_dir_.GetPath().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, true); - ASSERT_TRUE(IsReadOnly(src)); - - // Copy the file. - FilePath dst = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("dst.txt")); - ASSERT_TRUE(CopyFile(src, dst)); - EXPECT_EQ(file_contents, ReadTextFile(dst)); - - ASSERT_FALSE(IsReadOnly(dst)); -} - -TEST_F(FileUtilTest, CopyDirectoryACL) { - // Create source directories. - FilePath src = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("src")); - FilePath src_subdir = src.Append(FILE_PATH_LITERAL("subdir")); - CreateDirectory(src_subdir); - ASSERT_TRUE(PathExists(src_subdir)); - - // Create a file under the directory. - FilePath src_file = src.Append(FILE_PATH_LITERAL("src.txt")); - CreateTextFile(src_file, L"Gooooooooooooooooooooogle"); - SetReadOnly(src_file, true); - ASSERT_TRUE(IsReadOnly(src_file)); - - // Make directory read-only. - SetReadOnly(src_subdir, true); - ASSERT_TRUE(IsReadOnly(src_subdir)); - - // Copy the directory recursively. - FilePath dst = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("dst")); - FilePath dst_file = dst.Append(FILE_PATH_LITERAL("src.txt")); - EXPECT_TRUE(CopyDirectory(src, dst, true)); - - FilePath dst_subdir = dst.Append(FILE_PATH_LITERAL("subdir")); - ASSERT_FALSE(IsReadOnly(dst_subdir)); - ASSERT_FALSE(IsReadOnly(dst_file)); - - // Give write permissions to allow deletion. - SetReadOnly(src_subdir, false); - ASSERT_FALSE(IsReadOnly(src_subdir)); -} - -#endif // !defined(OS_FUCHSIA) - -TEST_F(FileUtilTest, DeleteNonExistent) { - FilePath non_existent = - temp_dir_.GetPath().AppendASCII("bogus_file_dne.foobar"); - 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, DeleteNonExistentWithNonExistentParent) { - FilePath non_existent = temp_dir_.GetPath().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_.GetPath().Append(FPL("Test DeleteFile 1.txt")); - CreateTextFile(file_name, bogus_content); - ASSERT_TRUE(PathExists(file_name)); - - // Make sure it's deleted - EXPECT_TRUE(DeleteFile(file_name, false)); - EXPECT_FALSE(PathExists(file_name)); - - // Test recursive case, create a new file - file_name = temp_dir_.GetPath().Append(FPL("Test DeleteFile 2.txt")); - CreateTextFile(file_name, bogus_content); - ASSERT_TRUE(PathExists(file_name)); - - // Make sure it's deleted - EXPECT_TRUE(DeleteFile(file_name, true)); - EXPECT_FALSE(PathExists(file_name)); -} - -#if defined(OS_WIN) -// Tests that the Delete function works for wild cards, especially -// with the recursion flag. Also coincidentally tests PathExists. -// TODO(erikkay): see if anyone's actually using this feature of the API -TEST_F(FileUtilTest, DeleteWildCard) { - // Create a file and a directory - FilePath file_name = - temp_dir_.GetPath().Append(FPL("Test DeleteWildCard.txt")); - CreateTextFile(file_name, bogus_content); - ASSERT_TRUE(PathExists(file_name)); - - FilePath subdir_path = temp_dir_.GetPath().Append(FPL("DeleteWildCardDir")); - CreateDirectory(subdir_path); - ASSERT_TRUE(PathExists(subdir_path)); - - // Create the wildcard path - FilePath directory_contents = temp_dir_.GetPath(); - directory_contents = directory_contents.Append(FPL("*")); - - // Delete non-recursively and check that only the file is deleted - EXPECT_TRUE(DeleteFile(directory_contents, false)); - EXPECT_FALSE(PathExists(file_name)); - EXPECT_TRUE(PathExists(subdir_path)); - - // Delete recursively and make sure all contents are deleted - EXPECT_TRUE(DeleteFile(directory_contents, true)); - EXPECT_FALSE(PathExists(file_name)); - EXPECT_FALSE(PathExists(subdir_path)); -} - -// TODO(erikkay): see if anyone's actually using this feature of the API -TEST_F(FileUtilTest, DeleteNonExistantWildCard) { - // Create a file and a directory - FilePath subdir_path = - temp_dir_.GetPath().Append(FPL("DeleteNonExistantWildCard")); - CreateDirectory(subdir_path); - ASSERT_TRUE(PathExists(subdir_path)); - - // Create the wildcard path - FilePath directory_contents = subdir_path; - directory_contents = directory_contents.Append(FPL("*")); - - // Delete non-recursively and check nothing got deleted - EXPECT_TRUE(DeleteFile(directory_contents, false)); - EXPECT_TRUE(PathExists(subdir_path)); - - // Delete recursively and check nothing got deleted - EXPECT_TRUE(DeleteFile(directory_contents, true)); - EXPECT_TRUE(PathExists(subdir_path)); -} -#endif - -// Tests non-recursive Delete() for a directory. -TEST_F(FileUtilTest, DeleteDirNonRecursive) { - // Create a subdirectory and put a file and two directories inside. - FilePath test_subdir = - temp_dir_.GetPath().Append(FPL("DeleteDirNonRecursive")); - CreateDirectory(test_subdir); - ASSERT_TRUE(PathExists(test_subdir)); - - FilePath file_name = test_subdir.Append(FPL("Test DeleteDir.txt")); - CreateTextFile(file_name, bogus_content); - ASSERT_TRUE(PathExists(file_name)); - - FilePath subdir_path1 = test_subdir.Append(FPL("TestSubDir1")); - CreateDirectory(subdir_path1); - ASSERT_TRUE(PathExists(subdir_path1)); - - FilePath subdir_path2 = test_subdir.Append(FPL("TestSubDir2")); - CreateDirectory(subdir_path2); - ASSERT_TRUE(PathExists(subdir_path2)); - - // Delete non-recursively and check that the empty dir got deleted - EXPECT_TRUE(DeleteFile(subdir_path2, false)); - EXPECT_FALSE(PathExists(subdir_path2)); - - // Delete non-recursively and check that nothing got deleted - EXPECT_FALSE(DeleteFile(test_subdir, false)); - EXPECT_TRUE(PathExists(test_subdir)); - EXPECT_TRUE(PathExists(file_name)); - EXPECT_TRUE(PathExists(subdir_path1)); -} - -// Tests recursive Delete() for a directory. -TEST_F(FileUtilTest, DeleteDirRecursive) { - // Create a subdirectory and put a file and two directories inside. - FilePath test_subdir = temp_dir_.GetPath().Append(FPL("DeleteDirRecursive")); - CreateDirectory(test_subdir); - ASSERT_TRUE(PathExists(test_subdir)); - - FilePath file_name = test_subdir.Append(FPL("Test DeleteDirRecursive.txt")); - CreateTextFile(file_name, bogus_content); - ASSERT_TRUE(PathExists(file_name)); - - FilePath subdir_path1 = test_subdir.Append(FPL("TestSubDir1")); - CreateDirectory(subdir_path1); - ASSERT_TRUE(PathExists(subdir_path1)); - - FilePath subdir_path2 = test_subdir.Append(FPL("TestSubDir2")); - CreateDirectory(subdir_path2); - ASSERT_TRUE(PathExists(subdir_path2)); - - // Delete recursively and check that the empty dir got deleted - EXPECT_TRUE(DeleteFile(subdir_path2, true)); - EXPECT_FALSE(PathExists(subdir_path2)); - - // Delete recursively and check that everything got deleted - EXPECT_TRUE(DeleteFile(test_subdir, true)); - EXPECT_FALSE(PathExists(file_name)); - EXPECT_FALSE(PathExists(subdir_path1)); - EXPECT_FALSE(PathExists(test_subdir)); -} - -// Tests recursive Delete() for a directory. -TEST_F(FileUtilTest, DeleteDirRecursiveWithOpenFile) { - // Create a subdirectory and put a file and two directories inside. - FilePath test_subdir = temp_dir_.GetPath().Append(FPL("DeleteWithOpenFile")); - CreateDirectory(test_subdir); - ASSERT_TRUE(PathExists(test_subdir)); - - FilePath file_name1 = test_subdir.Append(FPL("Undeletebable File1.txt")); - File file1(file_name1, - File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE); - ASSERT_TRUE(PathExists(file_name1)); - - FilePath file_name2 = test_subdir.Append(FPL("Deleteable File2.txt")); - CreateTextFile(file_name2, bogus_content); - ASSERT_TRUE(PathExists(file_name2)); - - FilePath file_name3 = test_subdir.Append(FPL("Undeletebable File3.txt")); - File file3(file_name3, - File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE); - ASSERT_TRUE(PathExists(file_name3)); - -#if defined(OS_LINUX) - // On Windows, holding the file open in sufficient to make it un-deletable. - // The POSIX code is verifiable on Linux by creating an "immutable" file but - // this is best-effort because it's not supported by all file systems. Both - // files will have the same flags so no need to get them individually. - int flags; - bool file_attrs_supported = - ioctl(file1.GetPlatformFile(), FS_IOC_GETFLAGS, &flags) == 0; - // Some filesystems (e.g. tmpfs) don't support file attributes. - if (file_attrs_supported) { - flags |= FS_IMMUTABLE_FL; - ioctl(file1.GetPlatformFile(), FS_IOC_SETFLAGS, &flags); - ioctl(file3.GetPlatformFile(), FS_IOC_SETFLAGS, &flags); - } -#endif - - // Delete recursively and check that at least the second file got deleted. - // This ensures that un-deletable files don't impact those that can be. - DeleteFile(test_subdir, true); - EXPECT_FALSE(PathExists(file_name2)); - -#if defined(OS_LINUX) - // Make sure that the test can clean up after itself. - if (file_attrs_supported) { - flags &= ~FS_IMMUTABLE_FL; - ioctl(file1.GetPlatformFile(), FS_IOC_SETFLAGS, &flags); - ioctl(file3.GetPlatformFile(), FS_IOC_SETFLAGS, &flags); - } -#endif -} - -TEST_F(FileUtilTest, MoveFileNew) { - // Create a file - FilePath file_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // The destination. - FilePath file_name_to = temp_dir_.GetPath().Append( - FILE_PATH_LITERAL("Move_Test_File_Destination.txt")); - ASSERT_FALSE(PathExists(file_name_to)); - - EXPECT_TRUE(Move(file_name_from, file_name_to)); - - // Check everything has been moved. - EXPECT_FALSE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(file_name_to)); -} - -TEST_F(FileUtilTest, MoveFileExists) { - // Create a file - FilePath file_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // The destination name. - FilePath file_name_to = temp_dir_.GetPath().Append( - FILE_PATH_LITERAL("Move_Test_File_Destination.txt")); - CreateTextFile(file_name_to, L"Old file content"); - ASSERT_TRUE(PathExists(file_name_to)); - - EXPECT_TRUE(Move(file_name_from, file_name_to)); - - // Check everything has been moved. - EXPECT_FALSE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(file_name_to)); - EXPECT_TRUE(L"Gooooooooooooooooooooogle" == ReadTextFile(file_name_to)); -} - -TEST_F(FileUtilTest, MoveFileDirExists) { - // Create a file - FilePath file_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // The destination directory - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Destination")); - CreateDirectory(dir_name_to); - ASSERT_TRUE(PathExists(dir_name_to)); - - EXPECT_FALSE(Move(file_name_from, dir_name_to)); -} - - -TEST_F(FileUtilTest, MoveNew) { - // Create a directory - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory - FilePath txt_file_name(FILE_PATH_LITERAL("Move_Test_File.txt")); - FilePath file_name_from = dir_name_from.Append(txt_file_name); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Move the directory. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_To_Subdir")); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Move_Test_File.txt")); - - ASSERT_FALSE(PathExists(dir_name_to)); - - EXPECT_TRUE(Move(dir_name_from, dir_name_to)); - - // Check everything has been moved. - EXPECT_FALSE(PathExists(dir_name_from)); - EXPECT_FALSE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(dir_name_to)); - EXPECT_TRUE(PathExists(file_name_to)); - - // Test path traversal. - file_name_from = dir_name_to.Append(txt_file_name); - file_name_to = dir_name_to.Append(FILE_PATH_LITERAL("..")); - file_name_to = file_name_to.Append(txt_file_name); - EXPECT_FALSE(Move(file_name_from, file_name_to)); - EXPECT_TRUE(PathExists(file_name_from)); - EXPECT_FALSE(PathExists(file_name_to)); - EXPECT_TRUE(internal::MoveUnsafe(file_name_from, file_name_to)); - EXPECT_FALSE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(file_name_to)); -} - -TEST_F(FileUtilTest, MoveExist) { - // Create a directory - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Move_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Move_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Move the directory - FilePath dir_name_exists = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Destination")); - - FilePath dir_name_to = - dir_name_exists.Append(FILE_PATH_LITERAL("Move_To_Subdir")); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Move_Test_File.txt")); - - // Create the destination directory. - CreateDirectory(dir_name_exists); - ASSERT_TRUE(PathExists(dir_name_exists)); - - EXPECT_TRUE(Move(dir_name_from, dir_name_to)); - - // Check everything has been moved. - EXPECT_FALSE(PathExists(dir_name_from)); - EXPECT_FALSE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(dir_name_to)); - EXPECT_TRUE(PathExists(file_name_to)); -} - -TEST_F(FileUtilTest, CopyDirectoryRecursivelyNew) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Create a subdirectory. - FilePath subdir_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Subdir")); - CreateDirectory(subdir_name_from); - ASSERT_TRUE(PathExists(subdir_name_from)); - - // Create a file under the subdirectory. - FilePath file_name2_from = - subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name2_from)); - - // Copy the directory recursively. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - FilePath subdir_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Subdir")); - FilePath file_name2_to = - subdir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - - ASSERT_FALSE(PathExists(dir_name_to)); - - EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, true)); - - // Check everything has been copied. - EXPECT_TRUE(PathExists(dir_name_from)); - EXPECT_TRUE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(subdir_name_from)); - EXPECT_TRUE(PathExists(file_name2_from)); - EXPECT_TRUE(PathExists(dir_name_to)); - EXPECT_TRUE(PathExists(file_name_to)); - EXPECT_TRUE(PathExists(subdir_name_to)); - EXPECT_TRUE(PathExists(file_name2_to)); -} - -TEST_F(FileUtilTest, CopyDirectoryRecursivelyExists) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Create a subdirectory. - FilePath subdir_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Subdir")); - CreateDirectory(subdir_name_from); - ASSERT_TRUE(PathExists(subdir_name_from)); - - // Create a file under the subdirectory. - FilePath file_name2_from = - subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name2_from)); - - // Copy the directory recursively. - FilePath dir_name_exists = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Destination")); - - FilePath dir_name_to = - dir_name_exists.Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - FilePath subdir_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Subdir")); - FilePath file_name2_to = - subdir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - - // Create the destination directory. - CreateDirectory(dir_name_exists); - ASSERT_TRUE(PathExists(dir_name_exists)); - - EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_exists, true)); - - // Check everything has been copied. - EXPECT_TRUE(PathExists(dir_name_from)); - EXPECT_TRUE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(subdir_name_from)); - EXPECT_TRUE(PathExists(file_name2_from)); - EXPECT_TRUE(PathExists(dir_name_to)); - EXPECT_TRUE(PathExists(file_name_to)); - EXPECT_TRUE(PathExists(subdir_name_to)); - EXPECT_TRUE(PathExists(file_name2_to)); -} - -TEST_F(FileUtilTest, CopyDirectoryNew) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Create a subdirectory. - FilePath subdir_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Subdir")); - CreateDirectory(subdir_name_from); - ASSERT_TRUE(PathExists(subdir_name_from)); - - // Create a file under the subdirectory. - FilePath file_name2_from = - subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name2_from)); - - // Copy the directory not recursively. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - FilePath subdir_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Subdir")); - - ASSERT_FALSE(PathExists(dir_name_to)); - - EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, false)); - - // Check everything has been copied. - EXPECT_TRUE(PathExists(dir_name_from)); - EXPECT_TRUE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(subdir_name_from)); - EXPECT_TRUE(PathExists(file_name2_from)); - EXPECT_TRUE(PathExists(dir_name_to)); - EXPECT_TRUE(PathExists(file_name_to)); - EXPECT_FALSE(PathExists(subdir_name_to)); -} - -TEST_F(FileUtilTest, CopyDirectoryExists) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Create a subdirectory. - FilePath subdir_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Subdir")); - CreateDirectory(subdir_name_from); - ASSERT_TRUE(PathExists(subdir_name_from)); - - // Create a file under the subdirectory. - FilePath file_name2_from = - subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name2_from)); - - // Copy the directory not recursively. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - FilePath subdir_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Subdir")); - - // Create the destination directory. - CreateDirectory(dir_name_to); - ASSERT_TRUE(PathExists(dir_name_to)); - - EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, false)); - - // Check everything has been copied. - EXPECT_TRUE(PathExists(dir_name_from)); - EXPECT_TRUE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(subdir_name_from)); - EXPECT_TRUE(PathExists(file_name2_from)); - EXPECT_TRUE(PathExists(dir_name_to)); - EXPECT_TRUE(PathExists(file_name_to)); - EXPECT_FALSE(PathExists(subdir_name_to)); -} - -TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToNew) { - // Create a file - FilePath file_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // The destination name - FilePath file_name_to = temp_dir_.GetPath().Append( - FILE_PATH_LITERAL("Copy_Test_File_Destination.txt")); - ASSERT_FALSE(PathExists(file_name_to)); - - EXPECT_TRUE(CopyDirectory(file_name_from, file_name_to, true)); - - // Check the has been copied - EXPECT_TRUE(PathExists(file_name_to)); -} - -TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToExisting) { - // Create a file - FilePath file_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // The destination name - FilePath file_name_to = temp_dir_.GetPath().Append( - FILE_PATH_LITERAL("Copy_Test_File_Destination.txt")); - CreateTextFile(file_name_to, L"Old file content"); - ASSERT_TRUE(PathExists(file_name_to)); - - EXPECT_TRUE(CopyDirectory(file_name_from, file_name_to, true)); - - // Check the has been copied - EXPECT_TRUE(PathExists(file_name_to)); - EXPECT_TRUE(L"Gooooooooooooooooooooogle" == ReadTextFile(file_name_to)); -} - -TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToExistingDirectory) { - // Create a file - FilePath file_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // The destination - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Destination")); - CreateDirectory(dir_name_to); - ASSERT_TRUE(PathExists(dir_name_to)); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - - EXPECT_TRUE(CopyDirectory(file_name_from, dir_name_to, true)); - - // Check the has been copied - EXPECT_TRUE(PathExists(file_name_to)); -} - -TEST_F(FileUtilTest, CopyFileFailureWithCopyDirectoryExcl) { - // Create a file - FilePath file_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Make a destination file. - FilePath file_name_to = temp_dir_.GetPath().Append( - FILE_PATH_LITERAL("Copy_Test_File_Destination.txt")); - CreateTextFile(file_name_to, L"Old file content"); - ASSERT_TRUE(PathExists(file_name_to)); - - // Overwriting the destination should fail. - EXPECT_FALSE(CopyDirectoryExcl(file_name_from, file_name_to, true)); - EXPECT_EQ(L"Old file content", ReadTextFile(file_name_to)); -} - -TEST_F(FileUtilTest, CopyDirectoryWithTrailingSeparators) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Copy the directory recursively. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - - // Create from path with trailing separators. -#if defined(OS_WIN) - FilePath from_path = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir\\\\\\")); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - FilePath from_path = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir///")); -#endif - - EXPECT_TRUE(CopyDirectory(from_path, dir_name_to, true)); - - // Check everything has been copied. - EXPECT_TRUE(PathExists(dir_name_from)); - EXPECT_TRUE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(dir_name_to)); - EXPECT_TRUE(PathExists(file_name_to)); -} - -#if defined(OS_POSIX) -TEST_F(FileUtilTest, CopyDirectoryWithNonRegularFiles) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_from)); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Create a symbolic link under the directory pointing to that file. - FilePath symlink_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Symlink")); - ASSERT_TRUE(CreateSymbolicLink(file_name_from, symlink_name_from)); - ASSERT_TRUE(PathExists(symlink_name_from)); - - // Create a fifo under the directory. - FilePath fifo_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Fifo")); - ASSERT_EQ(0, mkfifo(fifo_name_from.value().c_str(), 0644)); - ASSERT_TRUE(PathExists(fifo_name_from)); - - // Copy the directory. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - FilePath symlink_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Symlink")); - FilePath fifo_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Fifo")); - - ASSERT_FALSE(PathExists(dir_name_to)); - - EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, false)); - - // Check that only directories and regular files are copied. - EXPECT_TRUE(PathExists(dir_name_from)); - EXPECT_TRUE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(symlink_name_from)); - EXPECT_TRUE(PathExists(fifo_name_from)); - EXPECT_TRUE(PathExists(dir_name_to)); - EXPECT_TRUE(PathExists(file_name_to)); - EXPECT_FALSE(PathExists(symlink_name_to)); - EXPECT_FALSE(PathExists(fifo_name_to)); -} - -TEST_F(FileUtilTest, CopyDirectoryExclFileOverSymlink) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_from)); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Create a destination directory with a symlink of the same name. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_to)); - ASSERT_TRUE(PathExists(dir_name_to)); - - FilePath symlink_target = - dir_name_to.Append(FILE_PATH_LITERAL("Symlink_Target.txt")); - CreateTextFile(symlink_target, L"asdf"); - ASSERT_TRUE(PathExists(symlink_target)); - - FilePath symlink_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - ASSERT_TRUE(CreateSymbolicLink(symlink_target, symlink_name_to)); - ASSERT_TRUE(PathExists(symlink_name_to)); - - // Check that copying fails. - EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false)); -} - -TEST_F(FileUtilTest, CopyDirectoryExclDirectoryOverSymlink) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_from)); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a subdirectory. - FilePath subdir_name_from = dir_name_from.Append(FILE_PATH_LITERAL("Subsub")); - CreateDirectory(subdir_name_from); - ASSERT_TRUE(PathExists(subdir_name_from)); - - // Create a destination directory with a symlink of the same name. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_to)); - ASSERT_TRUE(PathExists(dir_name_to)); - - FilePath symlink_target = dir_name_to.Append(FILE_PATH_LITERAL("Subsub")); - CreateTextFile(symlink_target, L"asdf"); - ASSERT_TRUE(PathExists(symlink_target)); - - FilePath symlink_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - ASSERT_TRUE(CreateSymbolicLink(symlink_target, symlink_name_to)); - ASSERT_TRUE(PathExists(symlink_name_to)); - - // Check that copying fails. - EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false)); -} - -TEST_F(FileUtilTest, CopyDirectoryExclFileOverDanglingSymlink) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_from)); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Create a destination directory with a dangling symlink of the same name. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_to)); - ASSERT_TRUE(PathExists(dir_name_to)); - - FilePath symlink_target = - dir_name_to.Append(FILE_PATH_LITERAL("Symlink_Target.txt")); - CreateTextFile(symlink_target, L"asdf"); - ASSERT_TRUE(PathExists(symlink_target)); - - FilePath symlink_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - ASSERT_TRUE(CreateSymbolicLink(symlink_target, symlink_name_to)); - ASSERT_TRUE(PathExists(symlink_name_to)); - ASSERT_TRUE(DeleteFile(symlink_target, false)); - - // Check that copying fails and that no file was created for the symlink's - // referent. - EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false)); - EXPECT_FALSE(PathExists(symlink_target)); -} - -TEST_F(FileUtilTest, CopyDirectoryExclDirectoryOverDanglingSymlink) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_from)); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a subdirectory. - FilePath subdir_name_from = dir_name_from.Append(FILE_PATH_LITERAL("Subsub")); - CreateDirectory(subdir_name_from); - ASSERT_TRUE(PathExists(subdir_name_from)); - - // Create a destination directory with a dangling symlink of the same name. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_to)); - ASSERT_TRUE(PathExists(dir_name_to)); - - FilePath symlink_target = - dir_name_to.Append(FILE_PATH_LITERAL("Symlink_Target.txt")); - CreateTextFile(symlink_target, L"asdf"); - ASSERT_TRUE(PathExists(symlink_target)); - - FilePath symlink_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - ASSERT_TRUE(CreateSymbolicLink(symlink_target, symlink_name_to)); - ASSERT_TRUE(PathExists(symlink_name_to)); - ASSERT_TRUE(DeleteFile(symlink_target, false)); - - // Check that copying fails and that no directory was created for the - // symlink's referent. - EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false)); - EXPECT_FALSE(PathExists(symlink_target)); -} - -TEST_F(FileUtilTest, CopyDirectoryExclFileOverFifo) { - // Create a directory. - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_from)); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory. - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Create a destination directory with a fifo of the same name. - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_To_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_to)); - ASSERT_TRUE(PathExists(dir_name_to)); - - FilePath fifo_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - ASSERT_EQ(0, mkfifo(fifo_name_to.value().c_str(), 0644)); - ASSERT_TRUE(PathExists(fifo_name_to)); - - // Check that copying fails. - EXPECT_FALSE(CopyDirectoryExcl(dir_name_from, dir_name_to, false)); -} -#endif // defined(OS_POSIX) - -TEST_F(FileUtilTest, CopyFile) { - // Create a directory - FilePath dir_name_from = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Copy_From_Subdir")); - ASSERT_TRUE(CreateDirectory(dir_name_from)); - ASSERT_TRUE(DirectoryExists(dir_name_from)); - - // Create a file under the directory - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); - const std::wstring file_contents(L"Gooooooooooooooooooooogle"); - CreateTextFile(file_name_from, file_contents); - ASSERT_TRUE(PathExists(file_name_from)); - - // Copy the file. - FilePath dest_file = dir_name_from.Append(FILE_PATH_LITERAL("DestFile.txt")); - ASSERT_TRUE(CopyFile(file_name_from, dest_file)); - - // Try to copy the file to another location using '..' in the path. - FilePath dest_file2(dir_name_from); - dest_file2 = dest_file2.AppendASCII(".."); - dest_file2 = dest_file2.AppendASCII("DestFile.txt"); - ASSERT_FALSE(CopyFile(file_name_from, dest_file2)); - - FilePath dest_file2_test(dir_name_from); - dest_file2_test = dest_file2_test.DirName(); - dest_file2_test = dest_file2_test.AppendASCII("DestFile.txt"); - - // Check expected copy results. - EXPECT_TRUE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(dest_file)); - EXPECT_EQ(file_contents, ReadTextFile(dest_file)); - EXPECT_FALSE(PathExists(dest_file2_test)); - EXPECT_FALSE(PathExists(dest_file2)); - - // Change |file_name_from| contents. - const std::wstring new_file_contents(L"Moogle"); - CreateTextFile(file_name_from, new_file_contents); - ASSERT_TRUE(PathExists(file_name_from)); - EXPECT_EQ(new_file_contents, ReadTextFile(file_name_from)); - - // Overwrite |dest_file|. - ASSERT_TRUE(CopyFile(file_name_from, dest_file)); - EXPECT_TRUE(PathExists(dest_file)); - EXPECT_EQ(new_file_contents, ReadTextFile(dest_file)); - - // Create another directory. - FilePath dest_dir = temp_dir_.GetPath().Append(FPL("dest_dir")); - ASSERT_TRUE(CreateDirectory(dest_dir)); - EXPECT_TRUE(DirectoryExists(dest_dir)); - EXPECT_TRUE(IsDirectoryEmpty(dest_dir)); - - // Make sure CopyFile() cannot overwrite a directory. - ASSERT_FALSE(CopyFile(file_name_from, dest_dir)); - EXPECT_TRUE(DirectoryExists(dest_dir)); - EXPECT_TRUE(IsDirectoryEmpty(dest_dir)); -} - -// file_util winds up using autoreleased objects on the Mac, so this needs -// to be a PlatformTest. -typedef PlatformTest ReadOnlyFileUtilTest; - -TEST_F(ReadOnlyFileUtilTest, ContentsEqual) { - FilePath data_dir; - ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir)); - data_dir = data_dir.AppendASCII("file_util"); - ASSERT_TRUE(PathExists(data_dir)); - - FilePath original_file = - data_dir.Append(FILE_PATH_LITERAL("original.txt")); - FilePath same_file = - data_dir.Append(FILE_PATH_LITERAL("same.txt")); - FilePath same_length_file = - data_dir.Append(FILE_PATH_LITERAL("same_length.txt")); - FilePath different_file = - data_dir.Append(FILE_PATH_LITERAL("different.txt")); - FilePath different_first_file = - data_dir.Append(FILE_PATH_LITERAL("different_first.txt")); - FilePath different_last_file = - data_dir.Append(FILE_PATH_LITERAL("different_last.txt")); - FilePath empty1_file = - data_dir.Append(FILE_PATH_LITERAL("empty1.txt")); - FilePath empty2_file = - data_dir.Append(FILE_PATH_LITERAL("empty2.txt")); - FilePath shortened_file = - data_dir.Append(FILE_PATH_LITERAL("shortened.txt")); - FilePath binary_file = - data_dir.Append(FILE_PATH_LITERAL("binary_file.bin")); - FilePath binary_file_same = - data_dir.Append(FILE_PATH_LITERAL("binary_file_same.bin")); - FilePath binary_file_diff = - data_dir.Append(FILE_PATH_LITERAL("binary_file_diff.bin")); - - EXPECT_TRUE(ContentsEqual(original_file, original_file)); - EXPECT_TRUE(ContentsEqual(original_file, same_file)); - EXPECT_FALSE(ContentsEqual(original_file, same_length_file)); - EXPECT_FALSE(ContentsEqual(original_file, different_file)); - EXPECT_FALSE(ContentsEqual(FilePath(FILE_PATH_LITERAL("bogusname")), - FilePath(FILE_PATH_LITERAL("bogusname")))); - EXPECT_FALSE(ContentsEqual(original_file, different_first_file)); - EXPECT_FALSE(ContentsEqual(original_file, different_last_file)); - EXPECT_TRUE(ContentsEqual(empty1_file, empty2_file)); - EXPECT_FALSE(ContentsEqual(original_file, shortened_file)); - EXPECT_FALSE(ContentsEqual(shortened_file, original_file)); - EXPECT_TRUE(ContentsEqual(binary_file, binary_file_same)); - EXPECT_FALSE(ContentsEqual(binary_file, binary_file_diff)); -} - -TEST_F(ReadOnlyFileUtilTest, TextContentsEqual) { - FilePath data_dir; - ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir)); - data_dir = data_dir.AppendASCII("file_util"); - ASSERT_TRUE(PathExists(data_dir)); - - FilePath original_file = - data_dir.Append(FILE_PATH_LITERAL("original.txt")); - FilePath same_file = - data_dir.Append(FILE_PATH_LITERAL("same.txt")); - FilePath crlf_file = - data_dir.Append(FILE_PATH_LITERAL("crlf.txt")); - FilePath shortened_file = - data_dir.Append(FILE_PATH_LITERAL("shortened.txt")); - FilePath different_file = - data_dir.Append(FILE_PATH_LITERAL("different.txt")); - FilePath different_first_file = - data_dir.Append(FILE_PATH_LITERAL("different_first.txt")); - FilePath different_last_file = - data_dir.Append(FILE_PATH_LITERAL("different_last.txt")); - FilePath first1_file = - data_dir.Append(FILE_PATH_LITERAL("first1.txt")); - FilePath first2_file = - data_dir.Append(FILE_PATH_LITERAL("first2.txt")); - FilePath empty1_file = - data_dir.Append(FILE_PATH_LITERAL("empty1.txt")); - FilePath empty2_file = - data_dir.Append(FILE_PATH_LITERAL("empty2.txt")); - FilePath blank_line_file = - data_dir.Append(FILE_PATH_LITERAL("blank_line.txt")); - FilePath blank_line_crlf_file = - data_dir.Append(FILE_PATH_LITERAL("blank_line_crlf.txt")); - - EXPECT_TRUE(TextContentsEqual(original_file, same_file)); - EXPECT_TRUE(TextContentsEqual(original_file, crlf_file)); - EXPECT_FALSE(TextContentsEqual(original_file, shortened_file)); - EXPECT_FALSE(TextContentsEqual(original_file, different_file)); - EXPECT_FALSE(TextContentsEqual(original_file, different_first_file)); - EXPECT_FALSE(TextContentsEqual(original_file, different_last_file)); - EXPECT_FALSE(TextContentsEqual(first1_file, first2_file)); - EXPECT_TRUE(TextContentsEqual(empty1_file, empty2_file)); - EXPECT_FALSE(TextContentsEqual(original_file, empty1_file)); - EXPECT_TRUE(TextContentsEqual(blank_line_file, blank_line_crlf_file)); -} - -// We don't need equivalent functionality outside of Windows. -#if defined(OS_WIN) -TEST_F(FileUtilTest, CopyAndDeleteDirectoryTest) { - // Create a directory - FilePath dir_name_from = temp_dir_.GetPath().Append( - FILE_PATH_LITERAL("CopyAndDelete_From_Subdir")); - CreateDirectory(dir_name_from); - ASSERT_TRUE(PathExists(dir_name_from)); - - // Create a file under the directory - FilePath file_name_from = - dir_name_from.Append(FILE_PATH_LITERAL("CopyAndDelete_Test_File.txt")); - CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); - ASSERT_TRUE(PathExists(file_name_from)); - - // Move the directory by using CopyAndDeleteDirectory - FilePath dir_name_to = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("CopyAndDelete_To_Subdir")); - FilePath file_name_to = - dir_name_to.Append(FILE_PATH_LITERAL("CopyAndDelete_Test_File.txt")); - - ASSERT_FALSE(PathExists(dir_name_to)); - - EXPECT_TRUE(internal::CopyAndDeleteDirectory(dir_name_from, - dir_name_to)); - - // Check everything has been moved. - EXPECT_FALSE(PathExists(dir_name_from)); - EXPECT_FALSE(PathExists(file_name_from)); - EXPECT_TRUE(PathExists(dir_name_to)); - EXPECT_TRUE(PathExists(file_name_to)); -} - -TEST_F(FileUtilTest, GetTempDirTest) { - static const TCHAR* kTmpKey = _T("TMP"); - static const TCHAR* kTmpValues[] = { - _T(""), _T("C:"), _T("C:\\"), _T("C:\\tmp"), _T("C:\\tmp\\") - }; - // Save the original $TMP. - size_t original_tmp_size; - TCHAR* original_tmp; - ASSERT_EQ(0, ::_tdupenv_s(&original_tmp, &original_tmp_size, kTmpKey)); - // original_tmp may be NULL. - - for (unsigned int i = 0; i < arraysize(kTmpValues); ++i) { - FilePath path; - ::_tputenv_s(kTmpKey, kTmpValues[i]); - GetTempDir(&path); - EXPECT_TRUE(path.IsAbsolute()) << "$TMP=" << kTmpValues[i] << - " result=" << path.value(); - } - - // Restore the original $TMP. - if (original_tmp) { - ::_tputenv_s(kTmpKey, original_tmp); - free(original_tmp); - } else { - ::_tputenv_s(kTmpKey, _T("")); - } -} -#endif // OS_WIN - -// Test that files opened by OpenFile are not set up for inheritance into child -// procs. -TEST_F(FileUtilTest, OpenFileNoInheritance) { - FilePath file_path(temp_dir_.GetPath().Append(FPL("a_file"))); - - for (const char* mode : {"wb", "r,ccs=UTF-8"}) { - SCOPED_TRACE(mode); - ASSERT_NO_FATAL_FAILURE(CreateTextFile(file_path, L"Geepers")); - FILE* file = OpenFile(file_path, mode); - ASSERT_NE(nullptr, file); - { - ScopedClosureRunner file_closer(Bind(IgnoreResult(&CloseFile), file)); - bool is_inheritable = true; - ASSERT_NO_FATAL_FAILURE(GetIsInheritable(file, &is_inheritable)); - EXPECT_FALSE(is_inheritable); - } - ASSERT_TRUE(DeleteFile(file_path, false)); - } -} - -TEST_F(FileUtilTest, CreateTemporaryFileTest) { - FilePath temp_files[3]; - for (int i = 0; i < 3; i++) { - ASSERT_TRUE(CreateTemporaryFile(&(temp_files[i]))); - EXPECT_TRUE(PathExists(temp_files[i])); - EXPECT_FALSE(DirectoryExists(temp_files[i])); - } - for (int i = 0; i < 3; i++) - EXPECT_FALSE(temp_files[i] == temp_files[(i+1)%3]); - for (int i = 0; i < 3; i++) - EXPECT_TRUE(DeleteFile(temp_files[i], false)); -} - -TEST_F(FileUtilTest, CreateAndOpenTemporaryFileTest) { - FilePath names[3]; - FILE* fps[3]; - int i; - - // Create; make sure they are open and exist. - for (i = 0; i < 3; ++i) { - fps[i] = CreateAndOpenTemporaryFile(&(names[i])); - ASSERT_TRUE(fps[i]); - EXPECT_TRUE(PathExists(names[i])); - } - - // Make sure all names are unique. - for (i = 0; i < 3; ++i) { - EXPECT_FALSE(names[i] == names[(i+1)%3]); - } - - // Close and delete. - for (i = 0; i < 3; ++i) { - EXPECT_TRUE(CloseFile(fps[i])); - EXPECT_TRUE(DeleteFile(names[i], false)); - } -} - -#if defined(OS_FUCHSIA) -// TODO(crbug.com/851747): Re-enable when the Fuchsia-side fix for fdopen has -// been rolled into Chromium. -#define MAYBE_FileToFILE DISABLED_FileToFILE -#else -#define MAYBE_FileToFILE FileToFILE -#endif -TEST_F(FileUtilTest, MAYBE_FileToFILE) { - File file; - FILE* stream = FileToFILE(std::move(file), "w"); - EXPECT_FALSE(stream); - - FilePath file_name = temp_dir_.GetPath().Append(FPL("The file.txt")); - file = File(file_name, File::FLAG_CREATE | File::FLAG_WRITE); - EXPECT_TRUE(file.IsValid()); - - stream = FileToFILE(std::move(file), "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)); - EXPECT_TRUE(PathExists(temp_dir)); - EXPECT_TRUE(DeleteFile(temp_dir, false)); -} - -TEST_F(FileUtilTest, CreateNewTemporaryDirInDirTest) { - FilePath new_dir; - ASSERT_TRUE(CreateTemporaryDirInDir( - temp_dir_.GetPath(), FILE_PATH_LITERAL("CreateNewTemporaryDirInDirTest"), - &new_dir)); - EXPECT_TRUE(PathExists(new_dir)); - EXPECT_TRUE(temp_dir_.GetPath().IsParent(new_dir)); - EXPECT_TRUE(DeleteFile(new_dir, false)); -} - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -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 = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("create_directory_test")); -#if defined(OS_WIN) - FilePath test_path = - test_root.Append(FILE_PATH_LITERAL("dir\\tree\\likely\\doesnt\\exist\\")); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - FilePath test_path = - test_root.Append(FILE_PATH_LITERAL("dir/tree/likely/doesnt/exist/")); -#endif - - EXPECT_FALSE(PathExists(test_path)); - EXPECT_TRUE(CreateDirectory(test_path)); - EXPECT_TRUE(PathExists(test_path)); - // CreateDirectory returns true if the DirectoryExists returns true. - EXPECT_TRUE(CreateDirectory(test_path)); - - // Doesn't work to create it on top of a non-dir - test_path = test_path.Append(FILE_PATH_LITERAL("foobar.txt")); - EXPECT_FALSE(PathExists(test_path)); - CreateTextFile(test_path, L"test file"); - EXPECT_TRUE(PathExists(test_path)); - EXPECT_FALSE(CreateDirectory(test_path)); - - EXPECT_TRUE(DeleteFile(test_root, true)); - EXPECT_FALSE(PathExists(test_root)); - EXPECT_FALSE(PathExists(test_path)); - - // Verify assumptions made by the Windows implementation: - // 1. The current directory always exists. - // 2. The root directory always exists. - ASSERT_TRUE(DirectoryExists(FilePath(FilePath::kCurrentDirectory))); - FilePath top_level = test_root; - while (top_level != top_level.DirName()) { - top_level = top_level.DirName(); - } - ASSERT_TRUE(DirectoryExists(top_level)); - - // Given these assumptions hold, it should be safe to - // test that "creating" these directories succeeds. - EXPECT_TRUE(CreateDirectory( - FilePath(FilePath::kCurrentDirectory))); - EXPECT_TRUE(CreateDirectory(top_level)); - -#if defined(OS_WIN) - FilePath invalid_drive(FILE_PATH_LITERAL("o:\\")); - FilePath invalid_path = - invalid_drive.Append(FILE_PATH_LITERAL("some\\inaccessible\\dir")); - if (!PathExists(invalid_drive)) { - EXPECT_FALSE(CreateDirectory(invalid_path)); - } -#endif -} - -TEST_F(FileUtilTest, DetectDirectoryTest) { - // Check a directory - FilePath test_root = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("detect_directory_test")); - EXPECT_FALSE(PathExists(test_root)); - EXPECT_TRUE(CreateDirectory(test_root)); - EXPECT_TRUE(PathExists(test_root)); - EXPECT_TRUE(DirectoryExists(test_root)); - // Check a file - FilePath test_path = - test_root.Append(FILE_PATH_LITERAL("foobar.txt")); - EXPECT_FALSE(PathExists(test_path)); - CreateTextFile(test_path, L"test file"); - EXPECT_TRUE(PathExists(test_path)); - EXPECT_FALSE(DirectoryExists(test_path)); - EXPECT_TRUE(DeleteFile(test_path, false)); - - EXPECT_TRUE(DeleteFile(test_root, true)); -} - -TEST_F(FileUtilTest, FileEnumeratorTest) { - // Test an empty directory. - FileEnumerator f0(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES); - EXPECT_EQ(FPL(""), f0.Next().value()); - EXPECT_EQ(FPL(""), f0.Next().value()); - - // Test an empty directory, non-recursively, including "..". - FileEnumerator f0_dotdot( - temp_dir_.GetPath(), false, - FILES_AND_DIRECTORIES | FileEnumerator::INCLUDE_DOT_DOT); - EXPECT_EQ(temp_dir_.GetPath().Append(FPL("..")).value(), - f0_dotdot.Next().value()); - EXPECT_EQ(FPL(""), f0_dotdot.Next().value()); - - // create the directories - FilePath dir1 = temp_dir_.GetPath().Append(FPL("dir1")); - EXPECT_TRUE(CreateDirectory(dir1)); - FilePath dir2 = temp_dir_.GetPath().Append(FPL("dir2")); - EXPECT_TRUE(CreateDirectory(dir2)); - FilePath dir2inner = dir2.Append(FPL("inner")); - EXPECT_TRUE(CreateDirectory(dir2inner)); - - // create the files - FilePath dir2file = dir2.Append(FPL("dir2file.txt")); - CreateTextFile(dir2file, std::wstring()); - FilePath dir2innerfile = dir2inner.Append(FPL("innerfile.txt")); - CreateTextFile(dir2innerfile, std::wstring()); - FilePath file1 = temp_dir_.GetPath().Append(FPL("file1.txt")); - CreateTextFile(file1, std::wstring()); - FilePath file2_rel = dir2.Append(FilePath::kParentDirectory) - .Append(FPL("file2.txt")); - CreateTextFile(file2_rel, std::wstring()); - FilePath file2_abs = temp_dir_.GetPath().Append(FPL("file2.txt")); - - // Only enumerate files. - FileEnumerator f1(temp_dir_.GetPath(), true, FileEnumerator::FILES); - FindResultCollector c1(&f1); - EXPECT_TRUE(c1.HasFile(file1)); - EXPECT_TRUE(c1.HasFile(file2_abs)); - EXPECT_TRUE(c1.HasFile(dir2file)); - EXPECT_TRUE(c1.HasFile(dir2innerfile)); - EXPECT_EQ(4, c1.size()); - - // Only enumerate directories. - FileEnumerator f2(temp_dir_.GetPath(), true, FileEnumerator::DIRECTORIES); - FindResultCollector c2(&f2); - EXPECT_TRUE(c2.HasFile(dir1)); - EXPECT_TRUE(c2.HasFile(dir2)); - EXPECT_TRUE(c2.HasFile(dir2inner)); - EXPECT_EQ(3, c2.size()); - - // Only enumerate directories non-recursively. - FileEnumerator f2_non_recursive(temp_dir_.GetPath(), false, - FileEnumerator::DIRECTORIES); - FindResultCollector c2_non_recursive(&f2_non_recursive); - EXPECT_TRUE(c2_non_recursive.HasFile(dir1)); - EXPECT_TRUE(c2_non_recursive.HasFile(dir2)); - EXPECT_EQ(2, c2_non_recursive.size()); - - // Only enumerate directories, non-recursively, including "..". - FileEnumerator f2_dotdot( - temp_dir_.GetPath(), false, - FileEnumerator::DIRECTORIES | FileEnumerator::INCLUDE_DOT_DOT); - FindResultCollector c2_dotdot(&f2_dotdot); - EXPECT_TRUE(c2_dotdot.HasFile(dir1)); - EXPECT_TRUE(c2_dotdot.HasFile(dir2)); - EXPECT_TRUE(c2_dotdot.HasFile(temp_dir_.GetPath().Append(FPL("..")))); - EXPECT_EQ(3, c2_dotdot.size()); - - // Enumerate files and directories. - FileEnumerator f3(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES); - FindResultCollector c3(&f3); - EXPECT_TRUE(c3.HasFile(dir1)); - EXPECT_TRUE(c3.HasFile(dir2)); - EXPECT_TRUE(c3.HasFile(file1)); - EXPECT_TRUE(c3.HasFile(file2_abs)); - EXPECT_TRUE(c3.HasFile(dir2file)); - EXPECT_TRUE(c3.HasFile(dir2inner)); - EXPECT_TRUE(c3.HasFile(dir2innerfile)); - EXPECT_EQ(7, c3.size()); - - // Non-recursive operation. - FileEnumerator f4(temp_dir_.GetPath(), false, FILES_AND_DIRECTORIES); - FindResultCollector c4(&f4); - EXPECT_TRUE(c4.HasFile(dir2)); - EXPECT_TRUE(c4.HasFile(dir2)); - EXPECT_TRUE(c4.HasFile(file1)); - EXPECT_TRUE(c4.HasFile(file2_abs)); - EXPECT_EQ(4, c4.size()); - - // Enumerate with a pattern. - FileEnumerator f5(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES, - FPL("dir*")); - FindResultCollector c5(&f5); - EXPECT_TRUE(c5.HasFile(dir1)); - EXPECT_TRUE(c5.HasFile(dir2)); - EXPECT_TRUE(c5.HasFile(dir2file)); - EXPECT_TRUE(c5.HasFile(dir2inner)); - EXPECT_TRUE(c5.HasFile(dir2innerfile)); - EXPECT_EQ(5, c5.size()); - -#if defined(OS_WIN) - { - // Make dir1 point to dir2. - ReparsePoint reparse_point(dir1, dir2); - EXPECT_TRUE(reparse_point.IsValid()); - - // There can be a delay for the enumeration code to see the change on - // the file system so skip this test for XP. - // Enumerate the reparse point. - FileEnumerator f6(dir1, true, FILES_AND_DIRECTORIES); - FindResultCollector c6(&f6); - FilePath inner2 = dir1.Append(FPL("inner")); - 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(3, c6.size()); - - // No changes for non recursive operation. - FileEnumerator f7(temp_dir_.GetPath(), false, FILES_AND_DIRECTORIES); - FindResultCollector c7(&f7); - EXPECT_TRUE(c7.HasFile(dir2)); - EXPECT_TRUE(c7.HasFile(dir2)); - EXPECT_TRUE(c7.HasFile(file1)); - EXPECT_TRUE(c7.HasFile(file2_abs)); - EXPECT_EQ(4, c7.size()); - - // Should not enumerate inside dir1 when using recursion. - FileEnumerator f8(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES); - FindResultCollector c8(&f8); - EXPECT_TRUE(c8.HasFile(dir1)); - EXPECT_TRUE(c8.HasFile(dir2)); - EXPECT_TRUE(c8.HasFile(file1)); - EXPECT_TRUE(c8.HasFile(file2_abs)); - EXPECT_TRUE(c8.HasFile(dir2file)); - EXPECT_TRUE(c8.HasFile(dir2inner)); - EXPECT_TRUE(c8.HasFile(dir2innerfile)); - EXPECT_EQ(7, c8.size()); - } -#endif - - // Make sure the destructor closes the find handle while in the middle of a - // query to allow TearDown to delete the directory. - FileEnumerator f9(temp_dir_.GetPath(), true, FILES_AND_DIRECTORIES); - EXPECT_FALSE(f9.Next().value().empty()); // Should have found something - // (we don't care what). -} - -TEST_F(FileUtilTest, AppendToFile) { - FilePath data_dir = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("FilePathTest")); - - // Create a fresh, empty copy of this directory. - if (PathExists(data_dir)) { - ASSERT_TRUE(DeleteFile(data_dir, true)); - } - ASSERT_TRUE(CreateDirectory(data_dir)); - - // Create a fresh, empty copy of this directory. - if (PathExists(data_dir)) { - ASSERT_TRUE(DeleteFile(data_dir, true)); - } - ASSERT_TRUE(CreateDirectory(data_dir)); - FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt"))); - - std::string data("hello"); - EXPECT_FALSE(AppendToFile(foobar, data.c_str(), data.size())); - EXPECT_EQ(static_cast(data.length()), - WriteFile(foobar, data.c_str(), data.length())); - EXPECT_TRUE(AppendToFile(foobar, data.c_str(), data.size())); - - 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_.GetPath().Append(FILE_PATH_LITERAL("ReadFileTest")); - - ASSERT_EQ(static_cast(kTestData.size()), - WriteFile(file_path, kTestData.data(), kTestData.size())); - - // Make buffers with various size. - std::vector small_buffer(kTestData.size() / 2); - std::vector exact_buffer(kTestData.size()); - std::vector large_buffer(kTestData.size() * 2); - - // Read the file with smaller buffer. - int bytes_read_small = ReadFile( - file_path, &small_buffer[0], static_cast(small_buffer.size())); - EXPECT_EQ(static_cast(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(exact_buffer.size())); - EXPECT_EQ(static_cast(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(large_buffer.size())); - EXPECT_EQ(static_cast(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_.GetPath().Append(FILE_PATH_LITERAL("ReadFileNotExistTest")); - EXPECT_EQ(-1, - ReadFile(file_path_not_exist, - &exact_buffer[0], - static_cast(exact_buffer.size()))); -} - -TEST_F(FileUtilTest, ReadFileToString) { - const char kTestData[] = "0123"; - std::string data; - - FilePath file_path = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("ReadFileToStringTest")); - FilePath file_path_dangerous = - temp_dir_.GetPath() - .Append(FILE_PATH_LITERAL("..")) - .Append(temp_dir_.GetPath().BaseName()) - .Append(FILE_PATH_LITERAL("ReadFileToStringTest")); - - // Create test file. - ASSERT_EQ(static_cast(strlen(kTestData)), - WriteFile(file_path, kTestData, strlen(kTestData))); - - EXPECT_TRUE(ReadFileToString(file_path, &data)); - EXPECT_EQ(kTestData, data); - - data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 0)); - EXPECT_EQ(0u, data.length()); - - data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 2)); - EXPECT_EQ("01", data); - - data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 3)); - EXPECT_EQ("012", data); - - data = "temp"; - EXPECT_TRUE(ReadFileToStringWithMaxSize(file_path, &data, 4)); - EXPECT_EQ("0123", data); - - data = "temp"; - EXPECT_TRUE(ReadFileToStringWithMaxSize(file_path, &data, 6)); - EXPECT_EQ("0123", data); - - EXPECT_TRUE(ReadFileToStringWithMaxSize(file_path, nullptr, 6)); - - EXPECT_TRUE(ReadFileToString(file_path, nullptr)); - - data = "temp"; - EXPECT_FALSE(ReadFileToString(file_path_dangerous, &data)); - EXPECT_EQ(0u, data.length()); - - // Delete test file. - EXPECT_TRUE(DeleteFile(file_path, false)); - - data = "temp"; - EXPECT_FALSE(ReadFileToString(file_path, &data)); - EXPECT_EQ(0u, data.length()); - - data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 6)); - EXPECT_EQ(0u, data.length()); -} - -#if !defined(OS_WIN) -TEST_F(FileUtilTest, ReadFileToStringWithUnknownFileSize) { - FilePath file_path("/dev/zero"); - std::string data = "temp"; - - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 0)); - EXPECT_EQ(0u, data.length()); - - data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 2)); - EXPECT_EQ(std::string(2, '\0'), data); - - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, nullptr, 6)); - - // Read more than buffer size. - data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, kLargeFileSize)); - EXPECT_EQ(kLargeFileSize, data.length()); - EXPECT_EQ(std::string(kLargeFileSize, '\0'), data); - - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, nullptr, kLargeFileSize)); -} -#endif // !defined(OS_WIN) - -#if !defined(OS_WIN) && !defined(OS_NACL) && !defined(OS_FUCHSIA) && \ - !defined(OS_IOS) -#define ChildMain WriteToPipeChildMain -#define ChildMainString "WriteToPipeChildMain" - -MULTIPROCESS_TEST_MAIN(ChildMain) { - const char kTestData[] = "0123"; - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - const FilePath pipe_path = command_line->GetSwitchValuePath("pipe-path"); - - int fd = open(pipe_path.value().c_str(), O_WRONLY); - CHECK_NE(-1, fd); - size_t written = 0; - while (written < strlen(kTestData)) { - ssize_t res = write(fd, kTestData + written, strlen(kTestData) - written); - if (res == -1) - break; - written += res; - } - CHECK_EQ(strlen(kTestData), written); - CHECK_EQ(0, close(fd)); - return 0; -} - -#define MoreThanBufferSizeChildMain WriteToPipeMoreThanBufferSizeChildMain -#define MoreThanBufferSizeChildMainString \ - "WriteToPipeMoreThanBufferSizeChildMain" - -MULTIPROCESS_TEST_MAIN(MoreThanBufferSizeChildMain) { - std::string data(kLargeFileSize, 'c'); - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - const FilePath pipe_path = command_line->GetSwitchValuePath("pipe-path"); - - int fd = open(pipe_path.value().c_str(), O_WRONLY); - CHECK_NE(-1, fd); - - size_t written = 0; - while (written < data.size()) { - ssize_t res = write(fd, data.c_str() + written, data.size() - written); - if (res == -1) { - // We are unable to write because reading process has already read - // requested number of bytes and closed pipe. - break; - } - written += res; - } - CHECK_EQ(0, close(fd)); - return 0; -} - -TEST_F(FileUtilTest, ReadFileToStringWithNamedPipe) { - FilePath pipe_path = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("test_pipe")); - ASSERT_EQ(0, mkfifo(pipe_path.value().c_str(), 0600)); - - base::CommandLine child_command_line( - base::GetMultiProcessTestChildBaseCommandLine()); - child_command_line.AppendSwitchPath("pipe-path", pipe_path); - - { - base::Process child_process = base::SpawnMultiProcessTestChild( - ChildMainString, child_command_line, base::LaunchOptions()); - ASSERT_TRUE(child_process.IsValid()); - - std::string data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(pipe_path, &data, 2)); - EXPECT_EQ("01", data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - { - base::Process child_process = base::SpawnMultiProcessTestChild( - ChildMainString, child_command_line, base::LaunchOptions()); - ASSERT_TRUE(child_process.IsValid()); - - std::string data = "temp"; - EXPECT_TRUE(ReadFileToStringWithMaxSize(pipe_path, &data, 6)); - EXPECT_EQ("0123", data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - { - base::Process child_process = base::SpawnMultiProcessTestChild( - MoreThanBufferSizeChildMainString, child_command_line, - base::LaunchOptions()); - ASSERT_TRUE(child_process.IsValid()); - - std::string data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(pipe_path, &data, 6)); - EXPECT_EQ("cccccc", data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - { - base::Process child_process = base::SpawnMultiProcessTestChild( - MoreThanBufferSizeChildMainString, child_command_line, - base::LaunchOptions()); - ASSERT_TRUE(child_process.IsValid()); - - std::string data = "temp"; - EXPECT_FALSE( - ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize - 1)); - EXPECT_EQ(std::string(kLargeFileSize - 1, 'c'), data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - { - base::Process child_process = base::SpawnMultiProcessTestChild( - MoreThanBufferSizeChildMainString, child_command_line, - base::LaunchOptions()); - ASSERT_TRUE(child_process.IsValid()); - - std::string data = "temp"; - EXPECT_TRUE(ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize)); - EXPECT_EQ(std::string(kLargeFileSize, 'c'), data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - { - base::Process child_process = base::SpawnMultiProcessTestChild( - MoreThanBufferSizeChildMainString, child_command_line, - base::LaunchOptions()); - ASSERT_TRUE(child_process.IsValid()); - - std::string data = "temp"; - EXPECT_TRUE( - ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize * 5)); - EXPECT_EQ(std::string(kLargeFileSize, 'c'), data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - - ASSERT_EQ(0, unlink(pipe_path.value().c_str())); -} -#endif // !defined(OS_WIN) && !defined(OS_NACL) && !defined(OS_FUCHSIA) && - // !defined(OS_IOS) - -#if defined(OS_WIN) -#define ChildMain WriteToPipeChildMain -#define ChildMainString "WriteToPipeChildMain" - -MULTIPROCESS_TEST_MAIN(ChildMain) { - const char kTestData[] = "0123"; - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - const FilePath pipe_path = command_line->GetSwitchValuePath("pipe-path"); - std::string switch_string = command_line->GetSwitchValueASCII("sync_event"); - EXPECT_FALSE(switch_string.empty()); - unsigned int switch_uint = 0; - EXPECT_TRUE(StringToUint(switch_string, &switch_uint)); - win::ScopedHandle sync_event(win::Uint32ToHandle(switch_uint)); - - HANDLE ph = CreateNamedPipe(pipe_path.value().c_str(), PIPE_ACCESS_OUTBOUND, - PIPE_WAIT, 1, 0, 0, 0, NULL); - EXPECT_NE(ph, INVALID_HANDLE_VALUE); - EXPECT_TRUE(SetEvent(sync_event.Get())); - EXPECT_TRUE(ConnectNamedPipe(ph, NULL)); - - DWORD written; - EXPECT_TRUE(::WriteFile(ph, kTestData, strlen(kTestData), &written, NULL)); - EXPECT_EQ(strlen(kTestData), written); - CloseHandle(ph); - return 0; -} - -#define MoreThanBufferSizeChildMain WriteToPipeMoreThanBufferSizeChildMain -#define MoreThanBufferSizeChildMainString \ - "WriteToPipeMoreThanBufferSizeChildMain" - -MULTIPROCESS_TEST_MAIN(MoreThanBufferSizeChildMain) { - std::string data(kLargeFileSize, 'c'); - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - const FilePath pipe_path = command_line->GetSwitchValuePath("pipe-path"); - std::string switch_string = command_line->GetSwitchValueASCII("sync_event"); - EXPECT_FALSE(switch_string.empty()); - unsigned int switch_uint = 0; - EXPECT_TRUE(StringToUint(switch_string, &switch_uint)); - win::ScopedHandle sync_event(win::Uint32ToHandle(switch_uint)); - - HANDLE ph = CreateNamedPipe(pipe_path.value().c_str(), PIPE_ACCESS_OUTBOUND, - PIPE_WAIT, 1, data.size(), data.size(), 0, NULL); - EXPECT_NE(ph, INVALID_HANDLE_VALUE); - EXPECT_TRUE(SetEvent(sync_event.Get())); - EXPECT_TRUE(ConnectNamedPipe(ph, NULL)); - - DWORD written; - EXPECT_TRUE(::WriteFile(ph, data.c_str(), data.size(), &written, NULL)); - EXPECT_EQ(data.size(), written); - CloseHandle(ph); - return 0; -} - -TEST_F(FileUtilTest, ReadFileToStringWithNamedPipe) { - FilePath pipe_path(FILE_PATH_LITERAL("\\\\.\\pipe\\test_pipe")); - win::ScopedHandle sync_event(CreateEvent(0, false, false, nullptr)); - - base::CommandLine child_command_line( - base::GetMultiProcessTestChildBaseCommandLine()); - child_command_line.AppendSwitchPath("pipe-path", pipe_path); - child_command_line.AppendSwitchASCII( - "sync_event", UintToString(win::HandleToUint32(sync_event.Get()))); - - base::LaunchOptions options; - options.handles_to_inherit.push_back(sync_event.Get()); - - { - base::Process child_process = base::SpawnMultiProcessTestChild( - ChildMainString, child_command_line, options); - ASSERT_TRUE(child_process.IsValid()); - // Wait for pipe creation in child process. - EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE)); - - std::string data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(pipe_path, &data, 2)); - EXPECT_EQ("01", data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - { - base::Process child_process = base::SpawnMultiProcessTestChild( - ChildMainString, child_command_line, options); - ASSERT_TRUE(child_process.IsValid()); - // Wait for pipe creation in child process. - EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE)); - - std::string data = "temp"; - EXPECT_TRUE(ReadFileToStringWithMaxSize(pipe_path, &data, 6)); - EXPECT_EQ("0123", data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - { - base::Process child_process = base::SpawnMultiProcessTestChild( - MoreThanBufferSizeChildMainString, child_command_line, options); - ASSERT_TRUE(child_process.IsValid()); - // Wait for pipe creation in child process. - EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE)); - - std::string data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(pipe_path, &data, 6)); - EXPECT_EQ("cccccc", data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - { - base::Process child_process = base::SpawnMultiProcessTestChild( - MoreThanBufferSizeChildMainString, child_command_line, options); - ASSERT_TRUE(child_process.IsValid()); - // Wait for pipe creation in child process. - EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE)); - - std::string data = "temp"; - EXPECT_FALSE( - ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize - 1)); - EXPECT_EQ(std::string(kLargeFileSize - 1, 'c'), data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - { - base::Process child_process = base::SpawnMultiProcessTestChild( - MoreThanBufferSizeChildMainString, child_command_line, options); - ASSERT_TRUE(child_process.IsValid()); - // Wait for pipe creation in child process. - EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE)); - - std::string data = "temp"; - EXPECT_TRUE(ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize)); - EXPECT_EQ(std::string(kLargeFileSize, 'c'), data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } - { - base::Process child_process = base::SpawnMultiProcessTestChild( - MoreThanBufferSizeChildMainString, child_command_line, options); - ASSERT_TRUE(child_process.IsValid()); - // Wait for pipe creation in child process. - EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(sync_event.Get(), INFINITE)); - - std::string data = "temp"; - EXPECT_TRUE( - ReadFileToStringWithMaxSize(pipe_path, &data, kLargeFileSize * 5)); - EXPECT_EQ(std::string(kLargeFileSize, 'c'), data); - - int rv = -1; - ASSERT_TRUE(WaitForMultiprocessTestChildExit( - child_process, TestTimeouts::action_timeout(), &rv)); - ASSERT_EQ(0, rv); - } -} -#endif // defined(OS_WIN) - -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_FUCHSIA) -TEST_F(FileUtilTest, ReadFileToStringWithProcFileSystem) { - FilePath file_path("/proc/cpuinfo"); - std::string data = "temp"; - - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 0)); - EXPECT_EQ(0u, data.length()); - - data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 2)); - EXPECT_TRUE(EqualsCaseInsensitiveASCII("pr", data)); - - data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &data, 4)); - EXPECT_TRUE(EqualsCaseInsensitiveASCII("proc", data)); - - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, nullptr, 4)); -} -#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_FUCHSIA) - -TEST_F(FileUtilTest, ReadFileToStringWithLargeFile) { - std::string data(kLargeFileSize, 'c'); - - FilePath file_path = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("ReadFileToStringTest")); - - // Create test file. - ASSERT_EQ(static_cast(kLargeFileSize), - WriteFile(file_path, data.c_str(), kLargeFileSize)); - - std::string actual_data = "temp"; - EXPECT_TRUE(ReadFileToString(file_path, &actual_data)); - EXPECT_EQ(data, actual_data); - - actual_data = "temp"; - EXPECT_FALSE(ReadFileToStringWithMaxSize(file_path, &actual_data, 0)); - EXPECT_EQ(0u, actual_data.length()); - - // Read more than buffer size. - actual_data = "temp"; - EXPECT_FALSE( - ReadFileToStringWithMaxSize(file_path, &actual_data, kLargeFileSize - 1)); - EXPECT_EQ(std::string(kLargeFileSize - 1, 'c'), actual_data); -} - -TEST_F(FileUtilTest, TouchFile) { - FilePath data_dir = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("FilePathTest")); - - // Create a fresh, empty copy of this directory. - if (PathExists(data_dir)) { - ASSERT_TRUE(DeleteFile(data_dir, true)); - } - ASSERT_TRUE(CreateDirectory(data_dir)); - - FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt"))); - std::string data("hello"); - ASSERT_EQ(static_cast(data.length()), - 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)); - - Time modification_time; - // Note that this timestamp is divisible by two (seconds) - FAT stores - // modification times with 2s resolution. - ASSERT_TRUE(Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT", - &modification_time)); - - ASSERT_TRUE(TouchFile(foobar, access_time, modification_time)); - File::Info file_info; - ASSERT_TRUE(GetFileInfo(foobar, &file_info)); -#if !defined(OS_FUCHSIA) - // Access time is not supported on Fuchsia, see https://crbug.com/735233. - EXPECT_EQ(access_time.ToInternalValue(), - file_info.last_accessed.ToInternalValue()); -#endif - EXPECT_EQ(modification_time.ToInternalValue(), - file_info.last_modified.ToInternalValue()); -} - -TEST_F(FileUtilTest, IsDirectoryEmpty) { - FilePath empty_dir = - temp_dir_.GetPath().Append(FILE_PATH_LITERAL("EmptyDir")); - - ASSERT_FALSE(PathExists(empty_dir)); - - ASSERT_TRUE(CreateDirectory(empty_dir)); - - EXPECT_TRUE(IsDirectoryEmpty(empty_dir)); - - FilePath foo(empty_dir.Append(FILE_PATH_LITERAL("foo.txt"))); - std::string bar("baz"); - ASSERT_EQ(static_cast(bar.length()), - WriteFile(foo, bar.c_str(), bar.length())); - - EXPECT_FALSE(IsDirectoryEmpty(empty_dir)); -} - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - -TEST_F(FileUtilTest, SetNonBlocking) { - const int kInvalidFd = 99999; - EXPECT_FALSE(SetNonBlocking(kInvalidFd)); - - base::FilePath path; - ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &path)); - path = path.Append(FPL("file_util")).Append(FPL("original.txt")); - ScopedFD fd(open(path.value().c_str(), O_RDONLY)); - ASSERT_GE(fd.get(), 0); - EXPECT_TRUE(SetNonBlocking(fd.get())); -} - -TEST_F(FileUtilTest, SetCloseOnExec) { - const int kInvalidFd = 99999; - EXPECT_FALSE(SetCloseOnExec(kInvalidFd)); - - base::FilePath path; - ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &path)); - path = path.Append(FPL("file_util")).Append(FPL("original.txt")); - ScopedFD fd(open(path.value().c_str(), O_RDONLY)); - ASSERT_GE(fd.get(), 0); - EXPECT_TRUE(SetCloseOnExec(fd.get())); -} - -#endif - -#if defined(OS_POSIX) - -// Testing VerifyPathControlledByAdmin() is hard, because there is no -// way a test can make a file owned by root, or change file paths -// at the root of the file system. VerifyPathControlledByAdmin() -// is implemented as a call to VerifyPathControlledByUser, which gives -// us the ability to test with paths under the test's temp directory, -// using a user id we control. -// Pull tests of VerifyPathControlledByUserTest() into a separate test class -// with a common SetUp() method. -class VerifyPathControlledByUserTest : public FileUtilTest { - protected: - void SetUp() override { - FileUtilTest::SetUp(); - - // Create a basic structure used by each test. - // base_dir_ - // |-> sub_dir_ - // |-> text_file_ - - base_dir_ = temp_dir_.GetPath().AppendASCII("base_dir"); - ASSERT_TRUE(CreateDirectory(base_dir_)); - - sub_dir_ = base_dir_.AppendASCII("sub_dir"); - ASSERT_TRUE(CreateDirectory(sub_dir_)); - - text_file_ = sub_dir_.AppendASCII("file.txt"); - CreateTextFile(text_file_, L"This text file has some text in it."); - - // Get the user and group files are created with from |base_dir_|. - struct stat stat_buf; - ASSERT_EQ(0, stat(base_dir_.value().c_str(), &stat_buf)); - uid_ = stat_buf.st_uid; - ok_gids_.insert(stat_buf.st_gid); - bad_gids_.insert(stat_buf.st_gid + 1); - - ASSERT_EQ(uid_, getuid()); // This process should be the owner. - - // To ensure that umask settings do not cause the initial state - // of permissions to be different from what we expect, explicitly - // set permissions on the directories we create. - // Make all files and directories non-world-writable. - - // Users and group can read, write, traverse - int enabled_permissions = - FILE_PERMISSION_USER_MASK | FILE_PERMISSION_GROUP_MASK; - // Other users can't read, write, traverse - int disabled_permissions = FILE_PERMISSION_OTHERS_MASK; - - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions( - base_dir_, enabled_permissions, disabled_permissions)); - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions( - sub_dir_, enabled_permissions, disabled_permissions)); - } - - FilePath base_dir_; - FilePath sub_dir_; - FilePath text_file_; - uid_t uid_; - - std::set ok_gids_; - std::set bad_gids_; -}; - -TEST_F(VerifyPathControlledByUserTest, BadPaths) { - // File does not exist. - FilePath does_not_exist = base_dir_.AppendASCII("does") - .AppendASCII("not") - .AppendASCII("exist"); - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, does_not_exist, uid_, ok_gids_)); - - // |base| not a subpath of |path|. - EXPECT_FALSE(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(VerifyPathControlledByUser(empty, base_dir_, uid_, ok_gids_)); - - // Finding that a bad call fails proves nothing unless a good call succeeds. - EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); -} - -TEST_F(VerifyPathControlledByUserTest, Symlinks) { - // Symlinks in the path should cause failure. - - // Symlink to the file at the end of the path. - FilePath file_link = base_dir_.AppendASCII("file_link"); - ASSERT_TRUE(CreateSymbolicLink(text_file_, file_link)) - << "Failed to create symlink."; - - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, file_link, uid_, ok_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(file_link, file_link, uid_, ok_gids_)); - - // Symlink from one directory to another within the path. - FilePath link_to_sub_dir = base_dir_.AppendASCII("link_to_sub_dir"); - ASSERT_TRUE(CreateSymbolicLink(sub_dir_, link_to_sub_dir)) - << "Failed to create symlink."; - - FilePath file_path_with_link = link_to_sub_dir.AppendASCII("file.txt"); - ASSERT_TRUE(PathExists(file_path_with_link)); - - EXPECT_FALSE(VerifyPathControlledByUser(base_dir_, file_path_with_link, uid_, - ok_gids_)); - - EXPECT_FALSE(VerifyPathControlledByUser(link_to_sub_dir, file_path_with_link, - uid_, ok_gids_)); - - // Symlinks in parents of base path are allowed. - EXPECT_TRUE(VerifyPathControlledByUser(file_path_with_link, - file_path_with_link, uid_, ok_gids_)); -} - -TEST_F(VerifyPathControlledByUserTest, OwnershipChecks) { - // Get a uid that is not the uid of files we create. - uid_t bad_uid = uid_ + 1; - - // Make all files and directories non-world-writable. - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH)); - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH)); - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions(text_file_, 0u, S_IWOTH)); - - // We control these paths. - EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); - EXPECT_TRUE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_)); - EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_)); - - // Another user does not control these paths. - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, sub_dir_, bad_uid, ok_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, text_file_, bad_uid, ok_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(sub_dir_, text_file_, bad_uid, ok_gids_)); - - // Another group does not control the paths. - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, bad_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, bad_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(sub_dir_, text_file_, uid_, bad_gids_)); -} - -TEST_F(VerifyPathControlledByUserTest, GroupWriteTest) { - // Make all files and directories writable only by their owner. - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH|S_IWGRP)); - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH|S_IWGRP)); - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions(text_file_, 0u, S_IWOTH|S_IWGRP)); - - // Any group is okay because the path is not group-writable. - EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); - EXPECT_TRUE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_)); - EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_)); - - EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, bad_gids_)); - EXPECT_TRUE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, bad_gids_)); - EXPECT_TRUE( - 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 no_gids; // Empty set of gids. - EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, no_gids)); - EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, text_file_, uid_, no_gids)); - EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, no_gids)); - - // Make all files and directories writable by their group. - ASSERT_NO_FATAL_FAILURE(ChangePosixFilePermissions(base_dir_, S_IWGRP, 0u)); - ASSERT_NO_FATAL_FAILURE(ChangePosixFilePermissions(sub_dir_, S_IWGRP, 0u)); - ASSERT_NO_FATAL_FAILURE(ChangePosixFilePermissions(text_file_, S_IWGRP, 0u)); - - // Now |ok_gids_| works, but |bad_gids_| fails. - EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); - EXPECT_TRUE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_)); - EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_)); - - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, bad_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, bad_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(sub_dir_, text_file_, uid_, bad_gids_)); - - // Because any group in the group set is allowed, - // the union of good and bad gids passes. - - std::set multiple_gids; - std::set_union( - ok_gids_.begin(), ok_gids_.end(), - bad_gids_.begin(), bad_gids_.end(), - std::inserter(multiple_gids, multiple_gids.begin())); - - EXPECT_TRUE( - VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, multiple_gids)); - EXPECT_TRUE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, multiple_gids)); - EXPECT_TRUE( - VerifyPathControlledByUser(sub_dir_, text_file_, uid_, multiple_gids)); -} - -TEST_F(VerifyPathControlledByUserTest, WriteBitChecks) { - // Make all files and directories non-world-writable. - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions(base_dir_, 0u, S_IWOTH)); - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions(sub_dir_, 0u, S_IWOTH)); - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions(text_file_, 0u, S_IWOTH)); - - // Initialy, we control all parts of the path. - EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); - EXPECT_TRUE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_)); - EXPECT_TRUE(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(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_)); - EXPECT_TRUE(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(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_)); - EXPECT_FALSE( - 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(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_)); - EXPECT_FALSE( - 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(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_)); - EXPECT_FALSE( - 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(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_)); - EXPECT_FALSE( - VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_)); - - // Back to the initial state: Nothing is writable, so every path - // should pass. - ASSERT_NO_FATAL_FAILURE( - ChangePosixFilePermissions(text_file_, 0u, S_IWOTH)); - EXPECT_TRUE(VerifyPathControlledByUser(base_dir_, sub_dir_, uid_, ok_gids_)); - EXPECT_TRUE( - VerifyPathControlledByUser(base_dir_, text_file_, uid_, ok_gids_)); - EXPECT_TRUE(VerifyPathControlledByUser(sub_dir_, text_file_, uid_, ok_gids_)); -} - -#endif // defined(OS_POSIX) - -#if defined(OS_ANDROID) -TEST_F(FileUtilTest, ValidContentUriTest) { - // Get the test image path. - FilePath data_dir; - ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir)); - data_dir = data_dir.AppendASCII("file_util"); - ASSERT_TRUE(PathExists(data_dir)); - FilePath image_file = data_dir.Append(FILE_PATH_LITERAL("red.png")); - int64_t image_size; - GetFileSize(image_file, &image_size); - ASSERT_GT(image_size, 0); - - // Insert the image into MediaStore. MediaStore will do some conversions, and - // return the content URI. - FilePath path = InsertImageIntoMediaStore(image_file); - EXPECT_TRUE(path.IsContentUri()); - EXPECT_TRUE(PathExists(path)); - // The file size may not equal to the input image as MediaStore may convert - // the image. - int64_t content_uri_size; - GetFileSize(path, &content_uri_size); - EXPECT_EQ(image_size, content_uri_size); - - // We should be able to read the file. - File file = OpenContentUriForRead(path); - EXPECT_TRUE(file.IsValid()); - auto buffer = std::make_unique(image_size); - EXPECT_TRUE(file.ReadAtCurrentPos(buffer.get(), image_size)); -} - -TEST_F(FileUtilTest, NonExistentContentUriTest) { - FilePath path("content://foo.bar"); - EXPECT_TRUE(path.IsContentUri()); - EXPECT_FALSE(PathExists(path)); - // Size should be smaller than 0. - int64_t size; - EXPECT_FALSE(GetFileSize(path, &size)); - - // We should not be able to read the file. - File file = OpenContentUriForRead(path); - EXPECT_FALSE(file.IsValid()); -} -#endif - -// Test that temp files obtained racily are all unique (no interference between -// threads). Mimics file operations in DoLaunchChildTestProcess() to rule out -// thread-safety issues @ https://crbug.com/826408#c17. -#if defined(OS_FUCHSIA) -// TODO(crbug.com/844416): Too slow to run on infra due to QEMU overloads. -#define MAYBE_MultiThreadedTempFiles DISABLED_MultiThreadedTempFiles -#else -#define MAYBE_MultiThreadedTempFiles MultiThreadedTempFiles -#endif -TEST(FileUtilMultiThreadedTest, MAYBE_MultiThreadedTempFiles) { - constexpr int kNumThreads = 64; - constexpr int kNumWritesPerThread = 32; - - std::unique_ptr threads[kNumThreads]; - for (auto& thread : threads) { - thread = std::make_unique("test worker"); - thread->Start(); - } - - // Wait until all threads are started for max parallelism. - for (auto& thread : threads) - thread->WaitUntilThreadStarted(); - - const RepeatingClosure open_write_close_read = BindRepeating([]() { - FilePath output_filename; - ScopedFILE output_file(CreateAndOpenTemporaryFile(&output_filename)); - EXPECT_TRUE(output_file); - - const std::string content = GenerateGUID(); -#if defined(OS_WIN) - HANDLE handle = - reinterpret_cast(_get_osfhandle(_fileno(output_file.get()))); - DWORD bytes_written = 0; - ::WriteFile(handle, content.c_str(), content.length(), &bytes_written, - NULL); -#else - size_t bytes_written = - ::write(::fileno(output_file.get()), content.c_str(), content.length()); -#endif - EXPECT_EQ(content.length(), bytes_written); - ::fflush(output_file.get()); - output_file.reset(); - - std::string output_file_contents; - EXPECT_TRUE(ReadFileToString(output_filename, &output_file_contents)) - << output_filename; - - EXPECT_EQ(content, output_file_contents); - - DeleteFile(output_filename, false); - }); - - // Post tasks to each thread in a round-robin fashion to ensure as much - // parallelism as possible. - for (int i = 0; i < kNumWritesPerThread; ++i) { - for (auto& thread : threads) { - thread->task_runner()->PostTask(FROM_HERE, open_write_close_read); - } - } - - for (auto& thread : threads) - thread->Stop(); -} - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - -TEST(ScopedFD, ScopedFDDoesClose) { - int fds[2]; - char c = 0; - ASSERT_EQ(0, pipe(fds)); - const int write_end = fds[1]; - ScopedFD read_end_closer(fds[0]); - { - 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) { - ScopedFD fd_closer(fd); -} -#endif - -TEST(ScopedFD, ScopedFDCrashesOnCloseFailure) { - int fds[2]; - ASSERT_EQ(0, pipe(fds)); - 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) || defined(OS_FUCHSIA) - -} // namespace - -} // namespace base diff --git a/files/file_util_win.cc b/files/file_util_win.cc deleted file mode 100644 index e81757f8f..000000000 --- a/files/file_util_win.cc +++ /dev/null @@ -1,1010 +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/files/file_util.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "base/files/file_enumerator.h" -#include "base/files/file_path.h" -#include "base/guid.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/metrics/histogram_functions.h" -#include "base/process/process_handle.h" -#include "base/rand_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_restrictions.h" -#include "base/time/time.h" -#include "base/win/scoped_handle.h" -#include "base/win/windows_version.h" - -namespace base { - -namespace { - -const DWORD kFileShareAll = - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; - -// Records a sample in a histogram named -// "Windows.PostOperationState.|operation|" indicating the state of |path| -// following the named operation. If |operation_succeeded| is true, the -// "operation succeeded" sample is recorded. Otherwise, the state of |path| is -// queried and the most meaningful sample is recorded. -void RecordPostOperationState(const FilePath& path, - StringPiece operation, - bool operation_succeeded) { - // The state of a filesystem item after an operation. - // These values are persisted to logs. Entries should not be renumbered and - // numeric values should never be reused. - enum class PostOperationState { - kOperationSucceeded = 0, - kFileNotFoundAfterFailure = 1, - kPathNotFoundAfterFailure = 2, - kAccessDeniedAfterFailure = 3, - kNoAttributesAfterFailure = 4, - kEmptyDirectoryAfterFailure = 5, - kNonEmptyDirectoryAfterFailure = 6, - kNotDirectoryAfterFailure = 7, - kCount - } metric = PostOperationState::kOperationSucceeded; - - if (!operation_succeeded) { - const DWORD attributes = ::GetFileAttributes(path.value().c_str()); - if (attributes == INVALID_FILE_ATTRIBUTES) { - // On failure to delete, one might expect the file/directory to still be - // in place. Slice a failure to get its attributes into a few common error - // buckets. - const DWORD error_code = ::GetLastError(); - if (error_code == ERROR_FILE_NOT_FOUND) - metric = PostOperationState::kFileNotFoundAfterFailure; - else if (error_code == ERROR_PATH_NOT_FOUND) - metric = PostOperationState::kPathNotFoundAfterFailure; - else if (error_code == ERROR_ACCESS_DENIED) - metric = PostOperationState::kAccessDeniedAfterFailure; - else - metric = PostOperationState::kNoAttributesAfterFailure; - } else if (attributes & FILE_ATTRIBUTE_DIRECTORY) { - if (IsDirectoryEmpty(path)) - metric = PostOperationState::kEmptyDirectoryAfterFailure; - else - metric = PostOperationState::kNonEmptyDirectoryAfterFailure; - } else { - metric = PostOperationState::kNotDirectoryAfterFailure; - } - } - - std::string histogram_name = "Windows.PostOperationState."; - operation.AppendToString(&histogram_name); - UmaHistogramEnumeration(histogram_name, metric, PostOperationState::kCount); -} - -// Records the sample |error| in a histogram named -// "Windows.FilesystemError.|operation|". -void RecordFilesystemError(StringPiece operation, DWORD error) { - std::string histogram_name = "Windows.FilesystemError."; - operation.AppendToString(&histogram_name); - UmaHistogramSparse(histogram_name, error); -} - -// Returns the Win32 last error code or ERROR_SUCCESS if the last error code is -// ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND. This is useful in cases where -// the absence of a file or path is a success condition (e.g., when attempting -// to delete an item in the filesystem). -DWORD ReturnLastErrorOrSuccessOnNotFound() { - const DWORD error_code = ::GetLastError(); - return (error_code == ERROR_FILE_NOT_FOUND || - error_code == ERROR_PATH_NOT_FOUND) - ? ERROR_SUCCESS - : error_code; -} - -// Deletes all files and directories in a path. -// Returns ERROR_SUCCESS on success or the Windows error code corresponding to -// the first error encountered. ERROR_FILE_NOT_FOUND and ERROR_PATH_NOT_FOUND -// are considered success conditions, and are therefore never returned. -DWORD DeleteFileRecursive(const FilePath& path, - const FilePath::StringType& pattern, - bool recursive) { - FileEnumerator traversal(path, false, - FileEnumerator::FILES | FileEnumerator::DIRECTORIES, - pattern); - DWORD result = ERROR_SUCCESS; - for (FilePath current = traversal.Next(); !current.empty(); - current = traversal.Next()) { - // Try to clear the read-only bit if we find it. - FileEnumerator::FileInfo info = traversal.GetInfo(); - if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) && - (recursive || !info.IsDirectory())) { - ::SetFileAttributes( - current.value().c_str(), - info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); - } - - DWORD this_result = ERROR_SUCCESS; - if (info.IsDirectory()) { - if (recursive) { - this_result = DeleteFileRecursive(current, pattern, true); - DCHECK_NE(static_cast(this_result), ERROR_FILE_NOT_FOUND); - DCHECK_NE(static_cast(this_result), ERROR_PATH_NOT_FOUND); - if (this_result == ERROR_SUCCESS && - !::RemoveDirectory(current.value().c_str())) { - this_result = ReturnLastErrorOrSuccessOnNotFound(); - } - } - } else if (!::DeleteFile(current.value().c_str())) { - this_result = ReturnLastErrorOrSuccessOnNotFound(); - } - if (result == ERROR_SUCCESS) - result = this_result; - } - return result; -} - -// Appends |mode_char| to |mode| before the optional character set encoding; see -// https://msdn.microsoft.com/library/yeby3zcb.aspx for details. -void AppendModeCharacter(base::char16 mode_char, base::string16* mode) { - size_t comma_pos = mode->find(L','); - mode->insert(comma_pos == base::string16::npos ? mode->length() : comma_pos, - 1, mode_char); -} - -bool DoCopyFile(const FilePath& from_path, - const FilePath& to_path, - bool fail_if_exists) { - AssertBlockingAllowed(); - if (from_path.ReferencesParent() || to_path.ReferencesParent()) - return false; - - // NOTE: I suspect we could support longer paths, but that would involve - // analyzing all our usage of files. - if (from_path.value().length() >= MAX_PATH || - to_path.value().length() >= MAX_PATH) { - return false; - } - - // Unlike the posix implementation that copies the file manually and discards - // the ACL bits, CopyFile() copies the complete SECURITY_DESCRIPTOR and access - // bits, which is usually not what we want. We can't do much about the - // SECURITY_DESCRIPTOR but at least remove the read only bit. - const wchar_t* dest = to_path.value().c_str(); - if (!::CopyFile(from_path.value().c_str(), dest, fail_if_exists)) { - // 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 DoCopyDirectory(const FilePath& from_path, - const FilePath& to_path, - bool recursive, - bool fail_if_exists) { - // 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. - AssertBlockingAllowed(); - - // 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 == real_from_path || real_from_path.IsParent(real_to_path)) - return false; - - int traverse_type = FileEnumerator::FILES; - if (recursive) - 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(); - } - - 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 (!DoCopyFile(current, target_path, fail_if_exists)) { - DLOG(ERROR) << "CopyDirectory() couldn't create file: " - << target_path.value().c_str(); - success = false; - } - - current = traversal.Next(); - if (!current.empty()) - from_is_dir = traversal.GetInfo().IsDirectory(); - } - - return success; -} - -// Returns ERROR_SUCCESS on success, or a Windows error code on failure. -DWORD DoDeleteFile(const FilePath& path, bool recursive) { - AssertBlockingAllowed(); - - if (path.empty()) - return ERROR_SUCCESS; - - if (path.value().length() >= MAX_PATH) - return ERROR_BAD_PATHNAME; - - // Handle any path with wildcards. - if (path.BaseName().value().find_first_of(L"*?") != - FilePath::StringType::npos) { - const DWORD error_code = - DeleteFileRecursive(path.DirName(), path.BaseName().value(), recursive); - DCHECK_NE(static_cast(error_code), ERROR_FILE_NOT_FOUND); - DCHECK_NE(static_cast(error_code), ERROR_PATH_NOT_FOUND); - return error_code; - } - - // Report success if the file or path does not exist. - const DWORD attr = ::GetFileAttributes(path.value().c_str()); - if (attr == INVALID_FILE_ATTRIBUTES) - return ReturnLastErrorOrSuccessOnNotFound(); - - // Clear the read-only bit if it is set. - if ((attr & FILE_ATTRIBUTE_READONLY) && - !::SetFileAttributes(path.value().c_str(), - attr & ~FILE_ATTRIBUTE_READONLY)) { - // It's possible for |path| to be gone now under a race with other deleters. - return ReturnLastErrorOrSuccessOnNotFound(); - } - - // Perform a simple delete on anything that isn't a directory. - if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { - return ::DeleteFile(path.value().c_str()) - ? ERROR_SUCCESS - : ReturnLastErrorOrSuccessOnNotFound(); - } - - if (recursive) { - const DWORD error_code = DeleteFileRecursive(path, L"*", true); - DCHECK_NE(static_cast(error_code), ERROR_FILE_NOT_FOUND); - DCHECK_NE(static_cast(error_code), ERROR_PATH_NOT_FOUND); - if (error_code != ERROR_SUCCESS) - return error_code; - } - return ::RemoveDirectory(path.value().c_str()) - ? ERROR_SUCCESS - : ReturnLastErrorOrSuccessOnNotFound(); -} - -} // namespace - -FilePath MakeAbsoluteFilePath(const FilePath& input) { - AssertBlockingAllowed(); - wchar_t file_path[MAX_PATH]; - if (!_wfullpath(file_path, input.value().c_str(), MAX_PATH)) - return FilePath(); - return FilePath(file_path); -} - -bool DeleteFile(const FilePath& path, bool recursive) { - static constexpr char kRecursive[] = "DeleteFile.Recursive"; - static constexpr char kNonRecursive[] = "DeleteFile.NonRecursive"; - const StringPiece operation(recursive ? kRecursive : kNonRecursive); - - AssertBlockingAllowed(); - - // Metrics for delete failures tracked in https://crbug.com/599084. Delete may - // fail for a number of reasons. Log some metrics relating to failures in the - // current code so that any improvements or regressions resulting from - // subsequent code changes can be detected. - const DWORD error = DoDeleteFile(path, recursive); - RecordPostOperationState(path, operation, error == ERROR_SUCCESS); - if (error == ERROR_SUCCESS) - return true; - - RecordFilesystemError(operation, error); - return false; -} - -bool DeleteFileAfterReboot(const FilePath& path) { - AssertBlockingAllowed(); - - if (path.value().length() >= MAX_PATH) - return false; - - return MoveFileEx(path.value().c_str(), NULL, - MOVEFILE_DELAY_UNTIL_REBOOT | - MOVEFILE_REPLACE_EXISTING) != FALSE; -} - -bool ReplaceFile(const FilePath& from_path, - const FilePath& to_path, - File::Error* error) { - AssertBlockingAllowed(); - // Try a simple move first. It will only succeed when |to_path| doesn't - // already exist. - if (::MoveFile(from_path.value().c_str(), to_path.value().c_str())) - return true; - File::Error move_error = File::OSErrorToFileError(GetLastError()); - - // Try the full-blown replace if the move fails, as ReplaceFile will only - // succeed when |to_path| does exist. When writing to a network share, we may - // not be able to change the ACLs. Ignore ACL errors then - // (REPLACEFILE_IGNORE_MERGE_ERRORS). - if (::ReplaceFile(to_path.value().c_str(), from_path.value().c_str(), NULL, - REPLACEFILE_IGNORE_MERGE_ERRORS, NULL, NULL)) { - return true; - } - // In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that - // |to_path| does not exist. In this case, the more relevant error comes - // from the call to MoveFile. - if (error) { - File::Error replace_error = File::OSErrorToFileError(GetLastError()); - *error = replace_error == File::FILE_ERROR_NOT_FOUND ? move_error - : replace_error; - } - return false; -} - -bool CopyDirectory(const FilePath& from_path, - const FilePath& to_path, - bool recursive) { - return DoCopyDirectory(from_path, to_path, recursive, false); -} - -bool CopyDirectoryExcl(const FilePath& from_path, - const FilePath& to_path, - bool recursive) { - return DoCopyDirectory(from_path, to_path, recursive, true); -} - -bool PathExists(const FilePath& path) { - AssertBlockingAllowed(); - return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); -} - -bool PathIsWritable(const FilePath& path) { - AssertBlockingAllowed(); - HANDLE dir = - CreateFile(path.value().c_str(), FILE_ADD_FILE, kFileShareAll, - NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - - if (dir == INVALID_HANDLE_VALUE) - return false; - - CloseHandle(dir); - return true; -} - -bool DirectoryExists(const FilePath& path) { - AssertBlockingAllowed(); - DWORD fileattr = GetFileAttributes(path.value().c_str()); - if (fileattr != INVALID_FILE_ATTRIBUTES) - return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0; - return false; -} - -bool GetTempDir(FilePath* path) { - wchar_t temp_path[MAX_PATH + 1]; - DWORD path_len = ::GetTempPath(MAX_PATH, temp_path); - if (path_len >= MAX_PATH || path_len <= 0) - return false; - // TODO(evanm): the old behavior of this function was to always strip the - // trailing slash. We duplicate this here, but it shouldn't be necessary - // when everyone is using the appropriate FilePath APIs. - *path = FilePath(temp_path).StripTrailingSeparators(); - return true; -} - -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) { - AssertBlockingAllowed(); - - FilePath temp_file; - - if (!GetTempDir(path)) - return false; - - if (CreateTemporaryFileInDir(*path, &temp_file)) { - *path = temp_file; - return true; - } - - return false; -} - -// 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 -// going 2-step? -FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { - AssertBlockingAllowed(); - if (!CreateTemporaryFileInDir(dir, path)) { - return NULL; - } - // Open file in binary mode, to avoid problems with fwrite. On Windows - // it replaces \n's with \r\n's, which may surprise you. - // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx - return OpenFile(*path, "wb+"); -} - -bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) { - AssertBlockingAllowed(); - - // Use GUID instead of ::GetTempFileName() to generate unique file names. - // "Due to the algorithm used to generate file names, GetTempFileName can - // perform poorly when creating a large number of files with the same prefix. - // In such cases, it is recommended that you construct unique file names based - // on GUIDs." - // https://msdn.microsoft.com/library/windows/desktop/aa364991.aspx - - FilePath temp_name; - bool create_file_success = false; - - // Although it is nearly impossible to get a duplicate name with GUID, we - // still use a loop here in case it happens. - for (int i = 0; i < 100; ++i) { - temp_name = dir.Append(ASCIIToUTF16(base::GenerateGUID()) + L".tmp"); - File file(temp_name, - File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE); - if (file.IsValid()) { - file.Close(); - create_file_success = true; - break; - } - } - - if (!create_file_success) { - DPLOG(WARNING) << "Failed to get temporary file name in " - << UTF16ToUTF8(dir.value()); - return false; - } - - wchar_t long_temp_name[MAX_PATH + 1]; - DWORD long_name_len = - GetLongPathName(temp_name.value().c_str(), long_temp_name, MAX_PATH); - if (long_name_len > MAX_PATH || long_name_len == 0) { - // GetLongPathName() failed, but we still have a temporary file. - *temp_file = std::move(temp_name); - return true; - } - - FilePath::StringType long_temp_name_str; - long_temp_name_str.assign(long_temp_name, long_name_len); - *temp_file = FilePath(std::move(long_temp_name_str)); - return true; -} - -bool CreateTemporaryDirInDir(const FilePath& base_dir, - const FilePath::StringType& prefix, - FilePath* new_dir) { - AssertBlockingAllowed(); - - FilePath path_to_create; - - for (int count = 0; count < 50; ++count) { - // Try create a new temporary directory with random generated name. If - // the one exists, keep trying another path name until we reach some limit. - string16 new_dir_name; - new_dir_name.assign(prefix); - new_dir_name.append(IntToString16(GetCurrentProcId())); - new_dir_name.push_back('_'); - new_dir_name.append( - IntToString16(RandInt(0, std::numeric_limits::max()))); - - path_to_create = base_dir.Append(new_dir_name); - if (::CreateDirectory(path_to_create.value().c_str(), NULL)) { - *new_dir = path_to_create; - return true; - } - } - - return false; -} - -bool CreateNewTempDirectory(const FilePath::StringType& prefix, - FilePath* new_temp_path) { - AssertBlockingAllowed(); - - FilePath system_temp_dir; - if (!GetTempDir(&system_temp_dir)) - return false; - - return CreateTemporaryDirInDir(system_temp_dir, prefix, new_temp_path); -} - -bool CreateDirectoryAndGetError(const FilePath& full_path, - File::Error* error) { - AssertBlockingAllowed(); - - // If the path exists, we've succeeded if it's a directory, failed otherwise. - const wchar_t* full_path_str = full_path.value().c_str(); - DWORD fileattr = ::GetFileAttributes(full_path_str); - if (fileattr != INVALID_FILE_ATTRIBUTES) { - if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - DVLOG(1) << "CreateDirectory(" << full_path_str << "), " - << "directory already exists."; - return true; - } - DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), " - << "conflicts with existing file."; - if (error) { - *error = File::FILE_ERROR_NOT_A_DIRECTORY; - } - return false; - } - - // Invariant: Path does not exist as file or directory. - - // Attempt to create the parent recursively. This will immediately return - // true if it already exists, otherwise will create all required parent - // directories starting with the highest-level missing parent. - FilePath parent_path(full_path.DirName()); - if (parent_path.value() == full_path.value()) { - if (error) { - *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 != File::FILE_OK); - } - return false; - } - - if (!::CreateDirectory(full_path_str, NULL)) { - DWORD error_code = ::GetLastError(); - if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) { - // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we - // were racing with someone creating the same directory, or a file - // with the same path. If DirectoryExists() returns true, we lost the - // race to create the same directory. - return true; - } else { - if (error) - *error = File::OSErrorToFileError(error_code); - DLOG(WARNING) << "Failed to create directory " << full_path_str - << ", last error is " << error_code << "."; - return false; - } - } else { - return true; - } -} - -bool NormalizeFilePath(const FilePath& path, FilePath* real_path) { - AssertBlockingAllowed(); - FilePath mapped_file; - if (!NormalizeToNativeFilePath(path, &mapped_file)) - return false; - // NormalizeToNativeFilePath() will return a path that starts with - // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath() - // will find a drive letter which maps to the path's device, so - // that we return a path starting with a drive letter. - return DevicePathToDriveLetterPath(mapped_file, real_path); -} - -bool DevicePathToDriveLetterPath(const FilePath& nt_device_path, - FilePath* out_drive_letter_path) { - AssertBlockingAllowed(); - - // Get the mapping of drive letters to device paths. - const int kDriveMappingSize = 1024; - wchar_t drive_mapping[kDriveMappingSize] = {'\0'}; - if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) { - DLOG(ERROR) << "Failed to get drive mapping."; - return false; - } - - // The drive mapping is a sequence of null terminated strings. - // The last string is empty. - wchar_t* drive_map_ptr = drive_mapping; - wchar_t device_path_as_string[MAX_PATH]; - wchar_t drive[] = L" :"; - - // For each string in the drive mapping, get the junction that links - // to it. If that junction is a prefix of |device_path|, then we - // know that |drive| is the real path prefix. - while (*drive_map_ptr) { - drive[0] = drive_map_ptr[0]; // Copy the drive letter. - - if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) { - FilePath device_path(device_path_as_string); - if (device_path == nt_device_path || - device_path.IsParent(nt_device_path)) { - *out_drive_letter_path = FilePath(drive + - nt_device_path.value().substr(wcslen(device_path_as_string))); - return true; - } - } - // Move to the next drive letter string, which starts one - // increment after the '\0' that terminates the current string. - while (*drive_map_ptr++) {} - } - - // No drive matched. The path does not start with a device junction - // that is mounted as a drive letter. This means there is no drive - // letter path to the volume that holds |device_path|, so fail. - return false; -} - -bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) { - AssertBlockingAllowed(); - // In Vista, GetFinalPathNameByHandle() would give us the real path - // from a file handle. If we ever deprecate XP, consider changing the - // code below to a call to GetFinalPathNameByHandle(). The method this - // function uses is explained in the following msdn article: - // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx - win::ScopedHandle file_handle( - ::CreateFile(path.value().c_str(), - GENERIC_READ, - kFileShareAll, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL)); - if (!file_handle.IsValid()) - return false; - - // Create a file mapping object. Can't easily use MemoryMappedFile, because - // we only map the first byte, and need direct access to the handle. You can - // not map an empty file, this call fails in that case. - win::ScopedHandle file_map_handle( - ::CreateFileMapping(file_handle.Get(), - NULL, - PAGE_READONLY, - 0, - 1, // Just one byte. No need to look at the data. - NULL)); - if (!file_map_handle.IsValid()) - return false; - - // Use a view of the file to get the path to the file. - void* file_view = MapViewOfFile(file_map_handle.Get(), - FILE_MAP_READ, 0, 0, 1); - if (!file_view) - return false; - - // The expansion of |path| into a full path may make it longer. - // GetMappedFileName() will fail if the result is longer than MAX_PATH. - // Pad a bit to be safe. If kMaxPathLength is ever changed to be less - // than MAX_PATH, it would be nessisary to test that GetMappedFileName() - // not return kMaxPathLength. This would mean that only part of the - // path fit in |mapped_file_path|. - const int kMaxPathLength = MAX_PATH + 10; - wchar_t mapped_file_path[kMaxPathLength]; - bool success = false; - HANDLE cp = GetCurrentProcess(); - if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) { - *nt_path = FilePath(mapped_file_path); - success = true; - } - ::UnmapViewOfFile(file_view); - return success; -} - -// TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle -// them if we do decide to. -bool IsLink(const FilePath& file_path) { - return false; -} - -bool GetFileInfo(const FilePath& file_path, File::Info* results) { - AssertBlockingAllowed(); - - WIN32_FILE_ATTRIBUTE_DATA attr; - if (!GetFileAttributesEx(file_path.value().c_str(), - GetFileExInfoStandard, &attr)) { - return false; - } - - ULARGE_INTEGER size; - size.HighPart = attr.nFileSizeHigh; - size.LowPart = attr.nFileSizeLow; - results->size = size.QuadPart; - - results->is_directory = - (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - results->last_modified = Time::FromFileTime(attr.ftLastWriteTime); - results->last_accessed = Time::FromFileTime(attr.ftLastAccessTime); - results->creation_time = Time::FromFileTime(attr.ftCreationTime); - - return true; -} - -FILE* OpenFile(const FilePath& filename, const char* mode) { - // 'N' is unconditionally added below, so be sure there is not one already - // present before a comma in |mode|. - DCHECK( - strchr(mode, 'N') == nullptr || - (strchr(mode, ',') != nullptr && strchr(mode, 'N') > strchr(mode, ','))); - AssertBlockingAllowed(); - string16 w_mode = ASCIIToUTF16(mode); - AppendModeCharacter(L'N', &w_mode); - return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO); -} - -FILE* FileToFILE(File file, const char* mode) { - if (!file.IsValid()) - return NULL; - int fd = - _open_osfhandle(reinterpret_cast(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) { - AssertBlockingAllowed(); - win::ScopedHandle file(CreateFile(filename.value().c_str(), - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, - NULL)); - if (!file.IsValid()) - return -1; - - DWORD read; - if (::ReadFile(file.Get(), data, max_size, &read, NULL)) - return read; - - return -1; -} - -int WriteFile(const FilePath& filename, const char* data, int size) { - AssertBlockingAllowed(); - win::ScopedHandle file(CreateFile(filename.value().c_str(), GENERIC_WRITE, 0, - NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, - NULL)); - if (!file.IsValid()) { - DPLOG(WARNING) << "CreateFile failed for path " - << UTF16ToUTF8(filename.value()); - return -1; - } - - DWORD written; - BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL); - if (result && static_cast(written) == size) - return written; - - if (!result) { - // WriteFile failed. - DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value()) - << " failed"; - } else { - // Didn't write all the bytes. - DLOG(WARNING) << "wrote" << written << " bytes to " - << UTF16ToUTF8(filename.value()) << " expected " << size; - } - return -1; -} - -bool AppendToFile(const FilePath& filename, const char* data, int size) { - AssertBlockingAllowed(); - win::ScopedHandle file(CreateFile(filename.value().c_str(), - FILE_APPEND_DATA, - 0, - NULL, - OPEN_EXISTING, - 0, - NULL)); - if (!file.IsValid()) { - VPLOG(1) << "CreateFile failed for path " << UTF16ToUTF8(filename.value()); - return false; - } - - DWORD written; - BOOL result = ::WriteFile(file.Get(), data, size, &written, NULL); - if (result && static_cast(written) == size) - return true; - - if (!result) { - // WriteFile failed. - VPLOG(1) << "Writing file " << UTF16ToUTF8(filename.value()) << " failed"; - } else { - // Didn't write all the bytes. - VPLOG(1) << "Only wrote " << written << " out of " << size << " byte(s) to " - << UTF16ToUTF8(filename.value()); - } - return false; -} - -bool GetCurrentDirectory(FilePath* dir) { - AssertBlockingAllowed(); - - wchar_t system_buffer[MAX_PATH]; - system_buffer[0] = 0; - DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer); - if (len == 0 || len > MAX_PATH) - return false; - // TODO(evanm): the old behavior of this function was to always strip the - // trailing slash. We duplicate this here, but it shouldn't be necessary - // when everyone is using the appropriate FilePath APIs. - std::wstring dir_str(system_buffer); - *dir = FilePath(dir_str).StripTrailingSeparators(); - return true; -} - -bool SetCurrentDirectory(const FilePath& directory) { - AssertBlockingAllowed(); - return ::SetCurrentDirectory(directory.value().c_str()) != 0; -} - -int GetMaximumPathComponentLength(const FilePath& path) { - AssertBlockingAllowed(); - - wchar_t volume_path[MAX_PATH]; - if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(), - volume_path, - arraysize(volume_path))) { - return -1; - } - - DWORD max_length = 0; - if (!GetVolumeInformationW(volume_path, NULL, 0, NULL, &max_length, NULL, - NULL, 0)) { - return -1; - } - - // Length of |path| with path separator appended. - size_t prefix = path.StripTrailingSeparators().value().size() + 1; - // The whole path string must be shorter than MAX_PATH. That is, it must be - // prefix + component_length < MAX_PATH (or equivalently, <= MAX_PATH - 1). - int whole_path_limit = std::max(0, MAX_PATH - 1 - static_cast(prefix)); - return std::min(whole_path_limit, static_cast(max_length)); -} - -bool CopyFile(const FilePath& from_path, const FilePath& to_path) { - return DoCopyFile(from_path, to_path, false); -} - -bool SetNonBlocking(int fd) { - unsigned long nonblocking = 1; - if (ioctlsocket(fd, FIONBIO, &nonblocking) == 0) - return true; - return false; -} - -// ----------------------------------------------------------------------------- - -namespace internal { - -bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) { - AssertBlockingAllowed(); - - // 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; - } - if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), - MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0) - return true; - - // Keep the last error value from MoveFileEx around in case the below - // fails. - bool ret = false; - DWORD last_error = ::GetLastError(); - - if (DirectoryExists(from_path)) { - // MoveFileEx fails if moving directory across volumes. We will simulate - // the move by using Copy and Delete. Ideally we could check whether - // from_path and to_path are indeed in different volumes. - ret = internal::CopyAndDeleteDirectory(from_path, to_path); - } - - if (!ret) { - // Leave a clue about what went wrong so that it can be (at least) picked - // up by a PLOG entry. - ::SetLastError(last_error); - } - - return ret; -} - -bool CopyAndDeleteDirectory(const FilePath& from_path, - const FilePath& to_path) { - AssertBlockingAllowed(); - if (CopyDirectory(from_path, to_path, true)) { - if (DeleteFile(from_path, true)) - return true; - - // Like Move, this function is not transactional, so we just - // leave the copied bits behind if deleting from_path fails. - // If to_path exists previously then we have already overwritten - // it by now, we don't get better off by deleting the new bits. - } - return false; -} - -} // namespace internal -} // namespace base diff --git a/files/file_win.cc b/files/file_win.cc deleted file mode 100644 index 82cd7e8b4..000000000 --- a/files/file_win.cc +++ /dev/null @@ -1,426 +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/files/file.h" - -#include -#include - -#include "base/logging.h" -#include "base/metrics/histogram_functions.h" -#include "base/threading/thread_restrictions.h" - -#include - -namespace base { - -// Make sure our Whence mappings match the system headers. -static_assert(File::FROM_BEGIN == FILE_BEGIN && - File::FROM_CURRENT == FILE_CURRENT && - File::FROM_END == FILE_END, - "whence mapping must match the system headers"); - -bool File::IsValid() const { - return file_.IsValid(); -} - -PlatformFile File::GetPlatformFile() const { - return file_.Get(); -} - -PlatformFile File::TakePlatformFile() { - return file_.Take(); -} - -void File::Close() { - if (!file_.IsValid()) - return; - - AssertBlockingAllowed(); - SCOPED_FILE_TRACE("Close"); - file_.Close(); -} - -int64_t File::Seek(Whence whence, int64_t offset) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - - SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset); - - LARGE_INTEGER distance, res; - distance.QuadPart = offset; - DWORD move_method = static_cast(whence); - if (!SetFilePointerEx(file_.Get(), distance, &res, move_method)) - return -1; - return res.QuadPart; -} - -int File::Read(int64_t offset, char* data, int size) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - DCHECK(!async_); - if (size < 0) - return -1; - - SCOPED_FILE_TRACE_WITH_SIZE("Read", size); - - LARGE_INTEGER offset_li; - offset_li.QuadPart = offset; - - OVERLAPPED overlapped = {0}; - overlapped.Offset = offset_li.LowPart; - overlapped.OffsetHigh = offset_li.HighPart; - - DWORD bytes_read; - if (::ReadFile(file_.Get(), data, size, &bytes_read, &overlapped)) - return bytes_read; - if (ERROR_HANDLE_EOF == GetLastError()) - return 0; - - return -1; -} - -int File::ReadAtCurrentPos(char* data, int size) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - DCHECK(!async_); - if (size < 0) - return -1; - - SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size); - - DWORD bytes_read; - if (::ReadFile(file_.Get(), data, size, &bytes_read, NULL)) - return bytes_read; - if (ERROR_HANDLE_EOF == GetLastError()) - return 0; - - return -1; -} - -int File::ReadNoBestEffort(int64_t offset, char* data, int size) { - // TODO(dbeam): trace this separately? - return Read(offset, data, size); -} - -int File::ReadAtCurrentPosNoBestEffort(char* data, int size) { - // TODO(dbeam): trace this separately? - return ReadAtCurrentPos(data, size); -} - -int File::Write(int64_t offset, const char* data, int size) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - DCHECK(!async_); - - SCOPED_FILE_TRACE_WITH_SIZE("Write", size); - - LARGE_INTEGER offset_li; - offset_li.QuadPart = offset; - - OVERLAPPED overlapped = {0}; - overlapped.Offset = offset_li.LowPart; - overlapped.OffsetHigh = offset_li.HighPart; - - DWORD bytes_written; - if (::WriteFile(file_.Get(), data, size, &bytes_written, &overlapped)) - return bytes_written; - - return -1; -} - -int File::WriteAtCurrentPos(const char* data, int size) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - DCHECK(!async_); - if (size < 0) - return -1; - - SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size); - - DWORD bytes_written; - if (::WriteFile(file_.Get(), data, size, &bytes_written, NULL)) - return bytes_written; - - return -1; -} - -int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) { - return WriteAtCurrentPos(data, size); -} - -int64_t File::GetLength() { - AssertBlockingAllowed(); - DCHECK(IsValid()); - - SCOPED_FILE_TRACE("GetLength"); - - LARGE_INTEGER size; - if (!::GetFileSizeEx(file_.Get(), &size)) - return -1; - - return static_cast(size.QuadPart); -} - -bool File::SetLength(int64_t length) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - - SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length); - - // Get the current file pointer. - LARGE_INTEGER file_pointer; - LARGE_INTEGER zero; - zero.QuadPart = 0; - if (!::SetFilePointerEx(file_.Get(), zero, &file_pointer, FILE_CURRENT)) - return false; - - LARGE_INTEGER length_li; - length_li.QuadPart = length; - // If length > file size, SetFilePointerEx() should extend the file - // with zeroes on all Windows standard file systems (NTFS, FATxx). - if (!::SetFilePointerEx(file_.Get(), length_li, NULL, FILE_BEGIN)) - return false; - - // 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. - // 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_.Get()) != FALSE) && - (::SetFilePointerEx(file_.Get(), file_pointer, NULL, FILE_BEGIN) != - FALSE)); -} - -bool File::SetTimes(Time last_access_time, Time last_modified_time) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - - SCOPED_FILE_TRACE("SetTimes"); - - FILETIME last_access_filetime = last_access_time.ToFileTime(); - FILETIME last_modified_filetime = last_modified_time.ToFileTime(); - return (::SetFileTime(file_.Get(), NULL, &last_access_filetime, - &last_modified_filetime) != FALSE); -} - -bool File::GetInfo(Info* info) { - AssertBlockingAllowed(); - DCHECK(IsValid()); - - SCOPED_FILE_TRACE("GetInfo"); - - BY_HANDLE_FILE_INFORMATION file_info; - if (!GetFileInformationByHandle(file_.Get(), &file_info)) - return false; - - LARGE_INTEGER size; - size.HighPart = file_info.nFileSizeHigh; - size.LowPart = file_info.nFileSizeLow; - info->size = size.QuadPart; - info->is_directory = - (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - info->is_symbolic_link = false; // Windows doesn't have symbolic links. - info->last_modified = Time::FromFileTime(file_info.ftLastWriteTime); - info->last_accessed = Time::FromFileTime(file_info.ftLastAccessTime); - info->creation_time = Time::FromFileTime(file_info.ftCreationTime); - return true; -} - -File::Error File::Lock() { - DCHECK(IsValid()); - - SCOPED_FILE_TRACE("Lock"); - - BOOL result = LockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD); - if (!result) - return GetLastFileError(); - return FILE_OK; -} - -File::Error File::Unlock() { - DCHECK(IsValid()); - - SCOPED_FILE_TRACE("Unlock"); - - BOOL result = UnlockFile(file_.Get(), 0, 0, MAXDWORD, MAXDWORD); - if (!result) - return GetLastFileError(); - return FILE_OK; -} - -File File::Duplicate() const { - if (!IsValid()) - return File(); - - SCOPED_FILE_TRACE("Duplicate"); - - HANDLE other_handle = nullptr; - - if (!::DuplicateHandle(GetCurrentProcess(), // hSourceProcessHandle - GetPlatformFile(), - GetCurrentProcess(), // hTargetProcessHandle - &other_handle, - 0, // dwDesiredAccess ignored due to SAME_ACCESS - FALSE, // !bInheritHandle - DUPLICATE_SAME_ACCESS)) { - return File(GetLastFileError()); - } - - return File(other_handle, async()); -} - -bool File::DeleteOnClose(bool delete_on_close) { - FILE_DISPOSITION_INFO disposition = {delete_on_close ? TRUE : FALSE}; - return ::SetFileInformationByHandle(GetPlatformFile(), FileDispositionInfo, - &disposition, sizeof(disposition)) != 0; -} - -// Static. -File::Error File::OSErrorToFileError(DWORD last_error) { - switch (last_error) { - case ERROR_SHARING_VIOLATION: - return FILE_ERROR_IN_USE; - case ERROR_ALREADY_EXISTS: - case ERROR_FILE_EXISTS: - return FILE_ERROR_EXISTS; - case ERROR_FILE_NOT_FOUND: - case ERROR_PATH_NOT_FOUND: - return FILE_ERROR_NOT_FOUND; - case ERROR_ACCESS_DENIED: - return FILE_ERROR_ACCESS_DENIED; - case ERROR_TOO_MANY_OPEN_FILES: - return FILE_ERROR_TOO_MANY_OPENED; - case ERROR_OUTOFMEMORY: - case ERROR_NOT_ENOUGH_MEMORY: - return FILE_ERROR_NO_MEMORY; - case ERROR_HANDLE_DISK_FULL: - case ERROR_DISK_FULL: - case ERROR_DISK_RESOURCES_EXHAUSTED: - return FILE_ERROR_NO_SPACE; - case ERROR_USER_MAPPED_FILE: - return FILE_ERROR_INVALID_OPERATION; - case ERROR_NOT_READY: - case ERROR_SECTOR_NOT_FOUND: - case ERROR_DEV_NOT_EXIST: - case ERROR_IO_DEVICE: - case ERROR_FILE_CORRUPT: - case ERROR_DISK_CORRUPT: - return FILE_ERROR_IO; - default: - UmaHistogramSparse("PlatformFile.UnknownErrors.Windows", last_error); - // This function should only be called for errors. - DCHECK_NE(static_cast(ERROR_SUCCESS), last_error); - return FILE_ERROR_FAILED; - } -} - -void File::DoInitialize(const FilePath& path, uint32_t flags) { - AssertBlockingAllowed(); - DCHECK(!IsValid()); - - DWORD disposition = 0; - - if (flags & FLAG_OPEN) - disposition = OPEN_EXISTING; - - if (flags & FLAG_CREATE) { - DCHECK(!disposition); - disposition = CREATE_NEW; - } - - if (flags & FLAG_OPEN_ALWAYS) { - DCHECK(!disposition); - disposition = OPEN_ALWAYS; - } - - if (flags & FLAG_CREATE_ALWAYS) { - DCHECK(!disposition); - DCHECK(flags & FLAG_WRITE); - disposition = CREATE_ALWAYS; - } - - if (flags & FLAG_OPEN_TRUNCATED) { - DCHECK(!disposition); - DCHECK(flags & FLAG_WRITE); - disposition = TRUNCATE_EXISTING; - } - - if (!disposition) { - ::SetLastError(ERROR_INVALID_PARAMETER); - error_details_ = FILE_ERROR_FAILED; - NOTREACHED(); - return; - } - - DWORD access = 0; - if (flags & FLAG_WRITE) - access = GENERIC_WRITE; - if (flags & FLAG_APPEND) { - DCHECK(!access); - access = FILE_APPEND_DATA; - } - if (flags & FLAG_READ) - access |= GENERIC_READ; - if (flags & FLAG_WRITE_ATTRIBUTES) - access |= FILE_WRITE_ATTRIBUTES; - if (flags & FLAG_EXECUTE) - access |= GENERIC_EXECUTE; - if (flags & FLAG_CAN_DELETE_ON_CLOSE) - access |= DELETE; - - DWORD sharing = (flags & FLAG_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ; - if (!(flags & FLAG_EXCLUSIVE_WRITE)) - sharing |= FILE_SHARE_WRITE; - if (flags & FLAG_SHARE_DELETE) - sharing |= FILE_SHARE_DELETE; - - DWORD create_flags = 0; - if (flags & FLAG_ASYNC) - create_flags |= FILE_FLAG_OVERLAPPED; - if (flags & FLAG_TEMPORARY) - create_flags |= FILE_ATTRIBUTE_TEMPORARY; - if (flags & FLAG_HIDDEN) - create_flags |= FILE_ATTRIBUTE_HIDDEN; - if (flags & FLAG_DELETE_ON_CLOSE) - create_flags |= FILE_FLAG_DELETE_ON_CLOSE; - if (flags & FLAG_BACKUP_SEMANTICS) - create_flags |= FILE_FLAG_BACKUP_SEMANTICS; - if (flags & FLAG_SEQUENTIAL_SCAN) - create_flags |= FILE_FLAG_SEQUENTIAL_SCAN; - - file_.Set(CreateFile(path.value().c_str(), access, sharing, NULL, - disposition, create_flags, NULL)); - - if (file_.IsValid()) { - error_details_ = FILE_OK; - async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC); - - if (flags & (FLAG_OPEN_ALWAYS)) - created_ = (ERROR_ALREADY_EXISTS != GetLastError()); - else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)) - created_ = true; - } else { - error_details_ = GetLastFileError(); - } -} - -bool File::Flush() { - AssertBlockingAllowed(); - DCHECK(IsValid()); - SCOPED_FILE_TRACE("Flush"); - return ::FlushFileBuffers(file_.Get()) != FALSE; -} - -void File::SetPlatformFile(PlatformFile file) { - file_.Set(file); -} - -// static -File::Error File::GetLastFileError() { - return File::OSErrorToFileError(GetLastError()); -} - -} // namespace base diff --git a/files/important_file_writer.cc b/files/important_file_writer.cc deleted file mode 100644 index 235bb8d36..000000000 --- a/files/important_file_writer.cc +++ /dev/null @@ -1,315 +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/files/important_file_writer.h" - -#include -#include -#include -#include -#include - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/critical_closure.h" -#include "base/debug/alias.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" -#include "base/numerics/safe_conversions.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/task_runner.h" -#include "base/task_runner_util.h" -#include "base/threading/thread.h" -#include "base/time/time.h" -#include "build/build_config.h" - -namespace base { - -namespace { - -constexpr auto kDefaultCommitInterval = TimeDelta::FromSeconds(10); - -// This enum is used to define the buckets for an enumerated UMA histogram. -// Hence, -// (a) existing enumerated constants should never be deleted or reordered, and -// (b) new constants should only be appended at the end of the enumeration. -enum TempFileFailure { - FAILED_CREATING, - FAILED_OPENING, - FAILED_CLOSING, // Unused. - FAILED_WRITING, - FAILED_RENAMING, - FAILED_FLUSHING, - TEMP_FILE_FAILURE_MAX -}; - -// Helper function to write samples to a histogram with a dynamically assigned -// histogram name. Works with different error code types convertible to int -// which is the actual argument type of UmaHistogramExactLinear. -template -void UmaHistogramExactLinearWithSuffix(const char* histogram_name, - StringPiece histogram_suffix, - SampleType add_sample, - SampleType max_sample) { - static_assert(std::is_convertible::value, - "SampleType should be convertible to int"); - DCHECK(histogram_name); - std::string histogram_full_name(histogram_name); - if (!histogram_suffix.empty()) { - histogram_full_name.append("."); - histogram_full_name.append(histogram_suffix.data(), - histogram_suffix.length()); - } - UmaHistogramExactLinear(histogram_full_name, static_cast(add_sample), - static_cast(max_sample)); -} - -// Helper function to write samples to a histogram with a dynamically assigned -// histogram name. Works with short timings from 1 ms up to 10 seconds (50 -// buckets) which is the actual argument type of UmaHistogramTimes. -void UmaHistogramTimesWithSuffix(const char* histogram_name, - StringPiece histogram_suffix, - TimeDelta sample) { - DCHECK(histogram_name); - std::string histogram_full_name(histogram_name); - if (!histogram_suffix.empty()) { - histogram_full_name.append("."); - histogram_full_name.append(histogram_suffix.data(), - histogram_suffix.length()); - } - UmaHistogramTimes(histogram_full_name, sample); -} - -void LogFailure(const FilePath& path, - StringPiece histogram_suffix, - TempFileFailure failure_code, - StringPiece message) { - UmaHistogramExactLinearWithSuffix("ImportantFile.TempFileFailures", - histogram_suffix, failure_code, - TEMP_FILE_FAILURE_MAX); - DPLOG(WARNING) << "temp file failure: " << path.value() << " : " << message; -} - -// Helper function to call WriteFileAtomically() with a -// std::unique_ptr. -void WriteScopedStringToFileAtomically( - const FilePath& path, - std::unique_ptr data, - Closure before_write_callback, - Callback after_write_callback, - const std::string& histogram_suffix) { - if (!before_write_callback.is_null()) - before_write_callback.Run(); - - TimeTicks start_time = TimeTicks::Now(); - bool result = - ImportantFileWriter::WriteFileAtomically(path, *data, histogram_suffix); - if (result) { - UmaHistogramTimesWithSuffix("ImportantFile.TimeToWrite", histogram_suffix, - TimeTicks::Now() - start_time); - } - - if (!after_write_callback.is_null()) - after_write_callback.Run(result); -} - -void DeleteTmpFile(const FilePath& tmp_file_path, - StringPiece histogram_suffix) { - if (!DeleteFile(tmp_file_path, false)) { - UmaHistogramExactLinearWithSuffix( - "ImportantFile.FileDeleteError", histogram_suffix, - -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX); - } -} - -} // namespace - -// static -bool ImportantFileWriter::WriteFileAtomically(const FilePath& path, - StringPiece data, - StringPiece histogram_suffix) { -#if defined(OS_CHROMEOS) - // On Chrome OS, chrome gets killed when it cannot finish shutdown quickly, - // and this function seems to be one of the slowest shutdown steps. - // Include some info to the report for investigation. crbug.com/418627 - // TODO(hashimoto): Remove this. - struct { - size_t data_size; - char path[128]; - } file_info; - file_info.data_size = data.size(); - strlcpy(file_info.path, path.value().c_str(), arraysize(file_info.path)); - debug::Alias(&file_info); -#endif - - // Write the data to a temp file then rename to avoid data loss if we crash - // while writing the file. Ensure that the temp file is on the same volume - // as target file, so it can be moved in one step, and that the temp file - // is securely created. - FilePath tmp_file_path; - if (!CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) { - UmaHistogramExactLinearWithSuffix( - "ImportantFile.FileCreateError", histogram_suffix, - -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX); - LogFailure(path, histogram_suffix, FAILED_CREATING, - "could not create temporary file"); - return false; - } - - File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE); - if (!tmp_file.IsValid()) { - UmaHistogramExactLinearWithSuffix( - "ImportantFile.FileOpenError", histogram_suffix, - -tmp_file.error_details(), -base::File::FILE_ERROR_MAX); - LogFailure(path, histogram_suffix, FAILED_OPENING, - "could not open temporary file"); - DeleteFile(tmp_file_path, false); - return false; - } - - // If this fails in the wild, something really bad is going on. - const int data_length = checked_cast(data.length()); - int bytes_written = tmp_file.Write(0, data.data(), data_length); - if (bytes_written < data_length) { - UmaHistogramExactLinearWithSuffix( - "ImportantFile.FileWriteError", histogram_suffix, - -base::File::GetLastFileError(), -base::File::FILE_ERROR_MAX); - } - bool flush_success = tmp_file.Flush(); - tmp_file.Close(); - - if (bytes_written < data_length) { - LogFailure(path, histogram_suffix, FAILED_WRITING, - "error writing, bytes_written=" + IntToString(bytes_written)); - DeleteTmpFile(tmp_file_path, histogram_suffix); - return false; - } - - if (!flush_success) { - LogFailure(path, histogram_suffix, FAILED_FLUSHING, "error flushing"); - DeleteTmpFile(tmp_file_path, histogram_suffix); - return false; - } - - base::File::Error replace_file_error = base::File::FILE_OK; - if (!ReplaceFile(tmp_file_path, path, &replace_file_error)) { - UmaHistogramExactLinearWithSuffix("ImportantFile.FileRenameError", - histogram_suffix, -replace_file_error, - -base::File::FILE_ERROR_MAX); - LogFailure(path, histogram_suffix, FAILED_RENAMING, - "could not rename temporary file"); - DeleteTmpFile(tmp_file_path, histogram_suffix); - return false; - } - - return true; -} - -ImportantFileWriter::ImportantFileWriter( - const FilePath& path, - scoped_refptr task_runner, - const char* histogram_suffix) - : ImportantFileWriter(path, - std::move(task_runner), - kDefaultCommitInterval, - histogram_suffix) {} - -ImportantFileWriter::ImportantFileWriter( - const FilePath& path, - scoped_refptr task_runner, - TimeDelta interval, - const char* histogram_suffix) - : path_(path), - task_runner_(std::move(task_runner)), - serializer_(nullptr), - commit_interval_(interval), - histogram_suffix_(histogram_suffix ? histogram_suffix : ""), - weak_factory_(this) { - DCHECK(task_runner_); -} - -ImportantFileWriter::~ImportantFileWriter() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // We're usually a member variable of some other object, which also tends - // to be our serializer. It may not be safe to call back to the parent object - // being destructed. - DCHECK(!HasPendingWrite()); -} - -bool ImportantFileWriter::HasPendingWrite() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return timer().IsRunning(); -} - -void ImportantFileWriter::WriteNow(std::unique_ptr data) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!IsValueInRangeForNumericType(data->length())) { - NOTREACHED(); - return; - } - - Closure task = AdaptCallbackForRepeating( - BindOnce(&WriteScopedStringToFileAtomically, path_, std::move(data), - std::move(before_next_write_callback_), - std::move(after_next_write_callback_), histogram_suffix_)); - - if (!task_runner_->PostTask(FROM_HERE, MakeCriticalClosure(task))) { - // Posting the task to background message loop is not expected - // to fail, but if it does, avoid losing data and just hit the disk - // on the current thread. - NOTREACHED(); - - task.Run(); - } - ClearPendingWrite(); -} - -void ImportantFileWriter::ScheduleWrite(DataSerializer* serializer) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - DCHECK(serializer); - serializer_ = serializer; - - if (!timer().IsRunning()) { - timer().Start( - FROM_HERE, commit_interval_, - Bind(&ImportantFileWriter::DoScheduledWrite, Unretained(this))); - } -} - -void ImportantFileWriter::DoScheduledWrite() { - DCHECK(serializer_); - std::unique_ptr data(new std::string); - if (serializer_->SerializeData(data.get())) { - WriteNow(std::move(data)); - } else { - DLOG(WARNING) << "failed to serialize data to be saved in " - << path_.value(); - } - ClearPendingWrite(); -} - -void ImportantFileWriter::RegisterOnNextWriteCallbacks( - const Closure& before_next_write_callback, - const Callback& after_next_write_callback) { - before_next_write_callback_ = before_next_write_callback; - after_next_write_callback_ = after_next_write_callback; -} - -void ImportantFileWriter::ClearPendingWrite() { - timer().Stop(); - serializer_ = nullptr; -} - -void ImportantFileWriter::SetTimerForTesting(Timer* timer_override) { - timer_override_ = timer_override; -} - -} // namespace base diff --git a/files/important_file_writer.h b/files/important_file_writer.h deleted file mode 100644 index 08a7ee34b..000000000 --- a/files/important_file_writer.h +++ /dev/null @@ -1,162 +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_FILES_IMPORTANT_FILE_WRITER_H_ -#define BASE_FILES_IMPORTANT_FILE_WRITER_H_ - -#include - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/sequence_checker.h" -#include "base/strings/string_piece.h" -#include "base/time/time.h" -#include "base/timer/timer.h" - -namespace base { - -class SequencedTaskRunner; - -// Helper for atomically writing a file to ensure that it won't be corrupted by -// *application* crash during write (implemented as create, flush, rename). -// -// As an added benefit, ImportantFileWriter makes it less likely that the file -// is corrupted by *system* crash, though even if the ImportantFileWriter call -// has already returned at the time of the crash it is not specified which -// version of the file (old or new) is preserved. And depending on system -// configuration (hardware and software) a significant likelihood of file -// corruption may remain, thus using ImportantFileWriter is not a valid -// substitute for file integrity checks and recovery codepaths for malformed -// files. -// -// Also note that ImportantFileWriter can be *really* slow (cf. File::Flush() -// for details) and thus please don't block shutdown on ImportantFileWriter. -class BASE_EXPORT ImportantFileWriter { - public: - // Used by ScheduleSave to lazily provide the data to be saved. Allows us - // to also batch data serializations. - class BASE_EXPORT DataSerializer { - public: - // Should put serialized string in |data| and return true on successful - // serialization. Will be called on the same thread on which - // ImportantFileWriter has been created. - virtual bool SerializeData(std::string* data) = 0; - - protected: - virtual ~DataSerializer() = default; - }; - - // Save |data| to |path| in an atomic manner. Blocks and writes data on the - // current thread. Does not guarantee file integrity across system crash (see - // the class comment above). - static bool WriteFileAtomically(const FilePath& path, - StringPiece data, - StringPiece histogram_suffix = StringPiece()); - - // Initialize the writer. - // |path| is the name of file to write. - // |task_runner| is the SequencedTaskRunner instance where on which we will - // execute file I/O operations. - // All non-const methods, ctor and dtor must be called on the same thread. - ImportantFileWriter(const FilePath& path, - scoped_refptr task_runner, - const char* histogram_suffix = nullptr); - - // Same as above, but with a custom commit interval. - ImportantFileWriter(const FilePath& path, - scoped_refptr task_runner, - TimeDelta interval, - const char* histogram_suffix = nullptr); - - // You have to ensure that there are no pending writes at the moment - // of destruction. - ~ImportantFileWriter(); - - const FilePath& path() const { return path_; } - - // Returns true if there is a scheduled write pending which has not yet - // been started. - bool HasPendingWrite() const; - - // Save |data| to target filename. Does not block. If there is a pending write - // scheduled by ScheduleWrite(), it is cancelled. - void WriteNow(std::unique_ptr data); - - // Schedule a save to target filename. Data will be serialized and saved - // to disk after the commit interval. If another ScheduleWrite is issued - // before that, only one serialization and write to disk will happen, and - // the most recent |serializer| will be used. This operation does not block. - // |serializer| should remain valid through the lifetime of - // ImportantFileWriter. - void ScheduleWrite(DataSerializer* serializer); - - // Serialize data pending to be saved and execute write on backend thread. - void DoScheduledWrite(); - - // Registers |before_next_write_callback| and |after_next_write_callback| to - // be synchronously invoked from WriteFileAtomically() before its next write - // and after its next write, respectively. The boolean passed to - // |after_next_write_callback| indicates whether the write was successful. - // Both callbacks must be thread safe as they will be called on |task_runner_| - // and may be called during Chrome shutdown. - // If called more than once before a write is scheduled on |task_runner|, the - // latest callbacks clobber the others. - void RegisterOnNextWriteCallbacks( - const Closure& before_next_write_callback, - const Callback& after_next_write_callback); - - TimeDelta commit_interval() const { - return commit_interval_; - } - - // Overrides the timer to use for scheduling writes with |timer_override|. - void SetTimerForTesting(Timer* timer_override); - - private: - const Timer& timer() const { - return timer_override_ ? const_cast(*timer_override_) - : timer_; - } - Timer& timer() { return timer_override_ ? *timer_override_ : timer_; } - - void ClearPendingWrite(); - - // Invoked synchronously on the next write event. - Closure before_next_write_callback_; - Callback after_next_write_callback_; - - // Path being written to. - const FilePath path_; - - // TaskRunner for the thread on which file I/O can be done. - const scoped_refptr task_runner_; - - // Timer used to schedule commit after ScheduleWrite. - OneShotTimer timer_; - - // An override for |timer_| used for testing. - Timer* timer_override_ = nullptr; - - // Serializer which will provide the data to be saved. - DataSerializer* serializer_; - - // Time delta after which scheduled data will be written to disk. - const TimeDelta commit_interval_; - - // Custom histogram suffix. - const std::string histogram_suffix_; - - SEQUENCE_CHECKER(sequence_checker_); - - WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(ImportantFileWriter); -}; - -} // namespace base - -#endif // BASE_FILES_IMPORTANT_FILE_WRITER_H_ diff --git a/files/important_file_writer_unittest.cc b/files/important_file_writer_unittest.cc deleted file mode 100644 index 5dddc7145..000000000 --- a/files/important_file_writer_unittest.cc +++ /dev/null @@ -1,351 +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/files/important_file_writer.h" - -#include "base/bind.h" -#include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/test/metrics/histogram_tester.h" -#include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/time/time.h" -#include "base/timer/mock_timer.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -std::string GetFileContent(const FilePath& path) { - std::string content; - if (!ReadFileToString(path, &content)) { - NOTREACHED(); - } - return content; -} - -class DataSerializer : public ImportantFileWriter::DataSerializer { - public: - explicit DataSerializer(const std::string& data) : data_(data) { - } - - bool SerializeData(std::string* output) override { - output->assign(data_); - return true; - } - - private: - const std::string data_; -}; - -class FailingDataSerializer : public ImportantFileWriter::DataSerializer { - public: - bool SerializeData(std::string* output) override { return false; } -}; - -enum WriteCallbackObservationState { - NOT_CALLED, - CALLED_WITH_ERROR, - CALLED_WITH_SUCCESS, -}; - -class WriteCallbacksObserver { - public: - WriteCallbacksObserver() = default; - - // Register OnBeforeWrite() and OnAfterWrite() to be called on the next write - // of |writer|. - void ObserveNextWriteCallbacks(ImportantFileWriter* writer); - - // Returns the |WriteCallbackObservationState| which was observed, then resets - // it to |NOT_CALLED|. - WriteCallbackObservationState GetAndResetObservationState(); - - private: - void OnBeforeWrite() { - EXPECT_FALSE(before_write_called_); - before_write_called_ = true; - } - - void OnAfterWrite(bool success) { - EXPECT_EQ(NOT_CALLED, after_write_observation_state_); - after_write_observation_state_ = - success ? CALLED_WITH_SUCCESS : CALLED_WITH_ERROR; - } - - bool before_write_called_ = false; - WriteCallbackObservationState after_write_observation_state_ = NOT_CALLED; - - DISALLOW_COPY_AND_ASSIGN(WriteCallbacksObserver); -}; - -void WriteCallbacksObserver::ObserveNextWriteCallbacks( - ImportantFileWriter* writer) { - writer->RegisterOnNextWriteCallbacks( - base::Bind(&WriteCallbacksObserver::OnBeforeWrite, - base::Unretained(this)), - base::Bind(&WriteCallbacksObserver::OnAfterWrite, - base::Unretained(this))); -} - -WriteCallbackObservationState -WriteCallbacksObserver::GetAndResetObservationState() { - EXPECT_EQ(after_write_observation_state_ != NOT_CALLED, before_write_called_) - << "The before-write callback should always be called before the " - "after-write callback"; - - WriteCallbackObservationState state = after_write_observation_state_; - before_write_called_ = false; - after_write_observation_state_ = NOT_CALLED; - return state; -} - -} // namespace - -class ImportantFileWriterTest : public testing::Test { - public: - ImportantFileWriterTest() = default; - void SetUp() override { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - file_ = temp_dir_.GetPath().AppendASCII("test-file"); - } - - protected: - WriteCallbacksObserver write_callback_observer_; - FilePath file_; - MessageLoop loop_; - - private: - ScopedTempDir temp_dir_; -}; - -TEST_F(ImportantFileWriterTest, Basic) { - ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); - EXPECT_FALSE(PathExists(writer.path())); - EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); - writer.WriteNow(std::make_unique("foo")); - RunLoop().RunUntilIdle(); - - EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); - ASSERT_TRUE(PathExists(writer.path())); - EXPECT_EQ("foo", GetFileContent(writer.path())); -} - -TEST_F(ImportantFileWriterTest, WriteWithObserver) { - ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); - EXPECT_FALSE(PathExists(writer.path())); - EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); - - // Confirm that the observer is invoked. - write_callback_observer_.ObserveNextWriteCallbacks(&writer); - writer.WriteNow(std::make_unique("foo")); - RunLoop().RunUntilIdle(); - - EXPECT_EQ(CALLED_WITH_SUCCESS, - write_callback_observer_.GetAndResetObservationState()); - ASSERT_TRUE(PathExists(writer.path())); - EXPECT_EQ("foo", GetFileContent(writer.path())); - - // Confirm that re-installing the observer works for another write. - EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); - write_callback_observer_.ObserveNextWriteCallbacks(&writer); - writer.WriteNow(std::make_unique("bar")); - RunLoop().RunUntilIdle(); - - EXPECT_EQ(CALLED_WITH_SUCCESS, - write_callback_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_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); - writer.WriteNow(std::make_unique("baz")); - RunLoop().RunUntilIdle(); - - EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); - ASSERT_TRUE(PathExists(writer.path())); - EXPECT_EQ("baz", GetFileContent(writer.path())); -} - -TEST_F(ImportantFileWriterTest, FailedWriteWithObserver) { - // Use an invalid file path (relative paths are invalid) to get a - // FILE_ERROR_ACCESS_DENIED error when trying to write the file. - ImportantFileWriter writer(FilePath().AppendASCII("bad/../path"), - ThreadTaskRunnerHandle::Get()); - EXPECT_FALSE(PathExists(writer.path())); - EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); - write_callback_observer_.ObserveNextWriteCallbacks(&writer); - writer.WriteNow(std::make_unique("foo")); - RunLoop().RunUntilIdle(); - - // Confirm that the write observer was invoked with its boolean parameter set - // to false. - EXPECT_EQ(CALLED_WITH_ERROR, - write_callback_observer_.GetAndResetObservationState()); - EXPECT_FALSE(PathExists(writer.path())); -} - -TEST_F(ImportantFileWriterTest, CallbackRunsOnWriterThread) { - base::Thread file_writer_thread("ImportantFileWriter test thread"); - file_writer_thread.Start(); - ImportantFileWriter writer(file_, file_writer_thread.task_runner()); - EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); - - // Block execution on |file_writer_thread| to verify that callbacks are - // executed on it. - base::WaitableEvent wait_helper( - base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED); - file_writer_thread.task_runner()->PostTask( - FROM_HERE, base::BindOnce(&base::WaitableEvent::Wait, - base::Unretained(&wait_helper))); - - write_callback_observer_.ObserveNextWriteCallbacks(&writer); - writer.WriteNow(std::make_unique("foo")); - RunLoop().RunUntilIdle(); - - // Expect the callback to not have been executed before the - // |file_writer_thread| is unblocked. - EXPECT_EQ(NOT_CALLED, write_callback_observer_.GetAndResetObservationState()); - - wait_helper.Signal(); - file_writer_thread.FlushForTesting(); - - EXPECT_EQ(CALLED_WITH_SUCCESS, - write_callback_observer_.GetAndResetObservationState()); - ASSERT_TRUE(PathExists(writer.path())); - EXPECT_EQ("foo", GetFileContent(writer.path())); -} - -TEST_F(ImportantFileWriterTest, ScheduleWrite) { - constexpr TimeDelta kCommitInterval = TimeDelta::FromSeconds(12345); - MockOneShotTimer timer; - ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get(), - kCommitInterval); - writer.SetTimerForTesting(&timer); - EXPECT_FALSE(writer.HasPendingWrite()); - DataSerializer serializer("foo"); - writer.ScheduleWrite(&serializer); - EXPECT_TRUE(writer.HasPendingWrite()); - ASSERT_TRUE(timer.IsRunning()); - EXPECT_EQ(kCommitInterval, timer.GetCurrentDelay()); - timer.Fire(); - EXPECT_FALSE(writer.HasPendingWrite()); - EXPECT_FALSE(timer.IsRunning()); - RunLoop().RunUntilIdle(); - ASSERT_TRUE(PathExists(writer.path())); - EXPECT_EQ("foo", GetFileContent(writer.path())); -} - -TEST_F(ImportantFileWriterTest, DoScheduledWrite) { - MockOneShotTimer timer; - ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); - writer.SetTimerForTesting(&timer); - EXPECT_FALSE(writer.HasPendingWrite()); - DataSerializer serializer("foo"); - writer.ScheduleWrite(&serializer); - EXPECT_TRUE(writer.HasPendingWrite()); - writer.DoScheduledWrite(); - EXPECT_FALSE(writer.HasPendingWrite()); - RunLoop().RunUntilIdle(); - ASSERT_TRUE(PathExists(writer.path())); - EXPECT_EQ("foo", GetFileContent(writer.path())); -} - -TEST_F(ImportantFileWriterTest, BatchingWrites) { - MockOneShotTimer timer; - ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); - writer.SetTimerForTesting(&timer); - DataSerializer foo("foo"), bar("bar"), baz("baz"); - writer.ScheduleWrite(&foo); - writer.ScheduleWrite(&bar); - writer.ScheduleWrite(&baz); - ASSERT_TRUE(timer.IsRunning()); - timer.Fire(); - RunLoop().RunUntilIdle(); - ASSERT_TRUE(PathExists(writer.path())); - EXPECT_EQ("baz", GetFileContent(writer.path())); -} - -TEST_F(ImportantFileWriterTest, ScheduleWrite_FailToSerialize) { - MockOneShotTimer timer; - ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); - writer.SetTimerForTesting(&timer); - EXPECT_FALSE(writer.HasPendingWrite()); - FailingDataSerializer serializer; - writer.ScheduleWrite(&serializer); - EXPECT_TRUE(writer.HasPendingWrite()); - ASSERT_TRUE(timer.IsRunning()); - timer.Fire(); - EXPECT_FALSE(writer.HasPendingWrite()); - RunLoop().RunUntilIdle(); - EXPECT_FALSE(PathExists(writer.path())); -} - -TEST_F(ImportantFileWriterTest, ScheduleWrite_WriteNow) { - MockOneShotTimer timer; - ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); - writer.SetTimerForTesting(&timer); - EXPECT_FALSE(writer.HasPendingWrite()); - DataSerializer serializer("foo"); - writer.ScheduleWrite(&serializer); - EXPECT_TRUE(writer.HasPendingWrite()); - writer.WriteNow(std::make_unique("bar")); - EXPECT_FALSE(writer.HasPendingWrite()); - EXPECT_FALSE(timer.IsRunning()); - - RunLoop().RunUntilIdle(); - ASSERT_TRUE(PathExists(writer.path())); - EXPECT_EQ("bar", GetFileContent(writer.path())); -} - -TEST_F(ImportantFileWriterTest, DoScheduledWrite_FailToSerialize) { - MockOneShotTimer timer; - ImportantFileWriter writer(file_, ThreadTaskRunnerHandle::Get()); - writer.SetTimerForTesting(&timer); - EXPECT_FALSE(writer.HasPendingWrite()); - FailingDataSerializer serializer; - writer.ScheduleWrite(&serializer); - EXPECT_TRUE(writer.HasPendingWrite()); - - writer.DoScheduledWrite(); - EXPECT_FALSE(timer.IsRunning()); - EXPECT_FALSE(writer.HasPendingWrite()); - RunLoop().RunUntilIdle(); - EXPECT_FALSE(PathExists(writer.path())); -} - -TEST_F(ImportantFileWriterTest, WriteFileAtomicallyHistogramSuffixTest) { - base::HistogramTester histogram_tester; - EXPECT_FALSE(PathExists(file_)); - EXPECT_TRUE(ImportantFileWriter::WriteFileAtomically(file_, "baz", "test")); - EXPECT_TRUE(PathExists(file_)); - EXPECT_EQ("baz", GetFileContent(file_)); - histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError", 0); - histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError.test", 0); - - FilePath invalid_file_ = FilePath().AppendASCII("bad/../non_existent/path"); - EXPECT_FALSE(PathExists(invalid_file_)); - EXPECT_FALSE( - ImportantFileWriter::WriteFileAtomically(invalid_file_, nullptr)); - histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError", 1); - histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError.test", 0); - EXPECT_FALSE( - ImportantFileWriter::WriteFileAtomically(invalid_file_, nullptr, "test")); - histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError", 1); - histogram_tester.ExpectTotalCount("ImportantFile.FileCreateError.test", 1); -} - -} // namespace base diff --git a/files/memory_mapped_file.cc b/files/memory_mapped_file.cc deleted file mode 100644 index ccd9e2366..000000000 --- a/files/memory_mapped_file.cc +++ /dev/null @@ -1,130 +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/files/memory_mapped_file.h" - -#include - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/numerics/safe_math.h" -#include "base/sys_info.h" -#include "build/build_config.h" - -namespace base { - -const MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile = {0, 0}; - -bool MemoryMappedFile::Region::operator==( - const MemoryMappedFile::Region& other) const { - return other.offset == offset && other.size == size; -} - -bool MemoryMappedFile::Region::operator!=( - const MemoryMappedFile::Region& other) const { - return other.offset != offset || other.size != size; -} - -MemoryMappedFile::~MemoryMappedFile() { - CloseHandles(); -} - -#if !defined(OS_NACL) -bool MemoryMappedFile::Initialize(const FilePath& file_name, Access access) { - if (IsValid()) - return false; - - uint32_t flags = 0; - switch (access) { - case READ_ONLY: - flags = File::FLAG_OPEN | File::FLAG_READ; - break; - case READ_WRITE: - flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE; - break; - case READ_WRITE_EXTEND: - // Can't open with "extend" because no maximum size is known. - NOTREACHED(); - } - file_.Initialize(file_name, flags); - - if (!file_.IsValid()) { - DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe(); - return false; - } - - if (!MapFileRegionToMemory(Region::kWholeFile, access)) { - CloseHandles(); - return false; - } - - return true; -} - -bool MemoryMappedFile::Initialize(File file, Access access) { - DCHECK_NE(READ_WRITE_EXTEND, access); - return Initialize(std::move(file), Region::kWholeFile, access); -} - -bool MemoryMappedFile::Initialize(File file, - const Region& region, - Access access) { - switch (access) { - case READ_WRITE_EXTEND: - DCHECK(Region::kWholeFile != region); - { - CheckedNumeric region_end(region.offset); - region_end += region.size; - if (!region_end.IsValid()) { - DLOG(ERROR) << "Region bounds exceed maximum for base::File."; - return false; - } - } - FALLTHROUGH; - case READ_ONLY: - case READ_WRITE: - // Ensure that the region values are valid. - if (region.offset < 0) { - DLOG(ERROR) << "Region bounds are not valid."; - return false; - } - break; - } - - if (IsValid()) - return false; - - if (region != Region::kWholeFile) - DCHECK_GE(region.offset, 0); - - file_ = std::move(file); - - if (!MapFileRegionToMemory(region, access)) { - CloseHandles(); - return false; - } - - return true; -} - -bool MemoryMappedFile::IsValid() const { - return data_ != nullptr; -} - -// static -void MemoryMappedFile::CalculateVMAlignedBoundaries(int64_t start, - size_t size, - int64_t* aligned_start, - size_t* aligned_size, - int32_t* offset) { - // Sadly, on Windows, the mmap alignment is not just equal to the page size. - auto mask = SysInfo::VMAllocationGranularity() - 1; - DCHECK(IsValueInRangeForNumericType(mask)); - *offset = start & mask; - *aligned_start = start & ~mask; - *aligned_size = (size + *offset + mask) & ~mask; -} -#endif // !defined(OS_NACL) - -} // namespace base diff --git a/files/memory_mapped_file.h b/files/memory_mapped_file.h deleted file mode 100644 index 04f43367d..000000000 --- a/files/memory_mapped_file.h +++ /dev/null @@ -1,136 +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_FILES_MEMORY_MAPPED_FILE_H_ -#define BASE_FILES_MEMORY_MAPPED_FILE_H_ - -#include -#include - -#include "base/base_export.h" -#include "base/files/file.h" -#include "base/macros.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#endif - -namespace base { - -class FilePath; - -class BASE_EXPORT MemoryMappedFile { - public: - enum Access { - // Mapping a file into memory effectively allows for file I/O on any thread. - // The accessing thread could be paused while data from the file is paged - // into memory. Worse, a corrupted filesystem could cause a SEGV within the - // program instead of just an I/O error. - READ_ONLY, - - // This provides read/write access to a file and must be used with care of - // the additional subtleties involved in doing so. Though the OS will do - // the writing of data on its own time, too many dirty pages can cause - // the OS to pause the thread while it writes them out. The pause can - // be as much as 1s on some systems. - READ_WRITE, - - // This provides read/write access but with the ability to write beyond - // the end of the existing file up to a maximum size specified as the - // "region". Depending on the OS, the file may or may not be immediately - // extended to the maximum size though it won't be loaded in RAM until - // needed. Note, however, that the maximum size will still be reserved - // in the process address space. - READ_WRITE_EXTEND, - }; - - // The default constructor sets all members to invalid/null values. - MemoryMappedFile(); - ~MemoryMappedFile(); - - // Used to hold information about a region [offset + size] of a file. - struct BASE_EXPORT Region { - static const Region kWholeFile; - - bool operator==(const Region& other) const; - bool operator!=(const Region& other) const; - - // Start of the region (measured in bytes from the beginning of the file). - int64_t offset; - - // Length of the region in bytes. - size_t size; - }; - - // Opens an existing file and maps it into memory. |access| can be read-only - // or read/write but not read/write+extend. If this object already points - // to a valid memory mapped file then this method will fail and return - // false. If it cannot open the file, the file does not exist, or the - // memory mapping fails, it will return false. - bool Initialize(const FilePath& file_name, Access access); - bool Initialize(const FilePath& file_name) { - return Initialize(file_name, READ_ONLY); - } - - // As above, but works with an already-opened file. |access| can be read-only - // or read/write but not read/write+extend. MemoryMappedFile takes ownership - // of |file| and closes it when done. |file| must have been opened with - // permissions suitable for |access|. If the memory mapping fails, it will - // return false. - bool Initialize(File file, Access access); - bool Initialize(File file) { - return Initialize(std::move(file), READ_ONLY); - } - - // As above, but works with a region of an already-opened file. All forms of - // |access| are allowed. If READ_WRITE_EXTEND is specified then |region| - // provides the maximum size of the file. If the memory mapping fails, it - // return false. - bool Initialize(File file, const Region& region, Access access); - bool Initialize(File file, const Region& region) { - return Initialize(std::move(file), region, READ_ONLY); - } - - const uint8_t* data() const { return data_; } - uint8_t* data() { return data_; } - size_t length() const { return length_; } - - // Is file_ a valid file handle that points to an open, memory mapped file? - bool IsValid() const; - - private: - // Given the arbitrarily aligned memory region [start, size], returns the - // boundaries of the region aligned to the granularity specified by the OS, - // (a page on Linux, ~32k on Windows) as follows: - // - |aligned_start| is page aligned and <= |start|. - // - |aligned_size| is a multiple of the VM granularity and >= |size|. - // - |offset| is the displacement of |start| w.r.t |aligned_start|. - static void CalculateVMAlignedBoundaries(int64_t start, - size_t size, - int64_t* aligned_start, - size_t* aligned_size, - int32_t* offset); - - // 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 MapFileRegionToMemory(const Region& region, Access access); - - // Closes all open handles. - void CloseHandles(); - - File file_; - uint8_t* data_; - size_t length_; - -#if defined(OS_WIN) - win::ScopedHandle file_mapping_; -#endif - - DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile); -}; - -} // namespace base - -#endif // BASE_FILES_MEMORY_MAPPED_FILE_H_ diff --git a/files/memory_mapped_file_posix.cc b/files/memory_mapped_file_posix.cc deleted file mode 100644 index 45a0aea6d..000000000 --- a/files/memory_mapped_file_posix.cc +++ /dev/null @@ -1,185 +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/files/memory_mapped_file.h" - -#include -#include -#include -#include -#include -#include - -#include "base/logging.h" -#include "base/numerics/safe_conversions.h" -#include "base/threading/thread_restrictions.h" -#include "build/build_config.h" - -#if defined(OS_ANDROID) -#include -#endif - -namespace base { - -MemoryMappedFile::MemoryMappedFile() : data_(nullptr), length_(0) {} - -#if !defined(OS_NACL) -bool MemoryMappedFile::MapFileRegionToMemory( - const MemoryMappedFile::Region& region, - Access access) { - AssertBlockingAllowed(); - - off_t map_start = 0; - size_t map_size = 0; - int32_t data_offset = 0; - - if (region == MemoryMappedFile::Region::kWholeFile) { - int64_t file_len = file_.GetLength(); - if (file_len < 0) { - DPLOG(ERROR) << "fstat " << file_.GetPlatformFile(); - return false; - } - if (!IsValueInRangeForNumericType(file_len)) - return false; - map_size = static_cast(file_len); - length_ = map_size; - } else { - // The region can be arbitrarily aligned. mmap, instead, requires both the - // start and size to be page-aligned. Hence, we map here the page-aligned - // outer region [|aligned_start|, |aligned_start| + |size|] which contains - // |region| and then add up the |data_offset| displacement. - int64_t aligned_start = 0; - size_t aligned_size = 0; - CalculateVMAlignedBoundaries(region.offset, - region.size, - &aligned_start, - &aligned_size, - &data_offset); - - // Ensure that the casts in the mmap call below are sane. - if (aligned_start < 0 || - !IsValueInRangeForNumericType(aligned_start)) { - DLOG(ERROR) << "Region bounds are not valid for mmap"; - return false; - } - - map_start = static_cast(aligned_start); - map_size = aligned_size; - length_ = region.size; - } - - int flags = 0; - switch (access) { - case READ_ONLY: - flags |= PROT_READ; - break; - - case READ_WRITE: - flags |= PROT_READ | PROT_WRITE; - break; - - case READ_WRITE_EXTEND: - flags |= PROT_READ | PROT_WRITE; - - const int64_t new_file_len = region.offset + region.size; - - // POSIX won't auto-extend the file when it is written so it must first - // be explicitly extended to the maximum size. Zeros will fill the new - // space. It is assumed that the existing file is fully realized as - // otherwise the entire file would have to be read and possibly written. - const int64_t original_file_len = file_.GetLength(); - if (original_file_len < 0) { - DPLOG(ERROR) << "fstat " << file_.GetPlatformFile(); - return false; - } - - // Increase the actual length of the file, if necessary. This can fail if - // the disk is full and the OS doesn't support sparse files. - if (!file_.SetLength(std::max(original_file_len, new_file_len))) { - DPLOG(ERROR) << "ftruncate " << file_.GetPlatformFile(); - return false; - } - - // Realize the extent of the file so that it can't fail (and crash) later - // when trying to write to a memory page that can't be created. This can - // fail if the disk is full and the file is sparse. - bool do_manual_extension = false; - -#if defined(OS_ANDROID) && __ANDROID_API__ < 21 - // Only Android API>=21 supports the fallocate call. Older versions need - // to manually extend the file by writing zeros at block intervals. - do_manual_extension = true; -#elif defined(OS_MACOSX) - // MacOS doesn't support fallocate even though their new APFS filesystem - // does support sparse files. It does, however, have the functionality - // available via fcntl. - // See also: https://openradar.appspot.com/32720223 - fstore_t params = {F_ALLOCATEALL, F_PEOFPOSMODE, region.offset, - region.size, 0}; - if (fcntl(file_.GetPlatformFile(), F_PREALLOCATE, ¶ms) != 0) { - DPLOG(ERROR) << "F_PREALLOCATE"; - // This can fail because the filesystem doesn't support it so don't - // give up just yet. Try the manual method below. - do_manual_extension = true; - } -#else - if (posix_fallocate(file_.GetPlatformFile(), region.offset, - region.size) != 0) { - DPLOG(ERROR) << "posix_fallocate"; - // This can fail because the filesystem doesn't support it so don't - // give up just yet. Try the manual method below. - do_manual_extension = true; - } -#endif - - // Manually realize the extended file by writing bytes to it at intervals. - if (do_manual_extension) { - int64_t block_size = 512; // Start with something safe. - struct stat statbuf; - if (fstat(file_.GetPlatformFile(), &statbuf) == 0 && - statbuf.st_blksize > 0) { - block_size = statbuf.st_blksize; - } - - // Write starting at the next block boundary after the old file length. - const int64_t extension_start = - (original_file_len + block_size - 1) & ~(block_size - 1); - for (int64_t i = extension_start; i < new_file_len; i += block_size) { - char existing_byte; - if (pread(file_.GetPlatformFile(), &existing_byte, 1, i) != 1) - return false; // Can't read? Not viable. - if (existing_byte != 0) - continue; // Block has data so must already exist. - if (pwrite(file_.GetPlatformFile(), &existing_byte, 1, i) != 1) - return false; // Can't write? Not viable. - } - } - - break; - } - - data_ = static_cast(mmap(nullptr, map_size, flags, MAP_SHARED, - file_.GetPlatformFile(), map_start)); - if (data_ == MAP_FAILED) { - DPLOG(ERROR) << "mmap " << file_.GetPlatformFile(); - return false; - } - - data_ += data_offset; - return true; -} -#endif - -void MemoryMappedFile::CloseHandles() { - AssertBlockingAllowed(); - - if (data_ != nullptr) - munmap(data_, length_); - file_.Close(); - - data_ = nullptr; - length_ = 0; -} - -} // namespace base diff --git a/files/memory_mapped_file_unittest.cc b/files/memory_mapped_file_unittest.cc deleted file mode 100644 index b7acc6188..000000000 --- a/files/memory_mapped_file_unittest.cc +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/memory_mapped_file.h" - -#include -#include - -#include - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -namespace base { - -namespace { - -// Create a temporary buffer and fill it with a watermark sequence. -std::unique_ptr CreateTestBuffer(size_t size, size_t offset) { - std::unique_ptr buf(new uint8_t[size]); - for (size_t i = 0; i < size; ++i) - buf.get()[i] = static_cast((offset + i) % 253); - return buf; -} - -// Check that the watermark sequence is consistent with the |offset| provided. -bool CheckBufferContents(const uint8_t* data, size_t size, size_t offset) { - std::unique_ptr test_data(CreateTestBuffer(size, offset)); - return memcmp(test_data.get(), data, size) == 0; -} - -class MemoryMappedFileTest : public PlatformTest { - protected: - void SetUp() override { - PlatformTest::SetUp(); - CreateTemporaryFile(&temp_file_path_); - } - - void TearDown() override { EXPECT_TRUE(DeleteFile(temp_file_path_, false)); } - - void CreateTemporaryTestFile(size_t size) { - File file(temp_file_path_, - File::FLAG_CREATE_ALWAYS | File::FLAG_READ | File::FLAG_WRITE); - EXPECT_TRUE(file.IsValid()); - - std::unique_ptr test_data(CreateTestBuffer(size, 0)); - size_t bytes_written = - file.Write(0, reinterpret_cast(test_data.get()), size); - EXPECT_EQ(size, bytes_written); - file.Close(); - } - - const FilePath temp_file_path() const { return temp_file_path_; } - - private: - FilePath temp_file_path_; -}; - -TEST_F(MemoryMappedFileTest, MapWholeFileByPath) { - const size_t kFileSize = 68 * 1024; - CreateTemporaryTestFile(kFileSize); - MemoryMappedFile map; - map.Initialize(temp_file_path()); - ASSERT_EQ(kFileSize, map.length()); - ASSERT_TRUE(map.data() != nullptr); - EXPECT_TRUE(map.IsValid()); - ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0)); -} - -TEST_F(MemoryMappedFileTest, MapWholeFileByFD) { - const size_t kFileSize = 68 * 1024; - CreateTemporaryTestFile(kFileSize); - MemoryMappedFile map; - map.Initialize(File(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ)); - ASSERT_EQ(kFileSize, map.length()); - ASSERT_TRUE(map.data() != nullptr); - EXPECT_TRUE(map.IsValid()); - ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0)); -} - -TEST_F(MemoryMappedFileTest, MapSmallFile) { - const size_t kFileSize = 127; - CreateTemporaryTestFile(kFileSize); - MemoryMappedFile map; - map.Initialize(temp_file_path()); - ASSERT_EQ(kFileSize, map.length()); - ASSERT_TRUE(map.data() != nullptr); - EXPECT_TRUE(map.IsValid()); - ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0)); -} - -TEST_F(MemoryMappedFileTest, MapWholeFileUsingRegion) { - const size_t kFileSize = 157 * 1024; - CreateTemporaryTestFile(kFileSize); - MemoryMappedFile map; - - File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ); - map.Initialize(std::move(file), MemoryMappedFile::Region::kWholeFile); - ASSERT_EQ(kFileSize, map.length()); - ASSERT_TRUE(map.data() != nullptr); - EXPECT_TRUE(map.IsValid()); - ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0)); -} - -TEST_F(MemoryMappedFileTest, MapPartialRegionAtBeginning) { - const size_t kFileSize = 157 * 1024; - const size_t kPartialSize = 4 * 1024 + 32; - CreateTemporaryTestFile(kFileSize); - MemoryMappedFile map; - - File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ); - MemoryMappedFile::Region region = {0, kPartialSize}; - map.Initialize(std::move(file), region); - ASSERT_EQ(kPartialSize, map.length()); - ASSERT_TRUE(map.data() != nullptr); - EXPECT_TRUE(map.IsValid()); - ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, 0)); -} - -TEST_F(MemoryMappedFileTest, MapPartialRegionAtEnd) { - const size_t kFileSize = 157 * 1024; - const size_t kPartialSize = 5 * 1024 - 32; - const size_t kOffset = kFileSize - kPartialSize; - CreateTemporaryTestFile(kFileSize); - MemoryMappedFile map; - - File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ); - MemoryMappedFile::Region region = {kOffset, kPartialSize}; - map.Initialize(std::move(file), region); - ASSERT_EQ(kPartialSize, map.length()); - ASSERT_TRUE(map.data() != nullptr); - EXPECT_TRUE(map.IsValid()); - ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset)); -} - -TEST_F(MemoryMappedFileTest, MapSmallPartialRegionInTheMiddle) { - const size_t kFileSize = 157 * 1024; - const size_t kOffset = 1024 * 5 + 32; - const size_t kPartialSize = 8; - - CreateTemporaryTestFile(kFileSize); - MemoryMappedFile map; - - File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ); - MemoryMappedFile::Region region = {kOffset, kPartialSize}; - map.Initialize(std::move(file), region); - ASSERT_EQ(kPartialSize, map.length()); - ASSERT_TRUE(map.data() != nullptr); - EXPECT_TRUE(map.IsValid()); - ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset)); -} - -TEST_F(MemoryMappedFileTest, MapLargePartialRegionInTheMiddle) { - const size_t kFileSize = 157 * 1024; - const size_t kOffset = 1024 * 5 + 32; - const size_t kPartialSize = 16 * 1024 - 32; - - CreateTemporaryTestFile(kFileSize); - MemoryMappedFile map; - - File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ); - MemoryMappedFile::Region region = {kOffset, kPartialSize}; - map.Initialize(std::move(file), region); - ASSERT_EQ(kPartialSize, map.length()); - ASSERT_TRUE(map.data() != nullptr); - EXPECT_TRUE(map.IsValid()); - ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset)); -} - -TEST_F(MemoryMappedFileTest, WriteableFile) { - const size_t kFileSize = 127; - CreateTemporaryTestFile(kFileSize); - - { - MemoryMappedFile map; - map.Initialize(temp_file_path(), MemoryMappedFile::READ_WRITE); - ASSERT_EQ(kFileSize, map.length()); - ASSERT_TRUE(map.data() != nullptr); - EXPECT_TRUE(map.IsValid()); - ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0)); - - uint8_t* bytes = map.data(); - bytes[0] = 'B'; - bytes[1] = 'a'; - bytes[2] = 'r'; - bytes[kFileSize - 1] = '!'; - EXPECT_FALSE(CheckBufferContents(map.data(), kFileSize, 0)); - EXPECT_TRUE(CheckBufferContents(map.data() + 3, kFileSize - 4, 3)); - } - - int64_t file_size; - ASSERT_TRUE(GetFileSize(temp_file_path(), &file_size)); - EXPECT_EQ(static_cast(kFileSize), file_size); - - std::string contents; - ASSERT_TRUE(ReadFileToString(temp_file_path(), &contents)); - EXPECT_EQ("Bar", contents.substr(0, 3)); - EXPECT_EQ("!", contents.substr(kFileSize - 1, 1)); -} - -TEST_F(MemoryMappedFileTest, ExtendableFile) { - const size_t kFileSize = 127; - const size_t kFileExtend = 100; - CreateTemporaryTestFile(kFileSize); - - { - File file(temp_file_path(), - File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE); - MemoryMappedFile::Region region = {0, kFileSize + kFileExtend}; - MemoryMappedFile map; - map.Initialize(std::move(file), region, - MemoryMappedFile::READ_WRITE_EXTEND); - EXPECT_EQ(kFileSize + kFileExtend, map.length()); - ASSERT_TRUE(map.data() != nullptr); - EXPECT_TRUE(map.IsValid()); - ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0)); - - uint8_t* bytes = map.data(); - EXPECT_EQ(0, bytes[kFileSize + 0]); - EXPECT_EQ(0, bytes[kFileSize + 1]); - EXPECT_EQ(0, bytes[kFileSize + 2]); - bytes[kFileSize + 0] = 'B'; - bytes[kFileSize + 1] = 'A'; - bytes[kFileSize + 2] = 'Z'; - EXPECT_TRUE(CheckBufferContents(map.data(), kFileSize, 0)); - } - - int64_t file_size; - ASSERT_TRUE(GetFileSize(temp_file_path(), &file_size)); - EXPECT_LE(static_cast(kFileSize + 3), file_size); - EXPECT_GE(static_cast(kFileSize + kFileExtend), file_size); - - std::string contents; - ASSERT_TRUE(ReadFileToString(temp_file_path(), &contents)); - EXPECT_EQ("BAZ", contents.substr(kFileSize, 3)); -} - -} // namespace - -} // namespace base diff --git a/files/memory_mapped_file_win.cc b/files/memory_mapped_file_win.cc deleted file mode 100644 index 26869f6ac..000000000 --- a/files/memory_mapped_file_win.cc +++ /dev/null @@ -1,109 +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/files/memory_mapped_file.h" - -#include -#include - -#include - -#include "base/files/file_path.h" -#include "base/strings/string16.h" -#include "base/threading/thread_restrictions.h" - -#include - -namespace base { - -MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0) { -} - -bool MemoryMappedFile::MapFileRegionToMemory( - const MemoryMappedFile::Region& region, - Access access) { - AssertBlockingAllowed(); - - if (!file_.IsValid()) - return false; - - int flags = 0; - ULARGE_INTEGER size = {}; - switch (access) { - case READ_ONLY: - flags |= PAGE_READONLY; - break; - case READ_WRITE: - flags |= PAGE_READWRITE; - break; - case READ_WRITE_EXTEND: - flags |= PAGE_READWRITE; - size.QuadPart = region.size; - break; - } - - file_mapping_.Set(::CreateFileMapping(file_.GetPlatformFile(), NULL, flags, - size.HighPart, size.LowPart, NULL)); - if (!file_mapping_.IsValid()) - return false; - - LARGE_INTEGER map_start = {}; - SIZE_T map_size = 0; - int32_t data_offset = 0; - - if (region == MemoryMappedFile::Region::kWholeFile) { - DCHECK_NE(READ_WRITE_EXTEND, access); - int64_t file_len = file_.GetLength(); - if (file_len <= 0 || !IsValueInRangeForNumericType(file_len)) - return false; - length_ = static_cast(file_len); - } else { - // The region can be arbitrarily aligned. MapViewOfFile, instead, requires - // that the start address is aligned to the VM granularity (which is - // typically larger than a page size, for instance 32k). - // Also, conversely to POSIX's mmap, the |map_size| doesn't have to be - // aligned and must be less than or equal the mapped file size. - // We map here the outer region [|aligned_start|, |aligned_start+size|] - // which contains |region| and then add up the |data_offset| displacement. - int64_t aligned_start = 0; - size_t ignored = 0U; - CalculateVMAlignedBoundaries( - region.offset, region.size, &aligned_start, &ignored, &data_offset); - int64_t full_map_size = region.size + data_offset; - - // Ensure that the casts below in the MapViewOfFile call are sane. - if (aligned_start < 0 || full_map_size < 0 || - !IsValueInRangeForNumericType( - static_cast(full_map_size))) { - DLOG(ERROR) << "Region bounds are not valid for MapViewOfFile"; - return false; - } - map_start.QuadPart = aligned_start; - map_size = static_cast(full_map_size); - length_ = region.size; - } - - data_ = static_cast( - ::MapViewOfFile(file_mapping_.Get(), - (flags & PAGE_READONLY) ? FILE_MAP_READ : FILE_MAP_WRITE, - map_start.HighPart, map_start.LowPart, map_size)); - if (data_ == NULL) - return false; - data_ += data_offset; - return true; -} - -void MemoryMappedFile::CloseHandles() { - if (data_) - ::UnmapViewOfFile(data_); - if (file_mapping_.IsValid()) - file_mapping_.Close(); - if (file_.IsValid()) - file_.Close(); - - data_ = NULL; - length_ = 0; -} - -} // namespace base diff --git a/files/platform_file.h b/files/platform_file.h deleted file mode 100644 index 3929a0d08..000000000 --- a/files/platform_file.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2017 The Chromium 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_PLATFORM_FILE_H_ -#define BASE_FILES_PLATFORM_FILE_H_ - -#include "base/files/scoped_file.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/win/scoped_handle.h" -#include "base/win/windows_types.h" -#endif - -// This file defines platform-independent types for dealing with -// platform-dependent files. If possible, use the higher-level base::File class -// rather than these primitives. - -namespace base { - -#if defined(OS_WIN) - -using PlatformFile = HANDLE; -using ScopedPlatformFile = ::base::win::ScopedHandle; - -// It would be nice to make this constexpr but INVALID_HANDLE_VALUE is a -// ((void*)(-1)) which Clang rejects since reinterpret_cast is technically -// disallowed in constexpr. Visual Studio accepts this, however. -const PlatformFile kInvalidPlatformFile = INVALID_HANDLE_VALUE; - -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - -using PlatformFile = int; -using ScopedPlatformFile = ::base::ScopedFD; - -constexpr PlatformFile kInvalidPlatformFile = -1; - -#endif - -} // namespace - -#endif // BASE_FILES_PLATFORM_FILE_H_ diff --git a/files/scoped_file.cc b/files/scoped_file.cc deleted file mode 100644 index 1b9227d97..000000000 --- a/files/scoped_file.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/scoped_file.h" - -#include "base/logging.h" -#include "build/build_config.h" - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#include - -#include "base/posix/eintr_wrapper.h" -#endif - -namespace base { -namespace internal { - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - -// 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. - int ret = IGNORE_EINTR(close(fd)); - -#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_FUCHSIA) || \ - defined(OS_ANDROID) - // NB: Some file descriptors can return errors from close() e.g. network - // filesystems such as NFS and Linux input devices. On Linux, macOS, and - // Fuchsia's POSIX layer, errors from close other than EBADF do not indicate - // failure to actually close the fd. - if (ret != 0 && errno != EBADF) - ret = 0; -#endif - - PCHECK(0 == ret); -} - -#endif // OS_POSIX || OS_FUCHSIA - -} // namespace internal -} // namespace base diff --git a/files/scoped_file.h b/files/scoped_file.h deleted file mode 100644 index e32a60390..000000000 --- a/files/scoped_file.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FILES_SCOPED_FILE_H_ -#define BASE_FILES_SCOPED_FILE_H_ - -#include - -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/scoped_generic.h" -#include "build/build_config.h" - -namespace base { - -namespace internal { - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -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) || defined(OS_FUCHSIA) -// 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 ScopedFD; -#endif - -// Automatically closes |FILE*|s. -typedef std::unique_ptr ScopedFILE; - -} // namespace base - -#endif // BASE_FILES_SCOPED_FILE_H_ diff --git a/files/scoped_temp_dir.cc b/files/scoped_temp_dir.cc deleted file mode 100644 index 01ec0f0ca..000000000 --- a/files/scoped_temp_dir.cc +++ /dev/null @@ -1,97 +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/files/scoped_temp_dir.h" - -#include "base/files/file_util.h" -#include "base/logging.h" - -namespace base { - -namespace { - -constexpr FilePath::CharType kScopedDirPrefix[] = - FILE_PATH_LITERAL("scoped_dir"); - -} // namespace - -ScopedTempDir::ScopedTempDir() = default; - -ScopedTempDir::~ScopedTempDir() { - if (!path_.empty() && !Delete()) - DLOG(WARNING) << "Could not delete temp dir in dtor."; -} - -bool ScopedTempDir::CreateUniqueTempDir() { - if (!path_.empty()) - return false; - - // This "scoped_dir" prefix is only used on Windows and serves as a template - // for the unique name. - if (!base::CreateNewTempDirectory(kScopedDirPrefix, &path_)) - return false; - - return true; -} - -bool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) { - if (!path_.empty()) - return false; - - // If |base_path| does not exist, create it. - if (!base::CreateDirectory(base_path)) - return false; - - // Create a new, uniquely named directory under |base_path|. - if (!base::CreateTemporaryDirInDir(base_path, kScopedDirPrefix, &path_)) - return false; - - return true; -} - -bool ScopedTempDir::Set(const FilePath& path) { - if (!path_.empty()) - return false; - - if (!DirectoryExists(path) && !base::CreateDirectory(path)) - return false; - - path_ = path; - return true; -} - -bool ScopedTempDir::Delete() { - if (path_.empty()) - return false; - - bool ret = base::DeleteFile(path_, true); - if (ret) { - // We only clear the path if deleted the directory. - path_.clear(); - } - - return ret; -} - -FilePath ScopedTempDir::Take() { - FilePath ret = path_; - path_ = FilePath(); - return ret; -} - -const FilePath& ScopedTempDir::GetPath() const { - DCHECK(!path_.empty()) << "Did you call CreateUniqueTempDir* before?"; - return path_; -} - -bool ScopedTempDir::IsValid() const { - return !path_.empty() && DirectoryExists(path_); -} - -// static -const FilePath::CharType* ScopedTempDir::GetTempDirPrefix() { - return kScopedDirPrefix; -} - -} // namespace base diff --git a/files/scoped_temp_dir.h b/files/scoped_temp_dir.h deleted file mode 100644 index 872f6f81f..000000000 --- a/files/scoped_temp_dir.h +++ /dev/null @@ -1,71 +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_FILES_SCOPED_TEMP_DIR_H_ -#define BASE_FILES_SCOPED_TEMP_DIR_H_ - -// An object representing a temporary / scratch directory that should be -// cleaned up (recursively) when this object goes out of scope. Since deletion -// occurs during the destructor, no further error handling is possible if the -// directory fails to be deleted. As a result, deletion is not guaranteed by -// this class. (However note that, whenever possible, by default -// CreateUniqueTempDir creates the directory in a location that is -// automatically cleaned up on reboot, or at other appropriate times.) -// -// Multiple calls to the methods which establish a temporary directory -// (CreateUniqueTempDir, CreateUniqueTempDirUnderPath, and Set) must have -// intervening calls to Delete or Take, or the calls will fail. - -#include "base/base_export.h" -#include "base/files/file_path.h" -#include "base/macros.h" - -namespace base { - -class BASE_EXPORT ScopedTempDir { - public: - // No directory is owned/created initially. - ScopedTempDir(); - - // Recursively delete path. - ~ScopedTempDir(); - - // Creates a unique directory in TempPath, and takes ownership of it. - // See file_util::CreateNewTemporaryDirectory. - bool CreateUniqueTempDir() WARN_UNUSED_RESULT; - - // Creates a unique directory under a given path, and takes ownership of it. - bool CreateUniqueTempDirUnderPath(const FilePath& path) WARN_UNUSED_RESULT; - - // Takes ownership of directory at |path|, creating it if necessary. - // Don't call multiple times unless Take() has been called first. - bool Set(const FilePath& path) WARN_UNUSED_RESULT; - - // Deletes the temporary directory wrapped by this object. - bool Delete() WARN_UNUSED_RESULT; - - // Caller takes ownership of the temporary directory so it won't be destroyed - // when this object goes out of scope. - FilePath Take(); - - // Returns the path to the created directory. Call one of the - // CreateUniqueTempDir* methods before getting the path. - const FilePath& GetPath() const; - - // Returns true if path_ is non-empty and exists. - bool IsValid() const; - - // Returns the prefix used for temp directory names generated by - // ScopedTempDirs. - static const FilePath::CharType* GetTempDirPrefix(); - - private: - FilePath path_; - - DISALLOW_COPY_AND_ASSIGN(ScopedTempDir); -}; - -} // namespace base - -#endif // BASE_FILES_SCOPED_TEMP_DIR_H_ diff --git a/files/scoped_temp_dir_unittest.cc b/files/scoped_temp_dir_unittest.cc deleted file mode 100644 index 024b438aa..000000000 --- a/files/scoped_temp_dir_unittest.cc +++ /dev/null @@ -1,114 +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 - -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(ScopedTempDir, FullPath) { - FilePath test_path; - base::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_temp_dir"), - &test_path); - - // Against an existing dir, it should get destroyed when leaving scope. - EXPECT_TRUE(DirectoryExists(test_path)); - { - ScopedTempDir dir; - EXPECT_TRUE(dir.Set(test_path)); - EXPECT_TRUE(dir.IsValid()); - } - EXPECT_FALSE(DirectoryExists(test_path)); - - { - ScopedTempDir dir; - EXPECT_TRUE(dir.Set(test_path)); - // Now the dir doesn't exist, so ensure that it gets created. - EXPECT_TRUE(DirectoryExists(test_path)); - // When we call Release(), it shouldn't get destroyed when leaving scope. - FilePath path = dir.Take(); - EXPECT_EQ(path.value(), test_path.value()); - EXPECT_FALSE(dir.IsValid()); - } - EXPECT_TRUE(DirectoryExists(test_path)); - - // Clean up. - { - ScopedTempDir dir; - EXPECT_TRUE(dir.Set(test_path)); - } - EXPECT_FALSE(DirectoryExists(test_path)); -} - -TEST(ScopedTempDir, TempDir) { - // In this case, just verify that a directory was created and that it's a - // child of TempDir. - FilePath test_path; - { - ScopedTempDir dir; - EXPECT_TRUE(dir.CreateUniqueTempDir()); - test_path = dir.GetPath(); - EXPECT_TRUE(DirectoryExists(test_path)); - FilePath tmp_dir; - EXPECT_TRUE(base::GetTempDir(&tmp_dir)); - EXPECT_TRUE(test_path.value().find(tmp_dir.value()) != std::string::npos); - } - EXPECT_FALSE(DirectoryExists(test_path)); -} - -TEST(ScopedTempDir, UniqueTempDirUnderPath) { - // Create a path which will contain a unique temp path. - FilePath base_path; - ASSERT_TRUE(base::CreateNewTempDirectory(FILE_PATH_LITERAL("base_dir"), - &base_path)); - - FilePath test_path; - { - ScopedTempDir dir; - EXPECT_TRUE(dir.CreateUniqueTempDirUnderPath(base_path)); - test_path = dir.GetPath(); - EXPECT_TRUE(DirectoryExists(test_path)); - EXPECT_TRUE(base_path.IsParent(test_path)); - EXPECT_TRUE(test_path.value().find(base_path.value()) != std::string::npos); - } - EXPECT_FALSE(DirectoryExists(test_path)); - base::DeleteFile(base_path, true); -} - -TEST(ScopedTempDir, MultipleInvocations) { - ScopedTempDir dir; - EXPECT_TRUE(dir.CreateUniqueTempDir()); - EXPECT_FALSE(dir.CreateUniqueTempDir()); - EXPECT_TRUE(dir.Delete()); - EXPECT_TRUE(dir.CreateUniqueTempDir()); - EXPECT_FALSE(dir.CreateUniqueTempDir()); - ScopedTempDir other_dir; - EXPECT_TRUE(other_dir.Set(dir.Take())); - EXPECT_TRUE(dir.CreateUniqueTempDir()); - EXPECT_FALSE(dir.CreateUniqueTempDir()); - EXPECT_FALSE(other_dir.CreateUniqueTempDir()); -} - -#if defined(OS_WIN) -TEST(ScopedTempDir, LockedTempDir) { - ScopedTempDir dir; - EXPECT_TRUE(dir.CreateUniqueTempDir()); - base::File file(dir.GetPath().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.GetPath().empty()); // We should still have a valid path. - file.Close(); - // Now, we should be able to delete. - EXPECT_TRUE(dir.Delete()); -} -#endif // defined(OS_WIN) - -} // namespace base diff --git a/format_macros.h b/format_macros.h deleted file mode 100644 index 1279ff781..000000000 --- a/format_macros.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FORMAT_MACROS_H_ -#define BASE_FORMAT_MACROS_H_ - -// This file defines the format macros for some integer types. - -// To print a 64-bit value in a portable way: -// int64_t value; -// printf("xyz:%" PRId64, value); -// The "d" in the macro corresponds to %d; you can also use PRIu64 etc. -// -// For wide strings, prepend "Wide" to the macro: -// int64_t value; -// StringPrintf(L"xyz: %" WidePRId64, value); -// -// To print a size_t value in a portable way: -// size_t size; -// printf("xyz: %" PRIuS, size); -// The "u" in the macro corresponds to %u, and S is for "size". - -#include -#include - -#include "build/build_config.h" - -#if (defined(OS_POSIX) || defined(OS_FUCHSIA)) && \ - (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64) -#error "inttypes.h has already been included before this header file, but " -#error "without __STDC_FORMAT_MACROS defined." -#endif - -#if (defined(OS_POSIX) || defined(OS_FUCHSIA)) && !defined(__STDC_FORMAT_MACROS) -#define __STDC_FORMAT_MACROS -#endif - -#include - -#if defined(OS_WIN) - -#if !defined(PRId64) || !defined(PRIu64) || !defined(PRIx64) -#error "inttypes.h provided by win toolchain should define these." -#endif - -#define WidePRId64 L"I64d" -#define WidePRIu64 L"I64u" -#define WidePRIx64 L"I64x" - -#if !defined(PRIuS) -#define PRIuS "Iu" -#endif - -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - -// GCC will concatenate wide and narrow strings correctly, so nothing needs to -// be done here. -#define WidePRId64 PRId64 -#define WidePRIu64 PRIu64 -#define WidePRIx64 PRIx64 - -#if !defined(PRIuS) -#define PRIuS "zu" -#endif - -#endif // defined(OS_WIN) - -// 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) - -#endif // BASE_FORMAT_MACROS_H_ diff --git a/fuchsia/OWNERS b/fuchsia/OWNERS deleted file mode 100644 index e7034eabb..000000000 --- a/fuchsia/OWNERS +++ /dev/null @@ -1 +0,0 @@ -file://build/fuchsia/OWNERS diff --git a/fuchsia/async_dispatcher.cc b/fuchsia/async_dispatcher.cc deleted file mode 100644 index b25b9f76c..000000000 --- a/fuchsia/async_dispatcher.cc +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright 2018 The Chromium 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/fuchsia/async_dispatcher.h" - -#include -#include -#include -#include -#include -#include - -#include "base/fuchsia/fuchsia_logging.h" - -namespace base { - -namespace { - -template -uintptr_t key_from_ptr(T* ptr) { - return reinterpret_cast(ptr); -}; - -} // namespace - -class AsyncDispatcher::WaitState : public LinkNode { - public: - explicit WaitState(AsyncDispatcher* async_dispatcher) { - async_dispatcher->wait_list_.Append(this); - } - ~WaitState() { RemoveFromList(); } - - async_wait_t* wait() { - // WaitState objects are allocated in-place in the |state| field of an - // enclosing async_wait_t, so async_wait_t address can be calculated by - // subtracting state offset in async_wait_t from |this|. - static_assert(std::is_standard_layout(), - "async_wait_t is expected to have standard layout."); - return reinterpret_cast(reinterpret_cast(this) - - offsetof(async_wait_t, state)); - } - - private: - DISALLOW_COPY_AND_ASSIGN(WaitState); -}; - -class AsyncDispatcher::TaskState : public LinkNode { - public: - explicit TaskState(LinkNode* previous_task) { - InsertAfter(previous_task); - } - ~TaskState() { RemoveFromList(); } - - async_task_t* task() { - // TaskState objects are allocated in-place in the |state| field of an - // enclosing async_task_t, so async_task_t address can be calculated by - // subtracting state offset in async_task_t from |this|. - static_assert(std::is_standard_layout(), - "async_task_t is expected to have standard layout."); - return reinterpret_cast(reinterpret_cast(this) - - offsetof(async_task_t, state)); - } - - private: - DISALLOW_COPY_AND_ASSIGN(TaskState); -}; - -AsyncDispatcher::AsyncDispatcher() : ops_storage_({}) { - zx_status_t status = zx::port::create(0u, &port_); - ZX_DCHECK(status == ZX_OK, status); - - status = zx::timer::create(0u, ZX_CLOCK_MONOTONIC, &timer_); - ZX_DCHECK(status == ZX_OK, status); - status = timer_.wait_async(port_, key_from_ptr(&timer_), ZX_TIMER_SIGNALED, - ZX_WAIT_ASYNC_REPEATING); - ZX_DCHECK(status == ZX_OK, status); - - status = zx::event::create(0, &stop_event_); - ZX_DCHECK(status == ZX_OK, status); - status = stop_event_.wait_async(port_, key_from_ptr(&stop_event_), - ZX_EVENT_SIGNALED, ZX_WAIT_ASYNC_REPEATING); - ZX_DCHECK(status == ZX_OK, status); - - ops_storage_.v1.now = NowOp; - ops_storage_.v1.begin_wait = BeginWaitOp; - ops_storage_.v1.cancel_wait = CancelWaitOp; - ops_storage_.v1.post_task = PostTaskOp; - ops_storage_.v1.cancel_task = CancelTaskOp; - ops_storage_.v1.queue_packet = QueuePacketOp; - ops_storage_.v1.set_guest_bell_trap = SetGuestBellTrapOp; - ops = &ops_storage_; - - DCHECK(!async_get_default()); - async_set_default(this); -} - -AsyncDispatcher::~AsyncDispatcher() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK_EQ(async_get_default(), this); - - // Some waits and tasks may be canceled while the dispatcher is being - // destroyed, so pop-from-head until none remain. - - while (!wait_list_.empty()) { - WaitState* state = wait_list_.head()->value(); - async_wait_t* wait = state->wait(); - state->~WaitState(); - wait->handler(this, wait, ZX_ERR_CANCELED, nullptr); - } - - while (!task_list_.empty()) { - TaskState* state = task_list_.head()->value(); - async_task_t* task = state->task(); - state->~TaskState(); - task->handler(this, task, ZX_ERR_CANCELED); - } - - async_set_default(nullptr); -} - -zx_status_t AsyncDispatcher::DispatchOrWaitUntil(zx_time_t deadline) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - zx_port_packet_t packet = {}; - zx_status_t status = port_.wait(zx::time(deadline), &packet); - if (status != ZX_OK) - return status; - - if (packet.type == ZX_PKT_TYPE_SIGNAL_ONE || - packet.type == ZX_PKT_TYPE_SIGNAL_REP) { - if (packet.key == key_from_ptr(&timer_)) { - // |timer_| has expired. - DCHECK(packet.signal.observed & ZX_TIMER_SIGNALED); - DispatchTasks(); - return ZX_OK; - } else if (packet.key == key_from_ptr(&stop_event_)) { - // Stop() was called. - DCHECK(packet.signal.observed & ZX_EVENT_SIGNALED); - status = zx_object_signal(stop_event_.get(), ZX_EVENT_SIGNALED, 0); - ZX_DCHECK(status == ZX_OK, status); - return ZX_ERR_CANCELED; - } else { - DCHECK_EQ(packet.type, ZX_PKT_TYPE_SIGNAL_ONE); - async_wait_t* wait = reinterpret_cast(packet.key); - - // Clean the state before invoking the handler: it may destroy the wait. - WaitState* state = reinterpret_cast(&wait->state); - state->~WaitState(); - - wait->handler(this, wait, packet.status, &packet.signal); - - return ZX_OK; - } - } - - NOTREACHED(); - return ZX_ERR_INTERNAL; -} - -void AsyncDispatcher::Stop() { - // Can be called on any thread. - zx_status_t status = - zx_object_signal(stop_event_.get(), 0, ZX_EVENT_SIGNALED); - ZX_DCHECK(status == ZX_OK, status); -} - -zx_time_t AsyncDispatcher::NowOp(async_t* async) { - DCHECK(async); - return zx_clock_get(ZX_CLOCK_MONOTONIC); -} - -zx_status_t AsyncDispatcher::BeginWaitOp(async_t* async, async_wait_t* wait) { - return static_cast(async)->BeginWait(wait); -} - -zx_status_t AsyncDispatcher::CancelWaitOp(async_t* async, async_wait_t* wait) { - return static_cast(async)->CancelWait(wait); -} - -zx_status_t AsyncDispatcher::PostTaskOp(async_t* async, async_task_t* task) { - return static_cast(async)->PostTask(task); -} - -zx_status_t AsyncDispatcher::CancelTaskOp(async_t* async, async_task_t* task) { - return static_cast(async)->CancelTask(task); -} - -zx_status_t AsyncDispatcher::QueuePacketOp(async_t* async, - async_receiver_t* receiver, - const zx_packet_user_t* data) { - return ZX_ERR_NOT_SUPPORTED; -} - -zx_status_t AsyncDispatcher::SetGuestBellTrapOp(async_t* async, - async_guest_bell_trap_t* trap, - zx_handle_t guest, - zx_vaddr_t addr, - size_t length) { - return ZX_ERR_NOT_SUPPORTED; -} - -zx_status_t AsyncDispatcher::BeginWait(async_wait_t* wait) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - static_assert(sizeof(AsyncDispatcher::WaitState) <= sizeof(async_state_t), - "WaitState is too big"); - WaitState* state = new (&wait->state) WaitState(this); - zx_status_t status = - zx::unowned_handle(wait->object) - ->wait_async(port_, reinterpret_cast(wait), wait->trigger, - ZX_WAIT_ASYNC_ONCE); - - if (status != ZX_OK) - state->~WaitState(); - - return status; -} - -zx_status_t AsyncDispatcher::CancelWait(async_wait_t* wait) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - zx_status_t status = - port_.cancel(wait->object, reinterpret_cast(wait)); - if (status == ZX_OK) { - WaitState* state = reinterpret_cast(&(wait->state)); - state->~WaitState(); - } - - return status; -} - -zx_status_t AsyncDispatcher::PostTask(async_task_t* task) { - // Can be called on any thread. - AutoLock lock(lock_); - - // Find correct position for the new task in |task_list_| to keep the list - // sorted by deadline. This implementation has O(N) complexity, but it's - // acceptable - async task are not expected to be used frequently. - // TODO(sergeyu): Consider using a more efficient data structure if tasks - // performance becomes important. - LinkNode* node; - for (node = task_list_.head(); node != task_list_.end(); - node = node->previous()) { - if (task->deadline >= node->value()->task()->deadline) - break; - } - - static_assert(sizeof(AsyncDispatcher::TaskState) <= sizeof(async_state_t), - "TaskState is too big"); - - // Will insert new task after |node|. - new (&task->state) TaskState(node); - - if (reinterpret_cast(&task->state) == task_list_.head()) { - // Task inserted at head. Earliest deadline changed. - RestartTimerLocked(); - } - - return ZX_OK; -} - -zx_status_t AsyncDispatcher::CancelTask(async_task_t* task) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - AutoLock lock(lock_); - - if (!task->state.reserved[0]) - return ZX_ERR_NOT_FOUND; - - TaskState* state = reinterpret_cast(&task->state); - state->~TaskState(); - - return ZX_OK; -} - -void AsyncDispatcher::DispatchTasks() { - // Snapshot now value to set implicit bound for the tasks that will run before - // DispatchTasks() returns. This also helps to avoid calling zx_clock_get() - // more than necessary. - zx_time_t now = zx_clock_get(ZX_CLOCK_MONOTONIC); - - while (true) { - async_task_t* task; - { - AutoLock lock(lock_); - if (task_list_.empty()) - break; - - TaskState* task_state = task_list_.head()->value(); - task = task_state->task(); - - if (task->deadline > now) { - RestartTimerLocked(); - break; - } - - task_state->~TaskState(); - - // ~TaskState() is expected to reset the state to 0. The destructor - // removes the task from the |task_list_| and LinkNode::RemoveFromList() - // sets both its fields to nullptr, which is equivalent to resetting the - // state to 0. - DCHECK_EQ(task->state.reserved[0], 0u); - } - - // The handler is responsible for freeing the |task| or it may reuse it. - task->handler(this, task, ZX_OK); - } -} - -void AsyncDispatcher::RestartTimerLocked() { - lock_.AssertAcquired(); - - if (task_list_.empty()) - return; - zx_time_t deadline = task_list_.head()->value()->task()->deadline; - zx_status_t status = timer_.set(zx::time(deadline), zx::duration()); - ZX_DCHECK(status == ZX_OK, status); -} - -} // namespace base diff --git a/fuchsia/async_dispatcher.h b/fuchsia/async_dispatcher.h deleted file mode 100644 index e464d3084..000000000 --- a/fuchsia/async_dispatcher.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018 The Chromium 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_FUCHSIA_ASYNC_DISPATCHER_H_ -#define BASE_FUCHSIA_ASYNC_DISPATCHER_H_ - -#include -#include -#include -#include - -#include "base/containers/linked_list.h" -#include "base/macros.h" -#include "base/synchronization/lock.h" -#include "base/threading/thread_checker.h" - -namespace base { - -// Implementation of dispatcher for Fuchsia's async library. It's necessary to -// run Fuchsia's library on chromium threads. -class BASE_EXPORT AsyncDispatcher : public async_t { - public: - AsyncDispatcher(); - ~AsyncDispatcher(); - - // Returns after running one or more tasks or waits until |deadline|. - // Returns |ZX_OK| if some tasks were executed, |ZX_ERR_TIMED_OUT| - the - // deadline expired, |ZX_ERR_CANCELED| - Stop() was called. - zx_status_t DispatchOrWaitUntil(zx_time_t deadline); - - // If Run() is being executed then it will return as soon as possible (e.g. - // finishing running the current task), otherwise the following Run() call - // will quit immediately instead of waiting until deadline expires. - void Stop(); - - private: - class WaitState; - class TaskState; - - static zx_time_t NowOp(async_t* async); - static zx_status_t BeginWaitOp(async_t* async, async_wait_t* wait); - static zx_status_t CancelWaitOp(async_t* async, async_wait_t* wait); - static zx_status_t PostTaskOp(async_t* async, async_task_t* task); - static zx_status_t CancelTaskOp(async_t* async, async_task_t* task); - static zx_status_t QueuePacketOp(async_t* async, - async_receiver_t* receiver, - const zx_packet_user_t* data); - static zx_status_t SetGuestBellTrapOp(async_t* async, - async_guest_bell_trap_t* trap, - zx_handle_t guest, - zx_vaddr_t addr, - size_t length); - - // async_ops_t implementation. Called by corresponding *Op() methods above. - zx_status_t BeginWait(async_wait_t* wait); - zx_status_t CancelWait(async_wait_t* wait); - zx_status_t PostTask(async_task_t* task); - zx_status_t CancelTask(async_task_t* task); - - // Runs tasks in |task_list_| that have deadline in the past. - void DispatchTasks(); - - // Must be called while |lock_| is held. - void RestartTimerLocked(); - - THREAD_CHECKER(thread_checker_); - - zx::port port_; - zx::timer timer_; - zx::event stop_event_; - - LinkedList wait_list_; - - async_ops_t ops_storage_; - - // |lock_| must be held when accessing |task_list_|. - base::Lock lock_; - - LinkedList task_list_; - - DISALLOW_COPY_AND_ASSIGN(AsyncDispatcher); -}; - -} // namespace base - -#endif // BASE_FUCHSIA_ASYNC_DISPATCHER_H_ diff --git a/fuchsia/async_dispatcher_unittest.cc b/fuchsia/async_dispatcher_unittest.cc deleted file mode 100644 index 351bbedbf..000000000 --- a/fuchsia/async_dispatcher_unittest.cc +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2018 The Chromium 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/fuchsia/async_dispatcher.h" - -#include -#include -#include -#include - -#include "base/callback.h" -#include "base/test/test_timeouts.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -struct TestTask : public async_task_t { - explicit TestTask() { - state = ASYNC_STATE_INIT; - handler = &TaskProc; - deadline = 0; - } - - static void TaskProc(async_t* async, async_task_t* task, zx_status_t status); - - int num_calls = 0; - int repeats = 1; - OnceClosure on_call; - zx_status_t last_status = ZX_OK; -}; - -// static -void TestTask::TaskProc(async_t* async, - async_task_t* task, - zx_status_t status) { - EXPECT_EQ(async, async_get_default()); - EXPECT_TRUE(status == ZX_OK || status == ZX_ERR_CANCELED) - << "status: " << status; - - auto* test_task = static_cast(task); - test_task->num_calls++; - test_task->last_status = status; - - if (!test_task->on_call.is_null()) - std::move(test_task->on_call).Run(); - - if (test_task->num_calls < test_task->repeats) - async_post_task(async, task); -}; - -struct TestWait : public async_wait_t { - TestWait(zx_handle_t handle, - zx_signals_t signals) { - state = ASYNC_STATE_INIT; - handler = &HandleProc; - object = handle; - trigger = signals; - } - - static void HandleProc(async_t* async, - async_wait_t* wait, - zx_status_t status, - const zx_packet_signal_t* signal); - int num_calls = 0; - OnceClosure on_call; - zx_status_t last_status = ZX_OK; -}; - -// static -void TestWait::HandleProc(async_t* async, - async_wait_t* wait, - zx_status_t status, - const zx_packet_signal_t* signal) { - EXPECT_EQ(async, async_get_default()); - EXPECT_TRUE(status == ZX_OK || status == ZX_ERR_CANCELED) - << "status: " << status; - - auto* test_wait = static_cast(wait); - - test_wait->num_calls++; - test_wait->last_status = status; - - if (!test_wait->on_call.is_null()) - std::move(test_wait->on_call).Run(); -} - -} // namespace - -class AsyncDispatcherTest : public testing::Test { - public: - AsyncDispatcherTest() { - dispatcher_ = std::make_unique(); - - async_ = async_get_default(); - EXPECT_TRUE(async_); - - EXPECT_EQ(zx::socket::create(ZX_SOCKET_DATAGRAM, &socket1_, &socket2_), - ZX_OK); - } - - ~AsyncDispatcherTest() override = default; - - void RunUntilIdle() { - while (true) { - zx_status_t status = dispatcher_->DispatchOrWaitUntil(0); - if (status != ZX_OK) { - EXPECT_EQ(status, ZX_ERR_TIMED_OUT); - break; - } - } - } - - protected: - std::unique_ptr dispatcher_; - - async_t* async_ = nullptr; - - zx::socket socket1_; - zx::socket socket2_; -}; - -TEST_F(AsyncDispatcherTest, PostTask) { - TestTask task; - ASSERT_EQ(async_post_task(async_, &task), ZX_OK); - dispatcher_->DispatchOrWaitUntil(0); - EXPECT_EQ(task.num_calls, 1); - EXPECT_EQ(task.last_status, ZX_OK); -} - -TEST_F(AsyncDispatcherTest, TaskRepeat) { - TestTask task; - task.repeats = 2; - ASSERT_EQ(async_post_task(async_, &task), ZX_OK); - RunUntilIdle(); - EXPECT_EQ(task.num_calls, 2); - EXPECT_EQ(task.last_status, ZX_OK); -} - -TEST_F(AsyncDispatcherTest, DelayedTask) { - TestTask task; - constexpr auto kDelay = TimeDelta::FromMilliseconds(5); - TimeTicks started = TimeTicks::Now(); - task.deadline = zx_deadline_after(kDelay.InNanoseconds()); - ASSERT_EQ(async_post_task(async_, &task), ZX_OK); - zx_status_t status = dispatcher_->DispatchOrWaitUntil(zx_deadline_after( - (kDelay + TestTimeouts::tiny_timeout()).InNanoseconds())); - EXPECT_EQ(status, ZX_OK); - EXPECT_GE(TimeTicks::Now() - started, kDelay); -} - -TEST_F(AsyncDispatcherTest, CancelTask) { - TestTask task; - ASSERT_EQ(async_post_task(async_, &task), ZX_OK); - ASSERT_EQ(async_cancel_task(async_, &task), ZX_OK); - RunUntilIdle(); - EXPECT_EQ(task.num_calls, 0); -} - -TEST_F(AsyncDispatcherTest, TaskObserveShutdown) { - TestTask task; - ASSERT_EQ(async_post_task(async_, &task), ZX_OK); - dispatcher_.reset(); - - EXPECT_EQ(task.num_calls, 1); - EXPECT_EQ(task.last_status, ZX_ERR_CANCELED); -} - -TEST_F(AsyncDispatcherTest, Wait) { - TestWait wait(socket1_.get(), ZX_SOCKET_READABLE); - EXPECT_EQ(async_begin_wait(async_, &wait), ZX_OK); - - // Handler shouldn't be called because the event wasn't signaled. - RunUntilIdle(); - EXPECT_EQ(wait.num_calls, 0); - - char byte = 0; - EXPECT_EQ(socket2_.write(/*options=*/0, &byte, sizeof(byte), - /*actual=*/nullptr), - ZX_OK); - - zx_status_t status = dispatcher_->DispatchOrWaitUntil( - zx_deadline_after(TestTimeouts::tiny_timeout().InNanoseconds())); - EXPECT_EQ(status, ZX_OK); - - EXPECT_EQ(wait.num_calls, 1); - EXPECT_EQ(wait.last_status, ZX_OK); -} - -TEST_F(AsyncDispatcherTest, CancelWait) { - TestWait wait(socket1_.get(), ZX_SOCKET_READABLE); - EXPECT_EQ(async_begin_wait(async_, &wait), ZX_OK); - - char byte = 0; - EXPECT_EQ(socket2_.write(/*options=*/0, &byte, sizeof(byte), - /*actual=*/nullptr), - ZX_OK); - - EXPECT_EQ(async_cancel_wait(async_, &wait), ZX_OK); - - RunUntilIdle(); - EXPECT_EQ(wait.num_calls, 0); -} - -TEST_F(AsyncDispatcherTest, WaitShutdown) { - TestWait wait(socket1_.get(), ZX_SOCKET_READABLE); - EXPECT_EQ(async_begin_wait(async_, &wait), ZX_OK); - RunUntilIdle(); - dispatcher_.reset(); - - EXPECT_EQ(wait.num_calls, 1); - EXPECT_EQ(wait.last_status, ZX_ERR_CANCELED); -} - -} // namespace base diff --git a/fuchsia/component_context.cc b/fuchsia/component_context.cc deleted file mode 100644 index a6d8f5bd6..000000000 --- a/fuchsia/component_context.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 The Chromium 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/fuchsia/component_context.h" - -#include -#include -#include - -#include "base/fuchsia/fuchsia_logging.h" -#include "base/no_destructor.h" - -namespace base { -namespace fuchsia { - -namespace { - -// static -zx::channel ConnectToServiceRoot() { - zx::channel client_channel; - zx::channel server_channel; - zx_status_t result = zx::channel::create(0, &client_channel, &server_channel); - ZX_CHECK(result == ZX_OK, result) << "zx_channel_create()"; - result = fdio_service_connect("/svc/.", server_channel.release()); - ZX_CHECK(result == ZX_OK, result) << "Failed to open /svc"; - return client_channel; -} - -} // namespace - -ComponentContext::ComponentContext(zx::channel service_root) - : service_root_(std::move(service_root)) { - DCHECK(service_root_); -} - -ComponentContext::~ComponentContext() = default; - -// static -ComponentContext* ComponentContext::GetDefault() { - static base::NoDestructor component_context( - ConnectToServiceRoot()); - return component_context.get(); -} - -zx_status_t ComponentContext::ConnectToService(FidlInterfaceRequest request) { - DCHECK(request.is_valid()); - return fdio_service_connect_at(service_root_.get(), request.interface_name(), - request.TakeChannel().release()); -} - -} // namespace fuchsia -} // namespace base diff --git a/fuchsia/component_context.h b/fuchsia/component_context.h deleted file mode 100644 index 0d395bd7e..000000000 --- a/fuchsia/component_context.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2018 The Chromium 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_FUCHSIA_COMPONENT_CONTEXT_H_ -#define BASE_FUCHSIA_COMPONENT_CONTEXT_H_ - -#include - -#include "base/base_export.h" -#include "base/fuchsia/fidl_interface_request.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" - -namespace fidl { - -template -class InterfacePtr; - -template -class Synchronous2InterfacePtr; - -} // namespace fidl - -namespace base { -namespace fuchsia { - -// Provides access to the component's environment. -class BASE_EXPORT ComponentContext { - public: - explicit ComponentContext(zx::channel service_root); - ~ComponentContext(); - - // Returns default ComponentContext instance for the current process. It uses - // /srv namespace to connect to environment services. - static ComponentContext* GetDefault(); - - // Satisfies the interface |request| by binding the channel to a service. - zx_status_t ConnectToService(FidlInterfaceRequest request); - - // Same as above, but returns interface pointer instead of taking a request. - template - fidl::InterfacePtr ConnectToService() { - fidl::InterfacePtr result; - ConnectToService(FidlInterfaceRequest(&result)); - return result; - } - - // Connects to an environment service and returns synchronous interface - // implementation. - template - fidl::Synchronous2InterfacePtr ConnectToServiceSync() { - fidl::Synchronous2InterfacePtr result; - ConnectToService(FidlInterfaceRequest(&result)); - return result; - } - - private: - zx::channel service_root_; - - DISALLOW_COPY_AND_ASSIGN(ComponentContext); -}; - -} // namespace fuchsia -} // namespace base - -#endif // BASE_FUCHSIA_COMPONENT_CONTEXT_H_ diff --git a/fuchsia/default_job.cc b/fuchsia/default_job.cc deleted file mode 100644 index c26aeb34a..000000000 --- a/fuchsia/default_job.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 The Chromium 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/fuchsia/default_job.h" - -#include - -#include "base/logging.h" - -namespace base { - -namespace { -zx_handle_t g_job = ZX_HANDLE_INVALID; -} - -zx::unowned_job GetDefaultJob() { - if (g_job == ZX_HANDLE_INVALID) - return zx::job::default_job(); - return zx::unowned_job(g_job); -} - -void SetDefaultJob(zx::job job) { - DCHECK_EQ(g_job, ZX_HANDLE_INVALID); - g_job = job.release(); -} - -} // namespace base diff --git a/fuchsia/default_job.h b/fuchsia/default_job.h deleted file mode 100644 index 9417f1c32..000000000 --- a/fuchsia/default_job.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 The Chromium 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_FUCHSIA_DEFAULT_JOB_H_ -#define BASE_FUCHSIA_DEFAULT_JOB_H_ - -#include - -#include "base/base_export.h" - -namespace base { - -// Gets and sets the job object used for creating new child processes, -// and looking them up by their process IDs. -// zx::job::default_job() will be returned if no job is explicitly set here. -// Only valid handles may be passed to SetDefaultJob(). -BASE_EXPORT zx::unowned_job GetDefaultJob(); -BASE_EXPORT void SetDefaultJob(zx::job job); - -} // namespace base - -#endif // BASE_FUCHSIA_DEFAULT_JOB_H_ diff --git a/fuchsia/fidl_interface_request.cc b/fuchsia/fidl_interface_request.cc deleted file mode 100644 index f0eaa1e55..000000000 --- a/fuchsia/fidl_interface_request.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 The Chromium 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/fuchsia/fidl_interface_request.h" - -namespace base { -namespace fuchsia { - -FidlInterfaceRequest::FidlInterfaceRequest(FidlInterfaceRequest&& moved) = - default; - -FidlInterfaceRequest::FidlInterfaceRequest(const char* interface_name, - zx::channel channel) - : interface_name_(interface_name), channel_(std::move(channel)) {} -FidlInterfaceRequest::~FidlInterfaceRequest() = default; - -// static -FidlInterfaceRequest FidlInterfaceRequest::CreateFromChannelUnsafe( - const char* interface_name, - zx::channel channel) { - return FidlInterfaceRequest(interface_name, std::move(channel)); -} - -zx::channel FidlInterfaceRequest::TakeChannel() { - DCHECK(channel_); - return std::move(channel_); -} - -} // namespace fuchsia -} // namespace base diff --git a/fuchsia/fidl_interface_request.h b/fuchsia/fidl_interface_request.h deleted file mode 100644 index 5ce4cdc9c..000000000 --- a/fuchsia/fidl_interface_request.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2018 The Chromium 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_FUCHSIA_FIDL_INTERFACE_REQUEST_H_ -#define BASE_FUCHSIA_FIDL_INTERFACE_REQUEST_H_ - -#include - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" - -namespace fidl { - -template -class InterfaceRequest; - -template -class InterfacePtr; - -template -class Synchronous2InterfacePtr; - -} // namespace fidl - -namespace base { -namespace fuchsia { - -// A request for a FIDL interface. FidlInterfaceRequest contains interface name -// and channel handle. Interface consumers create FidlInterfaceRequest when they -// need to connect to a service. FidlInterfaceRequest is resolved when the -// channel is passed to the service implementation, e.g. through -// ComponentContext. -class BASE_EXPORT FidlInterfaceRequest { - public: - template - explicit FidlInterfaceRequest(fidl::InterfaceRequest request) - : FidlInterfaceRequest(Interface::Name_, request.TakeChannel()) {} - - // Creates a new request for |Interface| and binds the client end to the - // |stub|. |stub| can be used immediately after the request is created, even - // before the request is passed to the service that implements the interface. - template - explicit FidlInterfaceRequest(fidl::InterfacePtr* stub) - : FidlInterfaceRequest(stub->NewRequest()) {} - - template - explicit FidlInterfaceRequest(fidl::Synchronous2InterfacePtr* stub) - : FidlInterfaceRequest(stub->NewRequest()) {} - - FidlInterfaceRequest(FidlInterfaceRequest&&); - ~FidlInterfaceRequest(); - - // Creates an interface request from the specified |channel|. Caller must - // ensure that the specified |interface_name| is valid for the specified - // |channel|. - static FidlInterfaceRequest CreateFromChannelUnsafe( - const char* interface_name, - zx::channel channel); - - bool is_valid() const { return interface_name_ && channel_; } - - const char* interface_name() const { return interface_name_; } - - // Extracts the channel handle to be passed to service implementation. The - // request becomes invalid after this call, i.e. TakeChannel() can be called - // only once. - zx::channel TakeChannel(); - - private: - FidlInterfaceRequest(const char* interface_name, zx::channel channel); - - const char* interface_name_; - zx::channel channel_; - - DISALLOW_COPY_AND_ASSIGN(FidlInterfaceRequest); -}; - -} // namespace fuchsia -} // namespace base - -#endif // BASE_FUCHSIA_FIDL_INTERFACE_REQUEST_H_ diff --git a/fuchsia/file_utils.cc b/fuchsia/file_utils.cc deleted file mode 100644 index 92bd854d8..000000000 --- a/fuchsia/file_utils.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 The Chromium 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/fuchsia/file_utils.h" - -#include -#include -#include -#include - -#include "base/files/file.h" -#include "base/fuchsia/fuchsia_logging.h" - -namespace base { -namespace fuchsia { - -zx::handle GetHandleFromFile(File file) { - // Unwrap the FD into |handles|. Negative result indicates failure. - zx_handle_t handles[FDIO_MAX_HANDLES] = {}; - uint32_t types[FDIO_MAX_HANDLES] = {}; - zx_status_t num_handles = - fdio_transfer_fd(file.GetPlatformFile(), 0, handles, types); - if (num_handles <= 0) { - DCHECK_LT(num_handles, 0); - ZX_DLOG(ERROR, num_handles) << "fdio_transfer_fd"; - return zx::handle(); - } - - // fdio_transfer_fd() has torn-down the file-descriptor, on success. - ignore_result(file.TakePlatformFile()); - - // Wrap the returned handles, so they will be closed on error. - zx::handle owned_handles[FDIO_MAX_HANDLES]; - for (int i = 0; i < FDIO_MAX_HANDLES; ++i) - owned_handles[i] = zx::handle(handles[i]); - - // We expect a single handle, of type PA_FDIO_REMOTE. - if (num_handles != 1 || types[0] != PA_FDIO_REMOTE) { - DLOG(ERROR) << "Specified file has " << num_handles - << " handles, and type: " << types[0]; - return zx::handle(); - } - - return std::move(owned_handles[0]); -} - -} // namespace fuchsia -} // namespace base diff --git a/fuchsia/file_utils.h b/fuchsia/file_utils.h deleted file mode 100644 index ee091acac..000000000 --- a/fuchsia/file_utils.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018 The Chromium 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_FUCHSIA_FILE_UTILS_H_ -#define BASE_FUCHSIA_FILE_UTILS_H_ - -#include - -#include "base/base_export.h" - -namespace base { - -class File; - -namespace fuchsia { - -// Gets a Zircon handle from a file or directory |path| in the process' -// namespace. -BASE_EXPORT zx::handle GetHandleFromFile(base::File file); - -} // namespace fuchsia -} // namespace base - -#endif // BASE_FUCHSIA_FILE_UTILS_H_ diff --git a/fuchsia/filtered_service_directory.cc b/fuchsia/filtered_service_directory.cc deleted file mode 100644 index af2317591..000000000 --- a/fuchsia/filtered_service_directory.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2018 The Chromium 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/fuchsia/filtered_service_directory.h" - -#include -#include - -#include "base/bind.h" -#include "base/fuchsia/component_context.h" -#include "base/fuchsia/fuchsia_logging.h" - -namespace base { -namespace fuchsia { - -FilteredServiceDirectory::FilteredServiceDirectory( - ComponentContext* component_context) - : component_context_(component_context) { - zx::channel server_channel; - zx_status_t status = - zx::channel::create(0, &server_channel, &directory_client_channel_); - ZX_CHECK(status == ZX_OK, status) << "zx_channel_create()"; - - service_directory_ = - std::make_unique(std::move(server_channel)); -} - -FilteredServiceDirectory::~FilteredServiceDirectory() { - service_directory_->RemoveAllServices(); -} - -void FilteredServiceDirectory::AddService(const char* service_name) { - service_directory_->AddService( - service_name, - base::BindRepeating(&FilteredServiceDirectory::HandleRequest, - base::Unretained(this), service_name)); -} - -zx::channel FilteredServiceDirectory::ConnectClient() { - zx::channel server_channel; - zx::channel client_channel; - zx_status_t status = zx::channel::create(0, &server_channel, &client_channel); - ZX_CHECK(status == ZX_OK, status) << "zx_channel_create()"; - - // ServiceDirectory puts public services under ./public . Connect to that - // directory and return client handle for the connection, - status = fdio_service_connect_at(directory_client_channel_.get(), "public", - server_channel.release()); - ZX_CHECK(status == ZX_OK, status) << "fdio_service_connect_at()"; - - return client_channel; -} - -void FilteredServiceDirectory::HandleRequest(const char* service_name, - zx::channel channel) { - component_context_->ConnectToService( - FidlInterfaceRequest::CreateFromChannelUnsafe(service_name, - std::move(channel))); -} - -} // namespace fuchsia -} // namespace base diff --git a/fuchsia/filtered_service_directory.h b/fuchsia/filtered_service_directory.h deleted file mode 100644 index 0c4bf25ee..000000000 --- a/fuchsia/filtered_service_directory.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2018 The Chromium 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_FUCHSIA_FILTERED_SERVICE_DIRECTORY_H_ -#define BASE_FUCHSIA_FILTERED_SERVICE_DIRECTORY_H_ - -#include "base/fuchsia/service_directory.h" - -#include - -#include "base/macros.h" - -namespace base { -namespace fuchsia { - -class ComponentContext; - -// ServiceDirectory that uses the supplied ComponentContext to satisfy requests -// for only a restricted set of services. -class BASE_EXPORT FilteredServiceDirectory { - public: - // Creates proxy that proxies requests to the specified |component_context|, - // which must outlive the proxy. - explicit FilteredServiceDirectory(ComponentContext* component_context); - ~FilteredServiceDirectory(); - - // Adds the specified service to the list of whitelisted services. - void AddService(const char* service_name); - - // Returns a client channel connected to the directory. The returned channel - // can be passed to a sandboxed process to be used for /svc namespace. - zx::channel ConnectClient(); - - private: - void HandleRequest(const char* service_name, zx::channel channel); - - ComponentContext* const component_context_; - std::unique_ptr service_directory_; - - // Client side of the channel used by |service_directory_|. - zx::channel directory_client_channel_; - - DISALLOW_COPY_AND_ASSIGN(FilteredServiceDirectory); -}; - -} // namespace fuchsia -} // namespace base - -#endif // BASE_FUCHSIA_FILTERED_SERVICE_DIRECTORY_H_ diff --git a/fuchsia/filtered_service_directory_unittest.cc b/fuchsia/filtered_service_directory_unittest.cc deleted file mode 100644 index 028281851..000000000 --- a/fuchsia/filtered_service_directory_unittest.cc +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2018 The Chromium 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/fuchsia/filtered_service_directory.h" - -#include -#include - -#include "base/fuchsia/service_directory_test_base.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace fuchsia { - -class FilteredServiceDirectoryTest : public ServiceDirectoryTestBase { - public: - FilteredServiceDirectoryTest() { - filtered_service_dir_ = - std::make_unique(client_context_.get()); - filtered_client_context_ = std::make_unique( - filtered_service_dir_->ConnectClient()); - } - - protected: - std::unique_ptr filtered_service_dir_; - std::unique_ptr filtered_client_context_; -}; - -// Verify that we can connect to a whitelisted service. -TEST_F(FilteredServiceDirectoryTest, Connect) { - filtered_service_dir_->AddService(test_fidl::TestInterface::Name_); - - auto stub = - filtered_client_context_->ConnectToService(); - VerifyTestInterface(&stub, false); -} - -// Verify that multiple connections to the same service work properly. -TEST_F(FilteredServiceDirectoryTest, ConnectMultiple) { - filtered_service_dir_->AddService(test_fidl::TestInterface::Name_); - - auto stub1 = - filtered_client_context_->ConnectToService(); - auto stub2 = - filtered_client_context_->ConnectToService(); - VerifyTestInterface(&stub1, false); - VerifyTestInterface(&stub2, false); -} - -// Verify that non-whitelisted services are blocked. -TEST_F(FilteredServiceDirectoryTest, ServiceBlocked) { - auto stub = - filtered_client_context_->ConnectToService(); - VerifyTestInterface(&stub, true); -} - -// Verify that FilteredServiceDirectory handles the case when the target service -// is not available in the underlying service directory. -TEST_F(FilteredServiceDirectoryTest, NoService) { - filtered_service_dir_->AddService(test_fidl::TestInterface::Name_); - - service_binding_.reset(); - - auto stub = - filtered_client_context_->ConnectToService(); - VerifyTestInterface(&stub, true); -} - -// Verify that FilteredServiceDirectory handles the case when the underlying -// service directory is destroyed. -TEST_F(FilteredServiceDirectoryTest, NoServiceDir) { - filtered_service_dir_->AddService(test_fidl::TestInterface::Name_); - - service_binding_.reset(); - service_directory_.reset(); - - auto stub = - filtered_client_context_->ConnectToService(); - VerifyTestInterface(&stub, true); -} - -} // namespace fuchsia -} // namespace base diff --git a/fuchsia/fuchsia_logging.cc b/fuchsia/fuchsia_logging.cc deleted file mode 100644 index 31a8dd56d..000000000 --- a/fuchsia/fuchsia_logging.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2017 The Chromium 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/fuchsia/fuchsia_logging.h" - -#include - -#include - -namespace logging { - -ZxLogMessage::ZxLogMessage(const char* file_path, - int line, - LogSeverity severity, - zx_status_t zx_err) - : LogMessage(file_path, line, severity), zx_err_(zx_err) {} - -ZxLogMessage::~ZxLogMessage() { - // zx_status_t error values are negative, so log the numeric version as - // decimal rather than hex. This is also useful to match zircon/errors.h for - // grepping. - stream() << ": " << zx_status_get_string(zx_err_) << " (" << zx_err_ << ")"; -} - -} // namespace logging diff --git a/fuchsia/fuchsia_logging.h b/fuchsia/fuchsia_logging.h deleted file mode 100644 index ba55f8db8..000000000 --- a/fuchsia/fuchsia_logging.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2017 The Chromium 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_FUCHSIA_FUCHSIA_LOGGING_H_ -#define BASE_FUCHSIA_FUCHSIA_LOGGING_H_ - -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/macros.h" -#include "build/build_config.h" - -// Use the ZX_LOG family of macros along with a zx_status_t containing a Zircon -// error. The error value will be decoded so that logged messages explain the -// error. - -namespace logging { - -class BASE_EXPORT ZxLogMessage : public logging::LogMessage { - public: - ZxLogMessage(const char* file_path, - int line, - LogSeverity severity, - zx_status_t zx_err); - ~ZxLogMessage(); - - private: - zx_status_t zx_err_; - - DISALLOW_COPY_AND_ASSIGN(ZxLogMessage); -}; - -} // namespace logging - -#define ZX_LOG_STREAM(severity, zx_err) \ - COMPACT_GOOGLE_LOG_EX_##severity(ZxLogMessage, zx_err).stream() - -#define ZX_LOG(severity, zx_err) \ - LAZY_STREAM(ZX_LOG_STREAM(severity, zx_err), LOG_IS_ON(severity)) -#define ZX_LOG_IF(severity, condition, zx_err) \ - LAZY_STREAM(ZX_LOG_STREAM(severity, zx_err), \ - LOG_IS_ON(severity) && (condition)) - -#define ZX_CHECK(condition, zx_err) \ - LAZY_STREAM(ZX_LOG_STREAM(FATAL, zx_err), !(condition)) \ - << "Check failed: " #condition << ". " - -#define ZX_DLOG(severity, zx_err) \ - LAZY_STREAM(ZX_LOG_STREAM(severity, zx_err), DLOG_IS_ON(severity)) - -#if DCHECK_IS_ON() -#define ZX_DLOG_IF(severity, condition, zx_err) \ - LAZY_STREAM(ZX_LOG_STREAM(severity, zx_err), \ - DLOG_IS_ON(severity) && (condition)) -#else // DCHECK_IS_ON() -#define ZX_DLOG_IF(severity, condition, zx_err) EAT_STREAM_PARAMETERS -#endif // DCHECK_IS_ON() - -#define ZX_DCHECK(condition, zx_err) \ - LAZY_STREAM(ZX_LOG_STREAM(DCHECK, zx_err), DCHECK_IS_ON() && !(condition)) \ - << "Check failed: " #condition << ". " - -#endif // BASE_FUCHSIA_FUCHSIA_LOGGING_H_ diff --git a/fuchsia/scoped_service_binding.h b/fuchsia/scoped_service_binding.h deleted file mode 100644 index 3acabde7a..000000000 --- a/fuchsia/scoped_service_binding.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2018 The Chromium 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_FUCHSIA_SCOPED_SERVICE_BINDING_H_ -#define BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_ - -#include - -#include "base/bind.h" -#include "base/fuchsia/service_directory.h" - -namespace base { -namespace fuchsia { - -template -class ScopedServiceBinding { - public: - // |service_directory| and |impl| must outlive the binding. - ScopedServiceBinding(ServiceDirectory* service_directory, Interface* impl) - : directory_(service_directory), impl_(impl) { - directory_->AddService( - Interface::Name_, - BindRepeating(&ScopedServiceBinding::BindClient, Unretained(this))); - } - - ~ScopedServiceBinding() { directory_->RemoveService(Interface::Name_); } - - void SetOnLastClientCallback(base::OnceClosure on_last_client_callback) { - on_last_client_callback_ = std::move(on_last_client_callback); - bindings_.set_empty_set_handler( - fit::bind_member(this, &ScopedServiceBinding::OnBindingSetEmpty)); - } - - private: - void BindClient(zx::channel channel) { - bindings_.AddBinding(impl_, - fidl::InterfaceRequest(std::move(channel))); - } - - void OnBindingSetEmpty() { - bindings_.set_empty_set_handler(nullptr); - std::move(on_last_client_callback_).Run(); - } - - ServiceDirectory* const directory_; - Interface* const impl_; - fidl::BindingSet bindings_; - base::OnceClosure on_last_client_callback_; - - DISALLOW_COPY_AND_ASSIGN(ScopedServiceBinding); -}; - -} // namespace fuchsia -} // namespace base - -#endif // BASE_FUCHSIA_SCOPED_SERVICE_BINDING_H_ diff --git a/fuchsia/scoped_zx_handle.h b/fuchsia/scoped_zx_handle.h deleted file mode 100644 index 33cf5124d..000000000 --- a/fuchsia/scoped_zx_handle.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017 The Chromium 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_FUCHSIA_SCOPED_ZX_HANDLE_H_ -#define BASE_FUCHSIA_SCOPED_ZX_HANDLE_H_ - -#include - -namespace base { - -// TODO(852541): Temporary shim to implement the old ScopedGeneric based -// container as a native zx::handle. Remove this once all callers have been -// migrated to use the libzx containers. -using ScopedZxHandle = zx::handle; - -} // namespace base - -#endif // BASE_FUCHSIA_SCOPED_ZX_HANDLE_H_ diff --git a/fuchsia/service_directory.cc b/fuchsia/service_directory.cc deleted file mode 100644 index 32eaaa51d..000000000 --- a/fuchsia/service_directory.cc +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2018 The Chromium 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/fuchsia/service_directory.h" - -#include -#include -#include -#include -#include - -#include "base/fuchsia/fuchsia_logging.h" -#include "base/message_loop/message_loop_current.h" -#include "base/no_destructor.h" - -namespace base { -namespace fuchsia { - -ServiceDirectory::ServiceDirectory(zx::channel directory_request) { - zx_status_t status = svc_dir_create(async_get_default(), - directory_request.release(), &svc_dir_); - ZX_CHECK(status == ZX_OK, status); -} - -ServiceDirectory::~ServiceDirectory() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(services_.empty()); - - zx_status_t status = svc_dir_destroy(svc_dir_); - ZX_DCHECK(status == ZX_OK, status); -} - -// static -ServiceDirectory* ServiceDirectory::GetDefault() { - static base::NoDestructor directory( - zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST))); - return directory.get(); -} - -void ServiceDirectory::AddService(StringPiece name, - ConnectServiceCallback connect_callback) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - DCHECK(services_.find(name) == services_.end()); - - std::string name_str = name.as_string(); - services_[name_str] = connect_callback; - - zx_status_t status = - svc_dir_add_service(svc_dir_, "public", name_str.c_str(), this, - &ServiceDirectory::HandleConnectRequest); - ZX_DCHECK(status == ZX_OK, status); - - // Publish to the legacy "flat" namespace, which is required by some clients. - status = svc_dir_add_service(svc_dir_, nullptr, name_str.c_str(), this, - &ServiceDirectory::HandleConnectRequest); - ZX_DCHECK(status == ZX_OK, status); -} - -void ServiceDirectory::RemoveService(StringPiece name) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - - std::string name_str = name.as_string(); - - auto it = services_.find(name_str); - DCHECK(it != services_.end()); - services_.erase(it); - - zx_status_t status = - svc_dir_remove_service(svc_dir_, "public", name_str.c_str()); - ZX_DCHECK(status == ZX_OK, status); - - // Unregister from the legacy "flat" namespace. - status = svc_dir_remove_service(svc_dir_, nullptr, name_str.c_str()); - ZX_DCHECK(status == ZX_OK, status); -} - -void ServiceDirectory::RemoveAllServices() { - while (!services_.empty()) { - RemoveService(services_.begin()->first); - } -} - -// static -void ServiceDirectory::HandleConnectRequest(void* context, - const char* service_name, - zx_handle_t service_request) { - auto* directory = reinterpret_cast(context); - DCHECK_CALLED_ON_VALID_THREAD(directory->thread_checker_); - - auto it = directory->services_.find(service_name); - - // HandleConnectRequest() is expected to be called only for registered - // services. - DCHECK(it != directory->services_.end()); - - it->second.Run(zx::channel(service_request)); -} - -} // namespace fuchsia -} // namespace base diff --git a/fuchsia/service_directory.h b/fuchsia/service_directory.h deleted file mode 100644 index f02c1d7e7..000000000 --- a/fuchsia/service_directory.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018 The Chromium 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_FUCHSIA_SERVICE_DIRECTORY_H_ -#define BASE_FUCHSIA_SERVICE_DIRECTORY_H_ - -#include - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/containers/flat_map.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" -#include "base/threading/thread_checker.h" - -typedef struct svc_dir svc_dir_t; - -namespace base { -namespace fuchsia { - -// Directory of FIDL services published for other processes to consume. Services -// published in this directory can be discovered from other processes by name. -// Normally this class should be used by creating a ScopedServiceBinding -// instance. This ensures that the service is unregistered when the -// implementation is destroyed. GetDefault() should be used to get the default -// ServiceDirectory for the current process. The default instance exports -// services via a channel supplied at process creation time. -// -// Not thread-safe. All methods must be called on the thread that created the -// object. -class BASE_EXPORT ServiceDirectory { - public: - // Callback called to connect incoming requests. - using ConnectServiceCallback = - base::RepeatingCallback; - - // Creates services directory that will be served over the - // |directory_channel|. - explicit ServiceDirectory(zx::channel directory_channel); - - ~ServiceDirectory(); - - // Returns default ServiceDirectory instance for the current process. It - // publishes services to the directory provided by the process creator. - static ServiceDirectory* GetDefault(); - - void AddService(StringPiece name, ConnectServiceCallback connect_callback); - void RemoveService(StringPiece name); - void RemoveAllServices(); - - private: - // Called by |svc_dir_| to handle service requests. - static void HandleConnectRequest(void* context, - const char* service_name, - zx_handle_t service_request); - - THREAD_CHECKER(thread_checker_); - - svc_dir_t* svc_dir_ = nullptr; - base::flat_map services_; - - DISALLOW_COPY_AND_ASSIGN(ServiceDirectory); -}; - -} // namespace fuchsia -} // namespace base - -#endif // BASE_FUCHSIA_SERVICE_DIRECTORY_H_ diff --git a/fuchsia/service_directory_test_base.cc b/fuchsia/service_directory_test_base.cc deleted file mode 100644 index c5a09f3b5..000000000 --- a/fuchsia/service_directory_test_base.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2018 The Chromium 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/fuchsia/service_directory_test_base.h" - -#include - -namespace base { -namespace fuchsia { - -TestInterfaceImpl::TestInterfaceImpl() = default; -TestInterfaceImpl::~TestInterfaceImpl() = default; - -// TestInterface implementation. -void TestInterfaceImpl::Add(int32_t a, int32_t b, AddCallback callback) { - callback(a + b); -} - -ServiceDirectoryTestBase::ServiceDirectoryTestBase() { - zx::channel service_directory_channel; - EXPECT_EQ(zx::channel::create(0, &service_directory_channel, - &service_directory_client_channel_), - ZX_OK); - - // Mount service dir and publish the service. - service_directory_ = - std::make_unique(std::move(service_directory_channel)); - service_binding_ = - std::make_unique>( - service_directory_.get(), &test_service_); - - ConnectClientContextToDirectory("public"); -} - -ServiceDirectoryTestBase::~ServiceDirectoryTestBase() = default; - -void ServiceDirectoryTestBase::ConnectClientContextToDirectory( - const char* path) { - // Open directory |path| from the service directory. - zx::channel public_directory_channel; - zx::channel public_directory_client_channel; - EXPECT_EQ(zx::channel::create(0, &public_directory_channel, - &public_directory_client_channel), - ZX_OK); - EXPECT_EQ(fdio_open_at(service_directory_client_channel_.get(), path, 0, - public_directory_channel.release()), - ZX_OK); - - // Create ComponentContext and connect to the test service. - client_context_ = std::make_unique( - std::move(public_directory_client_channel)); -} - -void ServiceDirectoryTestBase::VerifyTestInterface( - fidl::InterfacePtr* stub, - bool expect_error) { - // Call the service and wait for response. - base::RunLoop run_loop; - bool error = false; - - stub->set_error_handler([&run_loop, &error]() { - error = true; - run_loop.Quit(); - }); - - (*stub)->Add(2, 2, [&run_loop](int32_t result) { - EXPECT_EQ(result, 4); - run_loop.Quit(); - }); - - run_loop.Run(); - - EXPECT_EQ(error, expect_error); - - // Reset error handler because the current one captures |run_loop| and - // |error| references which are about to be destroyed. - stub->set_error_handler([]() {}); -} - -} // namespace fuchsia -} // namespace base diff --git a/fuchsia/service_directory_test_base.h b/fuchsia/service_directory_test_base.h deleted file mode 100644 index 88348a63a..000000000 --- a/fuchsia/service_directory_test_base.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2018 The Chromium 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_FUCHSIA_SERVICE_DIRECTORY_TEST_BASE_H_ -#define BASE_FUCHSIA_SERVICE_DIRECTORY_TEST_BASE_H_ - -#include - -#include "base/fuchsia/component_context.h" -#include "base/fuchsia/scoped_service_binding.h" -#include "base/fuchsia/test_fidl/cpp/fidl.h" -#include "base/message_loop/message_loop.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace fuchsia { - -class TestInterfaceImpl : public test_fidl::TestInterface { - public: - TestInterfaceImpl(); - ~TestInterfaceImpl() override; - - // TestInterface implementation. - void Add(int32_t a, int32_t b, AddCallback callback) override; -}; - -class ServiceDirectoryTestBase : public testing::Test { - public: - ServiceDirectoryTestBase(); - ~ServiceDirectoryTestBase() override; - - void ConnectClientContextToDirectory(const char* path); - void VerifyTestInterface(fidl::InterfacePtr* stub, - bool expect_error); - - protected: - MessageLoopForIO message_loop_; - std::unique_ptr service_directory_; - zx::channel service_directory_client_channel_; - TestInterfaceImpl test_service_; - std::unique_ptr> - service_binding_; - std::unique_ptr client_context_; -}; - -} // namespace fuchsia -} // namespace base - -#endif // BASE_FUCHSIA_SERVICE_DIRECTORY_TEST_BASE_H_ \ No newline at end of file diff --git a/fuchsia/service_directory_unittest.cc b/fuchsia/service_directory_unittest.cc deleted file mode 100644 index 0af6f1fef..000000000 --- a/fuchsia/service_directory_unittest.cc +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2018 The Chromium 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/fuchsia/service_directory.h" - -#include -#include -#include - -#include "base/bind.h" -#include "base/fuchsia/service_directory_test_base.h" -#include "base/location.h" -#include "base/run_loop.h" -#include "base/task_runner.h" -#include "base/test/test_timeouts.h" -#include "base/threading/thread_task_runner_handle.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace fuchsia { - -class ServiceDirectoryTest : public ServiceDirectoryTestBase {}; - -// Verifies that ComponentContext can consume a public service in -// ServiceDirectory and that connection is disconnected when the client stub is -// destroyed. -TEST_F(ServiceDirectoryTest, ConnectDisconnect) { - auto stub = client_context_->ConnectToService(); - VerifyTestInterface(&stub, false); - - base::RunLoop run_loop; - service_binding_->SetOnLastClientCallback(run_loop.QuitClosure()); - - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, - base::BindOnce( - [](base::RunLoop* run_loop) { - ADD_FAILURE(); - run_loop->Quit(); - }, - &run_loop), - TestTimeouts::action_timeout()); - - stub.Unbind(); - run_loop.Run(); -} - -// Verifies that we can connect to the service service more than once. -TEST_F(ServiceDirectoryTest, ConnectMulti) { - auto stub = client_context_->ConnectToService(); - auto stub2 = client_context_->ConnectToService(); - VerifyTestInterface(&stub, false); - VerifyTestInterface(&stub2, false); -} - -// Verify that services are also exported to the legacy flat service namespace. -TEST_F(ServiceDirectoryTest, ConnectLegacy) { - ConnectClientContextToDirectory("."); - auto stub = client_context_->ConnectToService(); - VerifyTestInterface(&stub, false); -} - -// Verify that ComponentContext can handle the case when the service directory -// connection is disconnected. -TEST_F(ServiceDirectoryTest, DirectoryGone) { - service_binding_.reset(); - service_directory_.reset(); - - fidl::InterfacePtr stub; - zx_status_t status = - client_context_->ConnectToService(FidlInterfaceRequest(&stub)); - EXPECT_EQ(status, ZX_ERR_PEER_CLOSED); - - VerifyTestInterface(&stub, true); -} - -// Verify that the case when the service doesn't exist is handled properly. -TEST_F(ServiceDirectoryTest, NoService) { - service_binding_.reset(); - auto stub = client_context_->ConnectToService(); - VerifyTestInterface(&stub, true); -} - -} // namespace fuchsia -} // namespace base diff --git a/fuchsia/test.fidl b/fuchsia/test.fidl deleted file mode 100644 index 1ecc89d42..000000000 --- a/fuchsia/test.fidl +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -library base.fuchsia.test_fidl; - -[Discoverable] -interface TestInterface { - 1: Add(int32 a, int32 b) -> (int32 sum); -}; diff --git a/gmock_unittest.cc b/gmock_unittest.cc deleted file mode 100644 index 5c16728e3..000000000 --- a/gmock_unittest.cc +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// This test is a simple sanity check to make sure gmock is able to build/link -// correctly. It just instantiates a mock object and runs through a couple of -// the basic mock features. - -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -// Gmock matchers and actions that we use below. -using testing::AnyOf; -using testing::Eq; -using testing::Return; -using testing::SetArgPointee; -using testing::WithArg; -using testing::_; - -namespace { - -// Simple class that we can mock out the behavior for. Everything is virtual -// for easy mocking. -class SampleClass { - public: - SampleClass() = default; - virtual ~SampleClass() = default; - - virtual int ReturnSomething() { - return -1; - } - - virtual void ReturnNothingConstly() const { - } - - virtual void OutputParam(int* a) { - } - - virtual int ReturnSecond(int a, int b) { - return b; - } -}; - -// Declare a mock for the class. -class MockSampleClass : public SampleClass { - public: - MOCK_METHOD0(ReturnSomething, int()); - MOCK_CONST_METHOD0(ReturnNothingConstly, void()); - MOCK_METHOD1(OutputParam, void(int* a)); - MOCK_METHOD2(ReturnSecond, int(int a, int b)); -}; - -// Create a couple of custom actions. Custom actions can be used for adding -// more complex behavior into your mock...though if you start needing these, ask -// if you're asking your mock to do too much. -ACTION(ReturnVal) { - // Return the first argument received. - return arg0; -} -ACTION(ReturnSecond) { - // Returns the second argument. This basically implemetns ReturnSecond. - return arg1; -} - -TEST(GmockTest, SimpleMatchAndActions) { - // Basic test of some simple gmock matchers, actions, and cardinality - // expectations. - MockSampleClass mock; - - EXPECT_CALL(mock, ReturnSomething()) - .WillOnce(Return(1)) - .WillOnce(Return(2)) - .WillOnce(Return(3)); - EXPECT_EQ(1, mock.ReturnSomething()); - EXPECT_EQ(2, mock.ReturnSomething()); - EXPECT_EQ(3, mock.ReturnSomething()); - - EXPECT_CALL(mock, ReturnNothingConstly()).Times(2); - mock.ReturnNothingConstly(); - mock.ReturnNothingConstly(); -} - -TEST(GmockTest, AssignArgument) { - // Capture an argument for examination. - MockSampleClass mock; - - EXPECT_CALL(mock, OutputParam(_)).WillRepeatedly(SetArgPointee<0>(5)); - - int arg = 0; - mock.OutputParam(&arg); - EXPECT_EQ(5, arg); -} - -TEST(GmockTest, SideEffects) { - // Capture an argument for examination. - MockSampleClass mock; - - EXPECT_CALL(mock, OutputParam(_)).WillRepeatedly(SetArgPointee<0>(5)); - - int arg = 0; - mock.OutputParam(&arg); - EXPECT_EQ(5, arg); -} - -TEST(GmockTest, CustomAction_ReturnSecond) { - // Test a mock of the ReturnSecond behavior using an action that provides an - // alternate implementation of the function. Danger here though, this is - // starting to add too much behavior of the mock, which means the mock - // implementation might start to have bugs itself. - MockSampleClass mock; - - EXPECT_CALL(mock, ReturnSecond(_, AnyOf(Eq(4), Eq(5)))) - .WillRepeatedly(ReturnSecond()); - EXPECT_EQ(4, mock.ReturnSecond(-1, 4)); - EXPECT_EQ(5, mock.ReturnSecond(0, 5)); - EXPECT_EQ(4, mock.ReturnSecond(0xdeadbeef, 4)); - EXPECT_EQ(4, mock.ReturnSecond(112358, 4)); - EXPECT_EQ(5, mock.ReturnSecond(1337, 5)); -} - -TEST(GmockTest, CustomAction_ReturnVal) { - // Alternate implemention of ReturnSecond using a more general custom action, - // and a WithArg adapter to bridge the interfaces. - MockSampleClass mock; - - EXPECT_CALL(mock, ReturnSecond(_, AnyOf(Eq(4), Eq(5)))) - .WillRepeatedly(WithArg<1>(ReturnVal())); - EXPECT_EQ(4, mock.ReturnSecond(-1, 4)); - EXPECT_EQ(5, mock.ReturnSecond(0, 5)); - EXPECT_EQ(4, mock.ReturnSecond(0xdeadbeef, 4)); - EXPECT_EQ(4, mock.ReturnSecond(112358, 4)); - EXPECT_EQ(5, mock.ReturnSecond(1337, 5)); -} - -} // namespace diff --git a/gtest_prod_util.h b/gtest_prod_util.h deleted file mode 100644 index 2ca267e27..000000000 --- a/gtest_prod_util.h +++ /dev/null @@ -1,66 +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_GTEST_PROD_UTIL_H_ -#define BASE_GTEST_PROD_UTIL_H_ - -#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck - -// This is a wrapper for gtest's FRIEND_TEST macro that friends -// test with all possible prefixes. This is very helpful when changing the test -// prefix, because the friend declarations don't need to be updated. -// -// Example usage: -// -// class MyClass { -// private: -// void MyMethod(); -// FRIEND_TEST_ALL_PREFIXES(MyClassTest, MyMethod); -// }; -#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name) \ - FRIEND_TEST(test_case_name, test_name); \ - FRIEND_TEST(test_case_name, DISABLED_##test_name); \ - FRIEND_TEST(test_case_name, FLAKY_##test_name) - -// C++ compilers will refuse to compile the following code: -// -// namespace foo { -// class MyClass { -// private: -// FRIEND_TEST_ALL_PREFIXES(MyClassTest, TestMethod); -// bool private_var; -// }; -// } // namespace foo -// -// class MyClassTest::TestMethod() { -// foo::MyClass foo_class; -// foo_class.private_var = true; -// } -// -// Unless you forward declare MyClassTest::TestMethod outside of namespace foo. -// Use FORWARD_DECLARE_TEST to do so for all possible prefixes. -// -// Example usage: -// -// FORWARD_DECLARE_TEST(MyClassTest, TestMethod); -// -// namespace foo { -// class MyClass { -// private: -// FRIEND_TEST_ALL_PREFIXES(::MyClassTest, TestMethod); // NOTE use of :: -// bool private_var; -// }; -// } // namespace foo -// -// class MyClassTest::TestMethod() { -// foo::MyClass foo_class; -// foo_class.private_var = true; -// } - -#define FORWARD_DECLARE_TEST(test_case_name, test_name) \ - class test_case_name##_##test_name##_Test; \ - class test_case_name##_##DISABLED_##test_name##_Test; \ - class test_case_name##_##FLAKY_##test_name##_Test - -#endif // BASE_GTEST_PROD_UTIL_H_ diff --git a/guid.cc b/guid.cc deleted file mode 100644 index 2a2365837..000000000 --- a/guid.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/guid.h" - -#include -#include - -#include "base/rand_util.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" - -namespace base { - -namespace { - -bool IsLowerHexDigit(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); -} - -bool IsValidGUIDInternal(const base::StringPiece& guid, bool strict) { - const size_t kGUIDLength = 36U; - if (guid.length() != kGUIDLength) - return false; - - for (size_t i = 0; i < guid.length(); ++i) { - char current = guid[i]; - if (i == 8 || i == 13 || i == 18 || i == 23) { - if (current != '-') - return false; - } else { - if ((strict && !IsLowerHexDigit(current)) || !IsHexDigit(current)) - return false; - } - } - - return true; -} - -} // namespace - -std::string GenerateGUID() { - uint64_t sixteen_bytes[2]; - // Use base::RandBytes instead of crypto::RandBytes, because crypto calls the - // base version directly, and to prevent the dependency from base/ to crypto/. - base::RandBytes(&sixteen_bytes, sizeof(sixteen_bytes)); - - // Set the GUID to version 4 as described in RFC 4122, section 4.4. - // The format of GUID version 4 must be xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, - // where y is one of [8, 9, A, B]. - - // Clear the version bits and set the version to 4: - sixteen_bytes[0] &= 0xffffffff'ffff0fffULL; - sixteen_bytes[0] |= 0x00000000'00004000ULL; - - // Set the two most significant bits (bits 6 and 7) of the - // clock_seq_hi_and_reserved to zero and one, respectively: - sixteen_bytes[1] &= 0x3fffffff'ffffffffULL; - sixteen_bytes[1] |= 0x80000000'00000000ULL; - - return RandomDataToGUIDString(sixteen_bytes); -} - -bool IsValidGUID(const base::StringPiece& guid) { - return IsValidGUIDInternal(guid, false /* strict */); -} - -bool IsValidGUIDOutputString(const base::StringPiece& guid) { - return IsValidGUIDInternal(guid, true /* strict */); -} - -std::string RandomDataToGUIDString(const uint64_t bytes[2]) { - return StringPrintf("%08x-%04x-%04x-%04x-%012llx", - static_cast(bytes[0] >> 32), - static_cast((bytes[0] >> 16) & 0x0000ffff), - static_cast(bytes[0] & 0x0000ffff), - static_cast(bytes[1] >> 48), - bytes[1] & 0x0000ffff'ffffffffULL); -} - -} // namespace base diff --git a/guid.h b/guid.h deleted file mode 100644 index c6937a1e5..000000000 --- a/guid.h +++ /dev/null @@ -1,45 +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_GUID_H_ -#define BASE_GUID_H_ - -#include - -#include - -#include "base/base_export.h" -#include "base/strings/string_piece.h" -#include "build/build_config.h" - -namespace base { - -// Generate a 128-bit random GUID in the form of version 4 as described in -// RFC 4122, section 4.4. -// The format of GUID version 4 must be xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, -// where y is one of [8, 9, A, B]. -// The hexadecimal values "a" through "f" are output as lower case characters. -// -// A cryptographically secure random source will be used, but consider using -// UnguessableToken for greater type-safety if GUID format is unnecessary. -BASE_EXPORT std::string GenerateGUID(); - -// Returns true if the input string conforms to the version 4 GUID format. -// Note that this does NOT check if the hexadecimal values "a" through "f" -// are in lower case characters, as Version 4 RFC says onput they're -// case insensitive. (Use IsValidGUIDOutputString for checking if the -// given string is valid output string) -BASE_EXPORT bool IsValidGUID(const base::StringPiece& guid); - -// Returns true if the input string is valid version 4 GUID output string. -// This also checks if the hexadecimal values "a" through "f" are in lower -// case characters. -BASE_EXPORT bool IsValidGUIDOutputString(const base::StringPiece& guid); - -// For unit testing purposes only. Do not use outside of tests. -BASE_EXPORT std::string RandomDataToGUIDString(const uint64_t bytes[2]); - -} // namespace base - -#endif // BASE_GUID_H_ diff --git a/guid_unittest.cc b/guid_unittest.cc deleted file mode 100644 index 70dad674d..000000000 --- a/guid_unittest.cc +++ /dev/null @@ -1,65 +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/guid.h" - -#include - -#include - -#include "base/strings/string_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -bool IsGUIDv4(const std::string& guid) { - // The format of GUID version 4 must be xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx, - // where y is one of [8, 9, A, B]. - return IsValidGUID(guid) && guid[14] == '4' && - (guid[19] == '8' || guid[19] == '9' || guid[19] == 'A' || - guid[19] == 'a' || guid[19] == 'B' || guid[19] == 'b'); -} - -} // namespace - -TEST(GUIDTest, GUIDGeneratesAllZeroes) { - uint64_t bytes[] = {0, 0}; - std::string clientid = RandomDataToGUIDString(bytes); - EXPECT_EQ("00000000-0000-0000-0000-000000000000", clientid); -} - -TEST(GUIDTest, GUIDGeneratesCorrectly) { - uint64_t bytes[] = {0x0123456789ABCDEFULL, 0xFEDCBA9876543210ULL}; - std::string clientid = RandomDataToGUIDString(bytes); - EXPECT_EQ("01234567-89ab-cdef-fedc-ba9876543210", clientid); -} - -TEST(GUIDTest, GUIDCorrectlyFormatted) { - const int kIterations = 10; - for (int it = 0; it < kIterations; ++it) { - std::string guid = GenerateGUID(); - EXPECT_TRUE(IsValidGUID(guid)); - EXPECT_TRUE(IsValidGUIDOutputString(guid)); - EXPECT_TRUE(IsValidGUID(ToLowerASCII(guid))); - EXPECT_TRUE(IsValidGUID(ToUpperASCII(guid))); - } -} - -TEST(GUIDTest, GUIDBasicUniqueness) { - const int kIterations = 10; - for (int it = 0; it < kIterations; ++it) { - std::string guid1 = GenerateGUID(); - std::string guid2 = GenerateGUID(); - EXPECT_EQ(36U, guid1.length()); - EXPECT_EQ(36U, guid2.length()); - EXPECT_NE(guid1, guid2); - EXPECT_TRUE(IsGUIDv4(guid1)); - EXPECT_TRUE(IsGUIDv4(guid2)); - } -} - -} // namespace base diff --git a/hash.cc b/hash.cc deleted file mode 100644 index ab5cebc98..000000000 --- a/hash.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/hash.h" - -// 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 { - -uint32_t Hash(const void* data, size_t length) { - // Currently our in-memory hash is the same as the persistent hash. The - // split between in-memory and persistent hash functions is maintained to - // allow the in-memory hash function to be updated in the future. - return PersistentHash(data, length); -} - -uint32_t Hash(const std::string& str) { - return PersistentHash(str.data(), str.size()); -} - -uint32_t Hash(const string16& str) { - return PersistentHash(str.data(), str.size() * sizeof(char16)); -} - -uint32_t PersistentHash(const void* data, size_t length) { - // This hash function must not change, since it is designed to be persistable - // to disk. - if (length > static_cast(std::numeric_limits::max())) { - NOTREACHED(); - return 0; - } - return ::SuperFastHash(reinterpret_cast(data), - static_cast(length)); -} - -uint32_t PersistentHash(const std::string& str) { - return PersistentHash(str.data(), str.size()); -} - -// Implement hashing for pairs of at-most 32 bit integer values. -// When size_t is 32 bits, we turn the 64-bit hash code into 32 bits by using -// multiply-add hashing. This algorithm, as described in -// Theorem 4.3.3 of the thesis "Über die Komplexität der Multiplikation in -// eingeschränkten Branchingprogrammmodellen" by Woelfel, is: -// -// h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32 -// -// Contact danakj@chromium.org for any questions. -size_t HashInts32(uint32_t value1, uint32_t value2) { - uint64_t value1_64 = value1; - uint64_t hash64 = (value1_64 << 32) | value2; - - if (sizeof(size_t) >= sizeof(uint64_t)) - return static_cast(hash64); - - uint64_t odd_random = 481046412LL << 32 | 1025306955LL; - uint32_t shift_random = 10121U << 16; - - hash64 = hash64 * odd_random + shift_random; - size_t high_bits = - static_cast(hash64 >> (8 * (sizeof(uint64_t) - sizeof(size_t)))); - return high_bits; -} - -// Implement hashing for pairs of up-to 64-bit integer values. -// We use the compound integer hash method to produce a 64-bit hash code, by -// breaking the two 64-bit inputs into 4 32-bit values: -// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000 -// Then we reduce our result to 32 bits if required, similar to above. -size_t HashInts64(uint64_t value1, uint64_t value2) { - uint32_t short_random1 = 842304669U; - uint32_t short_random2 = 619063811U; - uint32_t short_random3 = 937041849U; - uint32_t short_random4 = 3309708029U; - - uint32_t value1a = static_cast(value1 & 0xffffffff); - uint32_t value1b = static_cast((value1 >> 32) & 0xffffffff); - uint32_t value2a = static_cast(value2 & 0xffffffff); - uint32_t value2b = static_cast((value2 >> 32) & 0xffffffff); - - uint64_t product1 = static_cast(value1a) * short_random1; - uint64_t product2 = static_cast(value1b) * short_random2; - uint64_t product3 = static_cast(value2a) * short_random3; - uint64_t product4 = static_cast(value2b) * short_random4; - - uint64_t hash64 = product1 + product2 + product3 + product4; - - if (sizeof(size_t) >= sizeof(uint64_t)) - return static_cast(hash64); - - uint64_t odd_random = 1578233944LL << 32 | 194370989LL; - uint32_t shift_random = 20591U << 16; - - hash64 = hash64 * odd_random + shift_random; - size_t high_bits = - static_cast(hash64 >> (8 * (sizeof(uint64_t) - sizeof(size_t)))); - return high_bits; -} - -} // namespace base diff --git a/hash.h b/hash.h deleted file mode 100644 index 165899ee5..000000000 --- a/hash.h +++ /dev/null @@ -1,69 +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_HASH_H_ -#define BASE_HASH_H_ - -#include -#include - -#include -#include -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/strings/string16.h" - -namespace base { - -// Computes a hash of a memory buffer. This hash function is subject to change -// in the future, so use only for temporary in-memory structures. If you need -// to persist a change on disk or between computers, use PersistentHash(). -// -// WARNING: This hash function should not be used for any cryptographic purpose. -BASE_EXPORT uint32_t Hash(const void* data, size_t length); -BASE_EXPORT uint32_t Hash(const std::string& str); -BASE_EXPORT uint32_t Hash(const string16& str); - -// Computes a hash of a memory buffer. This hash function must not change so -// that code can use the hashed values for persistent storage purposes or -// sending across the network. If a new persistent hash function is desired, a -// new version will have to be added in addition. -// -// WARNING: This hash function should not be used for any cryptographic purpose. -BASE_EXPORT uint32_t PersistentHash(const void* data, size_t length); -BASE_EXPORT uint32_t PersistentHash(const std::string& str); - -// Hash pairs of 32-bit or 64-bit numbers. -BASE_EXPORT size_t HashInts32(uint32_t value1, uint32_t value2); -BASE_EXPORT size_t HashInts64(uint64_t value1, uint64_t value2); - -template -inline size_t HashInts(T1 value1, T2 value2) { - // This condition is expected to be compile-time evaluated and optimised away - // in release builds. - if (sizeof(T1) > sizeof(uint32_t) || (sizeof(T2) > sizeof(uint32_t))) - return HashInts64(value1, value2); - - return HashInts32(value1, value2); -} - -// A templated hasher for pairs of integer types. Example: -// -// using MyPair = std::pair; -// std::unordered_set> set; -template -struct IntPairHash; - -template -struct IntPairHash> { - size_t operator()(std::pair value) const { - return HashInts(value.first, value.second); - } -}; - -} // namespace base - -#endif // BASE_HASH_H_ diff --git a/hash_unittest.cc b/hash_unittest.cc deleted file mode 100644 index fc8a7519e..000000000 --- a/hash_unittest.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/hash.h" - -#include -#include - -#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 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/i18n/OWNERS b/i18n/OWNERS deleted file mode 100644 index d717b8dab..000000000 --- a/i18n/OWNERS +++ /dev/null @@ -1 +0,0 @@ -jshin@chromium.org diff --git a/i18n/base_i18n_export.h b/i18n/base_i18n_export.h deleted file mode 100644 index e8a2adda1..000000000 --- a/i18n/base_i18n_export.h +++ /dev/null @@ -1,29 +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_I18N_BASE_I18N_EXPORT_H_ -#define BASE_I18N_BASE_I18N_EXPORT_H_ - -#if defined(COMPONENT_BUILD) -#if defined(WIN32) - -#if defined(BASE_I18N_IMPLEMENTATION) -#define BASE_I18N_EXPORT __declspec(dllexport) -#else -#define BASE_I18N_EXPORT __declspec(dllimport) -#endif // defined(BASE_I18N_IMPLEMENTATION) - -#else // defined(WIN32) -#if defined(BASE_I18N_IMPLEMENTATION) -#define BASE_I18N_EXPORT __attribute__((visibility("default"))) -#else -#define BASE_I18N_EXPORT -#endif -#endif - -#else // defined(COMPONENT_BUILD) -#define BASE_I18N_EXPORT -#endif - -#endif // BASE_I18N_BASE_I18N_EXPORT_H_ diff --git a/i18n/base_i18n_switches.cc b/i18n/base_i18n_switches.cc deleted file mode 100644 index 103d6653d..000000000 --- a/i18n/base_i18n_switches.cc +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/i18n/base_i18n_switches.h" - -namespace switches { - -// Force the UI to a specific direction. Valid values are "ltr" (left-to-right) -// and "rtl" (right-to-left). -const char kForceUIDirection[] = "force-ui-direction"; - -// Force the text rendering to a specific direction. Valid values are "ltr" -// (left-to-right) and "rtl" (right-to-left). Only tested meaningfully with -// RTL. -const char kForceTextDirection[] = "force-text-direction"; - -const char kForceDirectionLTR[] = "ltr"; -const char kForceDirectionRTL[] = "rtl"; - -} // namespace switches diff --git a/i18n/base_i18n_switches.h b/i18n/base_i18n_switches.h deleted file mode 100644 index d1ba69064..000000000 --- a/i18n/base_i18n_switches.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_I18N_BASE_I18N_SWITCHES_H_ -#define BASE_I18N_BASE_I18N_SWITCHES_H_ - -#include "base/i18n/base_i18n_export.h" - -namespace switches { - -BASE_I18N_EXPORT extern const char kForceUIDirection[]; -BASE_I18N_EXPORT extern const char kForceTextDirection[]; - -// kForce*Direction choices for the switches above. -BASE_I18N_EXPORT extern const char kForceDirectionLTR[]; -BASE_I18N_EXPORT extern const char kForceDirectionRTL[]; - -} // namespace switches - -#endif // BASE_I18N_BASE_I18N_SWITCHES_H_ diff --git a/i18n/bidi_line_iterator.cc b/i18n/bidi_line_iterator.cc deleted file mode 100644 index 3f7f86866..000000000 --- a/i18n/bidi_line_iterator.cc +++ /dev/null @@ -1,119 +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/i18n/bidi_line_iterator.h" - -#include "base/logging.h" - -namespace base { -namespace i18n { - -namespace { - -UBiDiLevel GetParagraphLevelForDirection(TextDirection direction) { - switch (direction) { - case UNKNOWN_DIRECTION: - return UBIDI_DEFAULT_LTR; - break; - case RIGHT_TO_LEFT: - return 1; // Highest RTL level. - break; - case LEFT_TO_RIGHT: - return 0; // Highest LTR level. - break; - default: - NOTREACHED(); - return 0; - } -} - -// Overrides the default bidi class for a given character, for the custom -// "AS_URL" behavior. Returns U_BIDI_CLASS_DEFAULT to defer to the default ICU -// behavior. -// -// Matches the C callback interface of ICU's UBiDiClassCallback type (which is -// why there is an unused argument). -UCharDirection GetURLBiDiClassCallback(const void* /*unused*/, UChar32 c) { - // Note: Use a switch statement instead of strchr() to avoid iterating over a - // string for each character (the switch allows for much better compiler - // optimization). - switch (c) { - // The set of characters that delimit URL components (separating the scheme, - // username, password, domain labels, host, path segments, query - // names/values and fragment). - case '#': - case '&': - case '.': - case '/': - case ':': - case '=': - case '?': - case '@': - // Treat all of these characters as strong LTR, which effectively - // surrounds all of the text components of a URL (e.g., the domain labels - // and path segments) in a left-to-right embedding. This ensures that the - // URL components read from left to right, regardless of any RTL - // characters. (Within each component, RTL sequences are rendered from - // right to left as expected.) - return U_LEFT_TO_RIGHT; - default: - return U_BIDI_CLASS_DEFAULT; - } -} - -} // namespace - -BiDiLineIterator::BiDiLineIterator() : bidi_(nullptr) {} - -BiDiLineIterator::~BiDiLineIterator() { - if (bidi_) { - ubidi_close(bidi_); - bidi_ = nullptr; - } -} - -bool BiDiLineIterator::Open(const string16& text, - TextDirection direction, - CustomBehavior behavior) { - DCHECK(!bidi_); - UErrorCode error = U_ZERO_ERROR; - bidi_ = ubidi_openSized(static_cast(text.length()), 0, &error); - if (U_FAILURE(error)) - return false; - - if (behavior == CustomBehavior::AS_URL) { - ubidi_setClassCallback(bidi_, GetURLBiDiClassCallback, nullptr, nullptr, - nullptr, &error); - if (U_FAILURE(error)) - return false; - } - - ubidi_setPara(bidi_, text.data(), static_cast(text.length()), - GetParagraphLevelForDirection(direction), nullptr, &error); - return (U_SUCCESS(error)); -} - -int BiDiLineIterator::CountRuns() const { - DCHECK(bidi_ != nullptr); - UErrorCode error = U_ZERO_ERROR; - const int runs = ubidi_countRuns(bidi_, &error); - return U_SUCCESS(error) ? runs : 0; -} - -UBiDiDirection BiDiLineIterator::GetVisualRun(int index, - int* start, - int* length) const { - DCHECK(bidi_ != nullptr); - return ubidi_getVisualRun(bidi_, index, start, length); -} - -void BiDiLineIterator::GetLogicalRun(int start, - int* end, - UBiDiLevel* level) const { - DCHECK(bidi_ != nullptr); - ubidi_getLogicalRun(bidi_, start, end, level); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/bidi_line_iterator.h b/i18n/bidi_line_iterator.h deleted file mode 100644 index d840f6197..000000000 --- a/i18n/bidi_line_iterator.h +++ /dev/null @@ -1,60 +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_I18N_BIDI_LINE_ITERATOR_H_ -#define BASE_I18N_BIDI_LINE_ITERATOR_H_ - -#include "base/i18n/base_i18n_export.h" -#include "base/i18n/rtl.h" -#include "base/macros.h" -#include "base/strings/string16.h" -#include "third_party/icu/source/common/unicode/ubidi.h" -#include "third_party/icu/source/common/unicode/uchar.h" - -namespace base { -namespace i18n { - -// A simple wrapper class for the bidirectional iterator of ICU. -// This class uses the bidirectional iterator of ICU to split a line of -// bidirectional texts into visual runs in its display order. -class BASE_I18N_EXPORT BiDiLineIterator { - public: - // Specifies some alternative iteration behavior. - enum class CustomBehavior { - // No special behavior. - NONE, - // Treat URL delimiter characters as strong LTR. This is a special treatment - // for URLs that purposefully violates the URL Standard, as an experiment. - // It should only be used behind a flag. - AS_URL - }; - - BiDiLineIterator(); - ~BiDiLineIterator(); - - // Initializes the bidirectional iterator with the specified text. Returns - // whether initialization succeeded. - bool Open(const string16& text, - TextDirection direction, - CustomBehavior behavior); - - // Returns the number of visual runs in the text, or zero on error. - int CountRuns() const; - - // Gets the logical offset, length, and direction of the specified visual run. - UBiDiDirection GetVisualRun(int index, int* start, int* length) const; - - // Given a start position, figure out where the run ends (and the BiDiLevel). - void GetLogicalRun(int start, int* end, UBiDiLevel* level) const; - - private: - UBiDi* bidi_; - - DISALLOW_COPY_AND_ASSIGN(BiDiLineIterator); -}; - -} // namespace i18n -} // namespace base - -#endif // BASE_I18N_BIDI_LINE_ITERATOR_H_ diff --git a/i18n/bidi_line_iterator_unittest.cc b/i18n/bidi_line_iterator_unittest.cc deleted file mode 100644 index d531313bf..000000000 --- a/i18n/bidi_line_iterator_unittest.cc +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2017 The Chromium 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/bidi_line_iterator.h" - -#include "base/macros.h" -#include "base/strings/utf_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace i18n { -namespace { - -class BiDiLineIteratorTest : public testing::TestWithParam { - public: - BiDiLineIteratorTest() = default; - - BiDiLineIterator* iterator() { return &iterator_; } - - private: - BiDiLineIterator iterator_; - - DISALLOW_COPY_AND_ASSIGN(BiDiLineIteratorTest); -}; - -TEST_P(BiDiLineIteratorTest, OnlyLTR) { - iterator()->Open(UTF8ToUTF16("abc 😁 测试"), GetParam(), - BiDiLineIterator::CustomBehavior::NONE); - ASSERT_EQ(1, iterator()->CountRuns()); - - int start, length; - EXPECT_EQ(UBIDI_LTR, iterator()->GetVisualRun(0, &start, &length)); - EXPECT_EQ(0, start); - EXPECT_EQ(9, length); - - int end; - UBiDiLevel level; - iterator()->GetLogicalRun(0, &end, &level); - EXPECT_EQ(9, end); - if (GetParam() == TextDirection::RIGHT_TO_LEFT) - EXPECT_EQ(2, level); - else - EXPECT_EQ(0, level); -} - -TEST_P(BiDiLineIteratorTest, OnlyRTL) { - iterator()->Open(UTF8ToUTF16("מה השעה"), GetParam(), - BiDiLineIterator::CustomBehavior::NONE); - ASSERT_EQ(1, iterator()->CountRuns()); - - int start, length; - EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length)); - EXPECT_EQ(0, start); - EXPECT_EQ(7, length); - - int end; - UBiDiLevel level; - iterator()->GetLogicalRun(0, &end, &level); - EXPECT_EQ(7, end); - EXPECT_EQ(1, level); -} - -TEST_P(BiDiLineIteratorTest, Mixed) { - iterator()->Open(UTF8ToUTF16("אני משתמש ב- Chrome כדפדפן האינטרנט שלי"), - GetParam(), BiDiLineIterator::CustomBehavior::NONE); - ASSERT_EQ(3, iterator()->CountRuns()); - - // We'll get completely different results depending on the top-level paragraph - // direction. - if (GetParam() == TextDirection::RIGHT_TO_LEFT) { - // If para direction is RTL, expect the LTR substring "Chrome" to be nested - // within the surrounding RTL text. - int start, length; - EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length)); - EXPECT_EQ(19, start); - EXPECT_EQ(20, length); - EXPECT_EQ(UBIDI_LTR, iterator()->GetVisualRun(1, &start, &length)); - EXPECT_EQ(13, start); - EXPECT_EQ(6, length); - EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(2, &start, &length)); - EXPECT_EQ(0, start); - EXPECT_EQ(13, length); - - int end; - UBiDiLevel level; - iterator()->GetLogicalRun(0, &end, &level); - EXPECT_EQ(13, end); - EXPECT_EQ(1, level); - iterator()->GetLogicalRun(13, &end, &level); - EXPECT_EQ(19, end); - EXPECT_EQ(2, level); - iterator()->GetLogicalRun(19, &end, &level); - EXPECT_EQ(39, end); - EXPECT_EQ(1, level); - } else { - // If the para direction is LTR, expect the LTR substring "- Chrome " to be - // at the top level, with two nested RTL runs on either side. - int start, length; - EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length)); - EXPECT_EQ(0, start); - EXPECT_EQ(11, length); - EXPECT_EQ(UBIDI_LTR, iterator()->GetVisualRun(1, &start, &length)); - EXPECT_EQ(11, start); - EXPECT_EQ(9, length); - EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(2, &start, &length)); - EXPECT_EQ(20, start); - EXPECT_EQ(19, length); - - int end; - UBiDiLevel level; - iterator()->GetLogicalRun(0, &end, &level); - EXPECT_EQ(11, end); - EXPECT_EQ(1, level); - iterator()->GetLogicalRun(11, &end, &level); - EXPECT_EQ(20, end); - EXPECT_EQ(0, level); - iterator()->GetLogicalRun(20, &end, &level); - EXPECT_EQ(39, end); - EXPECT_EQ(1, level); - } -} - -TEST_P(BiDiLineIteratorTest, RTLPunctuationNoCustomBehavior) { - // This string features Hebrew characters interleaved with ASCII punctuation. - iterator()->Open(UTF8ToUTF16("א!ב\"ג#ד$ה%ו&ז'ח(ט)י*ך+כ,ל-ם.מ/" - "ן:נ;ס<ע=ף>פ?ץ@צ[ק\\ר]ש^ת_א`ב{ג|ד}ה~ו"), - GetParam(), BiDiLineIterator::CustomBehavior::NONE); - - // Expect a single RTL run. - ASSERT_EQ(1, iterator()->CountRuns()); - - int start, length; - EXPECT_EQ(UBIDI_RTL, iterator()->GetVisualRun(0, &start, &length)); - EXPECT_EQ(0, start); - EXPECT_EQ(65, length); - - int end; - UBiDiLevel level; - iterator()->GetLogicalRun(0, &end, &level); - EXPECT_EQ(65, end); - EXPECT_EQ(1, level); -} - -TEST_P(BiDiLineIteratorTest, RTLPunctuationAsURL) { - // This string features Hebrew characters interleaved with ASCII punctuation. - iterator()->Open(UTF8ToUTF16("א!ב\"ג#ד$ה%ו&ז'ח(ט)י*ך+כ,ל-ם.מ/" - "ן:נ;ס<ע=ף>פ?ץ@צ[ק\\ר]ש^ת_א`ב{ג|ד}ה~ו"), - GetParam(), BiDiLineIterator::CustomBehavior::AS_URL); - - const int kStringSize = 65; - - // Expect a primary RTL run, broken up by each of the 8 punctuation marks that - // are considered strong LTR (17 runs total). - struct { - int start; - UBiDiDirection dir; - } expected_runs[] = { - {0, UBIDI_RTL}, {5, UBIDI_LTR}, // '#' - {6, UBIDI_RTL}, {11, UBIDI_LTR}, // '&' - {12, UBIDI_RTL}, {27, UBIDI_LTR}, // '.' - {28, UBIDI_RTL}, {29, UBIDI_LTR}, // '/' - {30, UBIDI_RTL}, {31, UBIDI_LTR}, // ':' - {32, UBIDI_RTL}, {37, UBIDI_LTR}, // '=' - {38, UBIDI_RTL}, {41, UBIDI_LTR}, // '?' - {42, UBIDI_RTL}, {43, UBIDI_LTR}, // '@' - {44, UBIDI_RTL}, - }; - - ASSERT_EQ(arraysize(expected_runs), - static_cast(iterator()->CountRuns())); - - for (size_t i = 0; i < arraysize(expected_runs); ++i) { - const auto& expected_run = expected_runs[i]; - int expected_run_end = i >= arraysize(expected_runs) - 1 - ? kStringSize - : expected_runs[i + 1].start; - - size_t visual_index = GetParam() == TextDirection::RIGHT_TO_LEFT - ? arraysize(expected_runs) - 1 - i - : i; - int start, length; - EXPECT_EQ(expected_run.dir, - iterator()->GetVisualRun(visual_index, &start, &length)) - << "(i = " << i << ")"; - EXPECT_EQ(expected_run.start, start) << "(i = " << i << ")"; - EXPECT_EQ(expected_run_end - expected_run.start, length) - << "(i = " << i << ")"; - - int expected_level = - expected_run.dir == UBIDI_RTL - ? 1 - : (GetParam() == TextDirection::RIGHT_TO_LEFT ? 2 : 0); - int end; - UBiDiLevel level; - iterator()->GetLogicalRun(expected_run.start, &end, &level); - EXPECT_EQ(expected_run_end, end) << "(i = " << i << ")"; - EXPECT_EQ(expected_level, level) << "(i = " << i << ")"; - } -} - -INSTANTIATE_TEST_CASE_P(, - BiDiLineIteratorTest, - ::testing::Values(TextDirection::LEFT_TO_RIGHT, - TextDirection::RIGHT_TO_LEFT)); - -} // namespace -} // namespace i18n -} // namespace base diff --git a/i18n/break_iterator.cc b/i18n/break_iterator.cc deleted file mode 100644 index 251cd002e..000000000 --- a/i18n/break_iterator.cc +++ /dev/null @@ -1,191 +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/i18n/break_iterator.h" - -#include - -#include "base/logging.h" -#include "third_party/icu/source/common/unicode/ubrk.h" -#include "third_party/icu/source/common/unicode/uchar.h" -#include "third_party/icu/source/common/unicode/ustring.h" - -namespace base { -namespace i18n { - -const size_t npos = static_cast(-1); - -BreakIterator::BreakIterator(const StringPiece16& str, BreakType break_type) - : iter_(nullptr), - string_(str), - break_type_(break_type), - prev_(npos), - pos_(0) {} - -BreakIterator::BreakIterator(const StringPiece16& str, const string16& rules) - : iter_(nullptr), - string_(str), - rules_(rules), - break_type_(RULE_BASED), - prev_(npos), - pos_(0) {} - -BreakIterator::~BreakIterator() { - if (iter_) - ubrk_close(static_cast(iter_)); -} - -bool BreakIterator::Init() { - UErrorCode status = U_ZERO_ERROR; - UParseError parse_error; - UBreakIteratorType break_type; - switch (break_type_) { - case BREAK_CHARACTER: - break_type = UBRK_CHARACTER; - break; - case BREAK_WORD: - break_type = UBRK_WORD; - 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; - } - if (break_type_ == RULE_BASED) { - iter_ = ubrk_openRules(rules_.c_str(), - static_cast(rules_.length()), - string_.data(), - static_cast(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, nullptr, string_.data(), - static_cast(string_.size()), &status); - if (U_FAILURE(status)) { - NOTREACHED() << "ubrk_open failed for type " << break_type - << " with error " << status; - } - } - - if (U_FAILURE(status)) { - return false; - } - - // Move the iterator to the beginning of the string. - ubrk_first(static_cast(iter_)); - return true; -} - -bool BreakIterator::Advance() { - int32_t pos; - int32_t status; - prev_ = pos_; - switch (break_type_) { - case BREAK_CHARACTER: - case BREAK_WORD: - case BREAK_LINE: - case RULE_BASED: - pos = ubrk_next(static_cast(iter_)); - if (pos == UBRK_DONE) { - pos_ = npos; - return false; - } - pos_ = static_cast(pos); - return true; - case BREAK_NEWLINE: - do { - pos = ubrk_next(static_cast(iter_)); - if (pos == UBRK_DONE) - break; - pos_ = static_cast(pos); - status = ubrk_getRuleStatus(static_cast(iter_)); - } while (status >= UBRK_LINE_SOFT && status < UBRK_LINE_SOFT_LIMIT); - if (pos == UBRK_DONE && prev_ == pos_) { - pos_ = npos; - return false; - } - return true; - default: - NOTREACHED() << "invalid break_type_"; - return false; - } -} - -bool BreakIterator::SetText(const base::char16* text, const size_t length) { - UErrorCode status = U_ZERO_ERROR; - ubrk_setText(static_cast(iter_), - text, length, &status); - pos_ = 0; // implicit when ubrk_setText is done - prev_ = npos; - if (U_FAILURE(status)) { - NOTREACHED() << "ubrk_setText failed"; - return false; - } - string_ = StringPiece16(text, length); - return true; -} - -bool BreakIterator::IsWord() const { - return GetWordBreakStatus() == IS_WORD_BREAK; -} - -BreakIterator::WordBreakStatus BreakIterator::GetWordBreakStatus() const { - int32_t status = ubrk_getRuleStatus(static_cast(iter_)); - if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED) - return IS_LINE_OR_CHAR_BREAK; - // In ICU 60, trying to advance past the end of the text does not change - // |status| so that |pos_| has to be checked as well as |status|. - // See http://bugs.icu-project.org/trac/ticket/13447 . - return (status == UBRK_WORD_NONE || pos_ == npos) ? IS_SKIPPABLE_WORD - : IS_WORD_BREAK; -} - -bool BreakIterator::IsEndOfWord(size_t position) const { - if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED) - return false; - - UBreakIterator* iter = static_cast(iter_); - UBool boundary = ubrk_isBoundary(iter, static_cast(position)); - int32_t status = ubrk_getRuleStatus(iter); - return (!!boundary && status != UBRK_WORD_NONE); -} - -bool BreakIterator::IsStartOfWord(size_t position) const { - if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED) - return false; - - UBreakIterator* iter = static_cast(iter_); - UBool boundary = ubrk_isBoundary(iter, static_cast(position)); - ubrk_next(iter); - int32_t next_status = ubrk_getRuleStatus(iter); - return (!!boundary && next_status != UBRK_WORD_NONE); -} - -bool BreakIterator::IsGraphemeBoundary(size_t position) const { - if (break_type_ != BREAK_CHARACTER) - return false; - - UBreakIterator* iter = static_cast(iter_); - return !!ubrk_isBoundary(iter, static_cast(position)); -} - -string16 BreakIterator::GetString() const { - return GetStringPiece().as_string(); -} - -StringPiece16 BreakIterator::GetStringPiece() const { - DCHECK(prev_ != npos && pos_ != npos); - return string_.substr(prev_, pos_ - prev_); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/break_iterator.h b/i18n/break_iterator.h deleted file mode 100644 index dc30b644f..000000000 --- a/i18n/break_iterator.h +++ /dev/null @@ -1,182 +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_I18N_BREAK_ITERATOR_H_ -#define BASE_I18N_BREAK_ITERATOR_H_ - -#include - -#include "base/i18n/base_i18n_export.h" -#include "base/macros.h" -#include "base/strings/string16.h" -#include "base/strings/string_piece.h" - -// The BreakIterator class iterates through the words, word breaks, and -// line breaks in a UTF-16 string. -// -// It provides several modes, BREAK_WORD, BREAK_LINE, and BREAK_NEWLINE, -// which modify how characters are aggregated into the returned string. -// -// Under BREAK_WORD mode, once a word is encountered any non-word -// characters are not included in the returned string (e.g. in the -// UTF-16 equivalent of the string " foo bar! ", the word breaks are at -// the periods in ". .foo. .bar.!. ."). -// Note that Chinese/Japanese/Thai do not use spaces between words so that -// boundaries can fall in the middle of a continuous run of non-space / -// non-punctuation characters. -// -// Under BREAK_LINE mode, once a line breaking opportunity is encountered, -// any non-word characters are included in the returned string, breaking -// only when a space-equivalent character or a line breaking opportunity -// is encountered (e.g. in the UTF16-equivalent of the string " foo bar! ", -// the breaks are at the periods in ". .foo .bar! ."). -// -// Note that lines can be broken at any character/syllable/grapheme cluster -// boundary in Chinese/Japanese/Korean and at word boundaries in Thai -// (Thai does not use spaces between words). Therefore, this is NOT the same -// as breaking only at space-equivalent characters where its former -// name (BREAK_SPACE) implied. -// -// Under BREAK_NEWLINE mode, all characters are included in the returned -// string, breaking only when a newline-equivalent character is encountered -// (eg. in the UTF-16 equivalent of the string "foo\nbar!\n\n", the line -// breaks are at the periods in ".foo\n.bar\n.\n."). -// -// To extract the words from a string, move a BREAK_WORD BreakIterator -// through the string and test whether IsWord() is true. E.g., -// BreakIterator iter(str, BreakIterator::BREAK_WORD); -// if (!iter.Init()) -// return false; -// while (iter.Advance()) { -// if (iter.IsWord()) { -// // Region [iter.prev(), iter.pos()) contains a word. -// VLOG(1) << "word: " << iter.GetString(); -// } -// } - -namespace base { -namespace i18n { - -class BASE_I18N_EXPORT BreakIterator { - public: - enum BreakType { - BREAK_WORD, - BREAK_LINE, - // TODO(jshin): Remove this after reviewing call sites. - // If call sites really need break only on space-like characters - // implement it separately. - BREAK_SPACE = BREAK_LINE, - BREAK_NEWLINE, - BREAK_CHARACTER, - // But don't remove this one! - RULE_BASED, - }; - - enum WordBreakStatus { - // The end of text that the iterator recognizes as word characters. - // Non-word characters are things like punctuation and spaces. - IS_WORD_BREAK, - // Characters that the iterator can skip past, such as punctuation, - // whitespace, and, if using RULE_BASED mode, characters from another - // character set. - IS_SKIPPABLE_WORD, - // Only used if not in BREAK_WORD or RULE_BASED mode. This is returned for - // newlines, line breaks, and character breaks. - IS_LINE_OR_CHAR_BREAK - }; - - // Requires |str| to live as long as the BreakIterator does. - BreakIterator(const StringPiece16& 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 StringPiece16& str, const string16& rules); - ~BreakIterator(); - - // Init() must be called before any of the iterators are valid. - // Returns false if ICU failed to initialize. - bool Init(); - - // Advance to the next break. Returns false if we've run past the end of - // the string. (Note that the very last "break" is after the final - // character in the string, and when we advance to that position it's the - // 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, - // this distinction doesn't apply and it always returns false. - bool IsWord() const; - - // Under BREAK_WORD mode: - // - Returns IS_SKIPPABLE_WORD if non-word characters, such as punctuation or - // spaces, are found. - // - Returns IS_WORD_BREAK if the break we just hit is the end of a sequence - // of word characters. - // Under RULE_BASED mode: - // - Returns IS_SKIPPABLE_WORD if characters outside the rules' character set - // or non-word characters, such as punctuation or spaces, are found. - // - Returns IS_WORD_BREAK if the break we just hit is the end of a sequence - // of word characters that are in the rules' character set. - // Not under BREAK_WORD or RULE_BASED mode: - // - Returns IS_LINE_OR_CHAR_BREAK. - BreakIterator::WordBreakStatus GetWordBreakStatus() const; - - // Under BREAK_WORD mode, returns true if |position| is at the end of word or - // at the start of word. It always returns false under BREAK_LINE and - // BREAK_NEWLINE modes. - bool IsEndOfWord(size_t position) const; - bool IsStartOfWord(size_t position) const; - - // Under BREAK_CHARACTER mode, returns whether |position| is a Unicode - // grapheme boundary. - bool IsGraphemeBoundary(size_t position) const; - - // Returns the string between prev() and pos(). - // Advance() must have been called successfully at least once for pos() to - // have advanced to somewhere useful. - string16 GetString() const; - - StringPiece16 GetStringPiece() const; - - // Returns the value of pos() returned before Advance() was last called. - size_t prev() const { return prev_; } - - // Returns the current break position within the string, - // or BreakIterator::npos when done. - size_t pos() const { return pos_; } - - private: - // ICU iterator, avoiding ICU ubrk.h dependence. - // This is actually an ICU UBreakiterator* type, which turns out to be - // a typedef for a void* in the ICU headers. Using void* directly prevents - // callers from needing access to the ICU public headers directory. - void* iter_; - - // The string we're iterating over. Can be changed with SetText(...) - StringPiece16 string_; - - // 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. - size_t prev_, pos_; - - DISALLOW_COPY_AND_ASSIGN(BreakIterator); -}; - -} // namespace i18n -} // namespace base - -#endif // BASE_I18N_BREAK_ITERATOR_H_ diff --git a/i18n/break_iterator_unittest.cc b/i18n/break_iterator_unittest.cc deleted file mode 100644 index ed5de448a..000000000 --- a/i18n/break_iterator_unittest.cc +++ /dev/null @@ -1,584 +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/i18n/break_iterator.h" - -#include - -#include "base/macros.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 "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace i18n { - -TEST(BreakIteratorTest, BreakWordEmpty) { - string16 empty; - BreakIterator iter(empty, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakWord) { - string16 space(UTF8ToUTF16(" ")); - string16 str(UTF8ToUTF16(" foo bar! \npouet boom")); - BreakIterator iter(str, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(space, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("foo"), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(space, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("bar"), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("!"), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(space, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("\n"), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("pouet"), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(space, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("boom"), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakWordWide16) { - // Two greek words separated by space. - const string16 str(WideToUTF16( - L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9" - L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2")); - const string16 word1(str.substr(0, 10)); - const string16 word2(str.substr(11, 5)); - BreakIterator iter(str, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(word1, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(word2, iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakWordWide32) { - // U+1D49C MATHEMATICAL SCRIPT CAPITAL A - const char very_wide_char[] = "\xF0\x9D\x92\x9C"; - const string16 str( - UTF8ToUTF16(base::StringPrintf("%s a", very_wide_char))); - const string16 very_wide_word(str.substr(0, 2)); - - BreakIterator iter(str, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(very_wide_word, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("a"), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakWordThai) { - // Terms in Thai, without spaces in between. - const char term1[] = "พิมพ์"; - const char term2[] = "น้อย"; - const char term3[] = "ลง"; - const string16 str(UTF8ToUTF16(base::JoinString({term1, term2, term3}, ""))); - - BreakIterator iter(str, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(term1), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(term2), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(term3), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); -} - -// In some languages, the words are not broken by spaces. ICU provides a huge -// dictionary to detect word boundaries in Thai, Chinese, Japanese, Burmese, -// and Khmer. Due to the size of such a table, the part for Chinese and -// Japanese is not shipped on mobile. -#if !(defined(OS_IOS) || defined(OS_ANDROID)) - -TEST(BreakIteratorTest, BreakWordChinese) { - // Terms in Traditional Chinese, without spaces in between. - const char term1[] = "瀏覽"; - const char term2[] = "速度"; - const char term3[] = "飛快"; - const string16 str(UTF8ToUTF16(base::JoinString({term1, term2, term3}, ""))); - - BreakIterator iter(str, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(term1), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(term2), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(term3), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakWordJapanese) { - // Terms in Japanese, without spaces in between. - const char term1[] = "モバイル"; - const char term2[] = "でも"; - const string16 str(UTF8ToUTF16(base::JoinString({term1, term2}, ""))); - - BreakIterator iter(str, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(term1), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(term2), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakWordChineseEnglish) { - // Terms in Simplified Chinese mixed with English and wide punctuations. - string16 space(UTF8ToUTF16(" ")); - const char token1[] = "下载"; - const char token2[] = "Chrome"; - const char token3[] = "("; - const char token4[] = "Mac"; - const char token5[] = "版"; - const char token6[] = ")"; - const string16 str(UTF8ToUTF16(base::JoinString( - {token1, " ", token2, token3, token4, " ", token5, token6}, ""))); - - BreakIterator iter(str, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(token1), iter.GetString()); - - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(space, iter.GetString()); - - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(token2), iter.GetString()); - - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(token3), iter.GetString()); - - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(token4), iter.GetString()); - - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(space, iter.GetString()); - - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(token5), iter.GetString()); - - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(token6), iter.GetString()); - - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); -} - -#endif // !(defined(OS_IOS) || defined(OS_ANDROID)) - -TEST(BreakIteratorTest, BreakSpaceEmpty) { - string16 empty; - BreakIterator iter(empty, BreakIterator::BREAK_SPACE); - ASSERT_TRUE(iter.Init()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakSpace) { - string16 str(UTF8ToUTF16(" foo bar! \npouet boom")); - BreakIterator iter(str, BreakIterator::BREAK_SPACE); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("foo "), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("bar! \n"), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("pouet "), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("boom"), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakSpaceSP) { - string16 str(UTF8ToUTF16(" foo bar! \npouet boom ")); - BreakIterator iter(str, BreakIterator::BREAK_SPACE); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16(" "), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("foo "), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("bar! \n"), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("pouet "), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("boom "), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakSpacekWide16) { - // Two Greek words. - const string16 str(WideToUTF16( - L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9" - L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2")); - const string16 word1(str.substr(0, 11)); - const string16 word2(str.substr(11, 5)); - BreakIterator iter(str, BreakIterator::BREAK_SPACE); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(word1, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(word2, iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakSpaceWide32) { - // U+1D49C MATHEMATICAL SCRIPT CAPITAL A - const char very_wide_char[] = "\xF0\x9D\x92\x9C"; - const string16 str( - UTF8ToUTF16(base::StringPrintf("%s a", very_wide_char))); - const string16 very_wide_word(str.substr(0, 3)); - - BreakIterator iter(str, BreakIterator::BREAK_SPACE); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(very_wide_word, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("a"), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakLineEmpty) { - string16 empty; - BreakIterator iter(empty, BreakIterator::BREAK_NEWLINE); - ASSERT_TRUE(iter.Init()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakLine) { - string16 nl(UTF8ToUTF16("\n")); - string16 str(UTF8ToUTF16("\nfoo bar!\n\npouet boom")); - BreakIterator iter(str, BreakIterator::BREAK_NEWLINE); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(nl, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("foo bar!\n"), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(nl, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("pouet boom"), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakLineNL) { - string16 nl(UTF8ToUTF16("\n")); - string16 str(UTF8ToUTF16("\nfoo bar!\n\npouet boom\n")); - BreakIterator iter(str, BreakIterator::BREAK_NEWLINE); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(nl, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("foo bar!\n"), iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(nl, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("pouet boom\n"), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakLineWide16) { - // Two Greek words separated by newline. - const string16 str(WideToUTF16( - L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9" - L"\x03bf\x03c2\x000a\x0399\x03c3\x03c4\x03cc\x03c2")); - const string16 line1(str.substr(0, 11)); - const string16 line2(str.substr(11, 5)); - BreakIterator iter(str, BreakIterator::BREAK_NEWLINE); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(line1, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(line2, iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakLineWide32) { - // U+1D49C MATHEMATICAL SCRIPT CAPITAL A - const char very_wide_char[] = "\xF0\x9D\x92\x9C"; - const string16 str( - UTF8ToUTF16(base::StringPrintf("%s\na", very_wide_char))); - const string16 very_wide_line(str.substr(0, 3)); - BreakIterator iter(str, BreakIterator::BREAK_NEWLINE); - ASSERT_TRUE(iter.Init()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(very_wide_line, iter.GetString()); - EXPECT_TRUE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_EQ(UTF8ToUTF16("a"), iter.GetString()); - EXPECT_FALSE(iter.Advance()); - EXPECT_FALSE(iter.IsWord()); - EXPECT_FALSE(iter.Advance()); // Test unexpected advance after end. - EXPECT_FALSE(iter.IsWord()); -} - -TEST(BreakIteratorTest, BreakCharacter) { - static const wchar_t* kCharacters[] = { - // An English word consisting of four ASCII characters. - L"w", L"o", L"r", L"d", L" ", - // A Hindi word (which means "Hindi") consisting of three Devanagari - // characters. - L"\x0939\x093F", L"\x0928\x094D", L"\x0926\x0940", L" ", - // A Thai word (which means "feel") consisting of three Thai characters. - L"\x0E23\x0E39\x0E49", L"\x0E2A\x0E36", L"\x0E01", L" ", - }; - std::vector characters; - string16 text; - for (size_t i = 0; i < arraysize(kCharacters); ++i) { - characters.push_back(WideToUTF16(kCharacters[i])); - text.append(characters.back()); - } - BreakIterator iter(text, BreakIterator::BREAK_CHARACTER); - ASSERT_TRUE(iter.Init()); - for (size_t i = 0; i < arraysize(kCharacters); ++i) { - EXPECT_TRUE(iter.Advance()); - EXPECT_EQ(characters[i], iter.GetString()); - } -} - -// Test for https://code.google.com/p/chromium/issues/detail?id=411213 -// We should be able to get valid substrings with GetString() function -// after setting new content by calling SetText(). -TEST(BreakIteratorTest, GetStringAfterSetText) { - const string16 initial_string(ASCIIToUTF16("str")); - BreakIterator iter(initial_string, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - - const string16 long_string(ASCIIToUTF16("another,string")); - EXPECT_TRUE(iter.SetText(long_string.c_str(), long_string.size())); - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.Advance()); // Advance to ',' in |long_string| - - // Check that the current position is out of bounds of the |initial_string|. - EXPECT_LT(initial_string.size(), iter.pos()); - - // Check that we can get a valid substring of |long_string|. - EXPECT_EQ(ASCIIToUTF16(","), iter.GetString()); -} - -TEST(BreakIteratorTest, GetStringPiece) { - const string16 initial_string(ASCIIToUTF16("some string")); - BreakIterator iter(initial_string, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - - EXPECT_TRUE(iter.Advance()); - EXPECT_EQ(iter.GetString(), iter.GetStringPiece().as_string()); - EXPECT_EQ(StringPiece16(ASCIIToUTF16("some")), iter.GetStringPiece()); - - EXPECT_TRUE(iter.Advance()); - EXPECT_TRUE(iter.Advance()); - EXPECT_EQ(iter.GetString(), iter.GetStringPiece().as_string()); - EXPECT_EQ(StringPiece16(ASCIIToUTF16("string")), iter.GetStringPiece()); -} - -// Make sure that when not in RULE_BASED or BREAK_WORD mode we're getting -// IS_LINE_OR_CHAR_BREAK. -TEST(BreakIteratorTest, GetWordBreakStatusBreakLine) { - // A string containing the English word "foo", followed by two Khmer - // characters, the English word "Can", and then two Russian characters and - // punctuation. - base::string16 text( - base::WideToUTF16(L"foo \x1791\x17C1 \nCan \x041C\x0438...")); - BreakIterator iter(text, BreakIterator::BREAK_LINE); - ASSERT_TRUE(iter.Init()); - - EXPECT_TRUE(iter.Advance()); - // Finds "foo" and the space. - EXPECT_EQ(base::UTF8ToUTF16("foo "), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_LINE_OR_CHAR_BREAK); - EXPECT_TRUE(iter.Advance()); - // Finds the Khmer characters, the next space, and the newline. - EXPECT_EQ(base::WideToUTF16(L"\x1791\x17C1 \n"), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_LINE_OR_CHAR_BREAK); - EXPECT_TRUE(iter.Advance()); - // Finds "Can" and the space. - EXPECT_EQ(base::UTF8ToUTF16("Can "), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_LINE_OR_CHAR_BREAK); - EXPECT_TRUE(iter.Advance()); - // Finds the Russian characters and periods. - EXPECT_EQ(base::WideToUTF16(L"\x041C\x0438..."), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_LINE_OR_CHAR_BREAK); - EXPECT_FALSE(iter.Advance()); -} - -// Make sure that in BREAK_WORD mode we're getting IS_WORD_BREAK and -// IS_SKIPPABLE_WORD when we should be. IS_WORD_BREAK should be returned when we -// finish going over non-punctuation characters while IS_SKIPPABLE_WORD should -// be returned on punctuation and spaces. -TEST(BreakIteratorTest, GetWordBreakStatusBreakWord) { - // A string containing the English word "foo", followed by two Khmer - // characters, the English word "Can", and then two Russian characters and - // punctuation. - base::string16 text( - base::WideToUTF16(L"foo \x1791\x17C1 \nCan \x041C\x0438...")); - BreakIterator iter(text, BreakIterator::BREAK_WORD); - ASSERT_TRUE(iter.Init()); - - EXPECT_TRUE(iter.Advance()); - // Finds "foo". - EXPECT_EQ(base::UTF8ToUTF16("foo"), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_WORD_BREAK); - EXPECT_TRUE(iter.Advance()); - // Finds the space, and the Khmer characters. - EXPECT_EQ(base::UTF8ToUTF16(" "), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD); - EXPECT_TRUE(iter.Advance()); - EXPECT_EQ(base::WideToUTF16(L"\x1791\x17C1"), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_WORD_BREAK); - EXPECT_TRUE(iter.Advance()); - // Finds the space and the newline. - EXPECT_EQ(base::UTF8ToUTF16(" "), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD); - EXPECT_TRUE(iter.Advance()); - EXPECT_EQ(base::UTF8ToUTF16("\n"), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD); - EXPECT_TRUE(iter.Advance()); - // Finds "Can". - EXPECT_EQ(base::UTF8ToUTF16("Can"), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_WORD_BREAK); - EXPECT_TRUE(iter.Advance()); - // Finds the space and the Russian characters. - EXPECT_EQ(base::UTF8ToUTF16(" "), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD); - EXPECT_TRUE(iter.Advance()); - EXPECT_EQ(base::WideToUTF16(L"\x041C\x0438"), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_WORD_BREAK); - EXPECT_TRUE(iter.Advance()); - // Finds the trailing periods. - EXPECT_EQ(base::UTF8ToUTF16("."), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD); - EXPECT_TRUE(iter.Advance()); - EXPECT_EQ(base::UTF8ToUTF16("."), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD); - EXPECT_TRUE(iter.Advance()); - EXPECT_EQ(base::UTF8ToUTF16("."), iter.GetString()); - EXPECT_EQ(iter.GetWordBreakStatus(), BreakIterator::IS_SKIPPABLE_WORD); - EXPECT_FALSE(iter.Advance()); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/build_utf8_validator_tables.cc b/i18n/build_utf8_validator_tables.cc deleted file mode 100644 index 0cdcc3519..000000000 --- a/i18n/build_utf8_validator_tables.cc +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// 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_t throughout to represent bytes, to avoid -// signed/unsigned char confusion. - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/macros.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= ]\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_t 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_t 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_t to) { - CHECK(to == to_ + 1); - to_ = to; - } - - uint8_t from() const { return from_; } - uint8_t 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_t from_; - uint8_t 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 StringSet; - -// A UTF-8 "character" is represented by a sequence of bytes. -typedef std::vector 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 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_t 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(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_t 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(set_it->from()), - static_cast(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_t from; - uint8_t target_state; -}; - -typedef std::vector 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 StateMap; - -// Create a new state corresponding to |set|, add it |states| and |state_map| -// and return the index it was given in |states|. -uint8_t MakeState(const StringSet& set, - std::vector* 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_t 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(range.to() + 1), 1}}; - states->push_back( - State(new_state_initializer, - new_state_initializer + arraysize(new_state_initializer))); - const uint8_t new_state_number = - base::checked_cast(states->size() - 1); - CHECK(state_map->insert(std::make_pair(set, new_state_number)).second); - return new_state_number; -} - -std::vector GenerateStates(const PairVector& pairs) { - // States 0 and 1 are the initial/valid state and invalid state, respectively. - std::vector 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_t 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(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(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& 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 state_offset(1, 0); - std::vector shifts; - uint8_t pos = 0; - - for (std::vector::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_t 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_t state_index = 0; state_index < states.size(); ++state_index) { - const uint8_t shift = shifts[state_index]; - uint8_t next_range = 0; - uint8_t target_state = 1; - fprintf(stream, - " // State %d, offset 0x%02x\n", - static_cast(state_index), - static_cast(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[]) { - base::CommandLine::Init(argc, argv); - logging::LoggingSettings settings; - settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; - logging::InitLogging(settings); - if (base::CommandLine::ForCurrentProcess()->HasSwitch("help")) { - fwrite(kHelpText, 1, arraysize(kHelpText), stdout); - exit(EXIT_SUCCESS); - } - base::FilePath filename = - base::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 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/i18n/case_conversion.cc b/i18n/case_conversion.cc deleted file mode 100644 index a4a104cf9..000000000 --- a/i18n/case_conversion.cc +++ /dev/null @@ -1,90 +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/i18n/case_conversion.h" - -#include - -#include "base/numerics/safe_conversions.h" -#include "base/strings/string16.h" -#include "base/strings/string_util.h" -#include "third_party/icu/source/common/unicode/uchar.h" -#include "third_party/icu/source/common/unicode/unistr.h" -#include "third_party/icu/source/common/unicode/ustring.h" - -namespace base { -namespace i18n { - -namespace { - -// Provides a uniform interface for upper/lower/folding which take take -// slightly varying parameters. -typedef int32_t (*CaseMapperFunction)(UChar* dest, int32_t dest_capacity, - const UChar* src, int32_t src_length, - UErrorCode* error); - -int32_t ToUpperMapper(UChar* dest, int32_t dest_capacity, - const UChar* src, int32_t src_length, - UErrorCode* error) { - // Use default locale. - return u_strToUpper(dest, dest_capacity, src, src_length, nullptr, error); -} - -int32_t ToLowerMapper(UChar* dest, int32_t dest_capacity, - const UChar* src, int32_t src_length, - UErrorCode* error) { - // Use default locale. - return u_strToLower(dest, dest_capacity, src, src_length, nullptr, error); -} - -int32_t FoldCaseMapper(UChar* dest, int32_t dest_capacity, - const UChar* src, int32_t src_length, - UErrorCode* error) { - return u_strFoldCase(dest, dest_capacity, src, src_length, - U_FOLD_CASE_DEFAULT, error); -} - -// Provides similar functionality as UnicodeString::caseMap but on string16. -string16 CaseMap(StringPiece16 string, CaseMapperFunction case_mapper) { - string16 dest; - if (string.empty()) - return dest; - - // Provide an initial guess that the string length won't change. The typical - // strings we use will very rarely change length in this process, so don't - // optimize for that case. - dest.resize(string.size()); - - UErrorCode error; - do { - error = U_ZERO_ERROR; - - // ICU won't terminate the string if there's not enough room for the null - // terminator, but will otherwise. So we don't need to save room for that. - // Don't use WriteInto, which assumes null terminators. - int32_t new_length = case_mapper( - &dest[0], saturated_cast(dest.size()), - string.data(), saturated_cast(string.size()), - &error); - dest.resize(new_length); - } while (error == U_BUFFER_OVERFLOW_ERROR); - return dest; -} - -} // namespace - -string16 ToLower(StringPiece16 string) { - return CaseMap(string, &ToLowerMapper); -} - -string16 ToUpper(StringPiece16 string) { - return CaseMap(string, &ToUpperMapper); -} - -string16 FoldCase(StringPiece16 string) { - return CaseMap(string, &FoldCaseMapper); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/case_conversion.h b/i18n/case_conversion.h deleted file mode 100644 index 0631a800b..000000000 --- a/i18n/case_conversion.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_I18N_CASE_CONVERSION_H_ -#define BASE_I18N_CASE_CONVERSION_H_ - -#include "base/i18n/base_i18n_export.h" -#include "base/strings/string16.h" -#include "base/strings/string_piece.h" - -namespace base { -namespace i18n { - -// UNICODE CASE-HANDLING ADVICE -// -// In English it's always safe to convert to upper-case or lower-case text -// and get a good answer. But some languages have rules specific to those -// locales. One example is the Turkish I: -// http://www.i18nguy.com/unicode/turkish-i18n.html -// -// ToLower/ToUpper use the current ICU locale which will take into account -// the user language preference. Use this when dealing with user typing. -// -// FoldCase canonicalizes to a standardized form independent of the current -// locale. Use this when comparing general Unicode strings that don't -// necessarily belong in the user's current locale (like commands, protocol -// names, other strings from the web) for case-insensitive equality. -// -// Note that case conversions will change the length of the string in some -// not-uncommon cases. Never assume that the output is the same length as -// the input. - -// Returns the lower case equivalent of string. Uses ICU's current locale. -BASE_I18N_EXPORT string16 ToLower(StringPiece16 string); - -// Returns the upper case equivalent of string. Uses ICU's current locale. -BASE_I18N_EXPORT string16 ToUpper(StringPiece16 string); - -// Convert the given string to a canonical case, independent of the current -// locale. For ASCII the canonical form is lower case. -// See http://unicode.org/faq/casemap_charprop.html#2 -BASE_I18N_EXPORT string16 FoldCase(StringPiece16 string); - -} // namespace i18n -} // namespace base - -#endif // BASE_I18N_CASE_CONVERSION_H_ diff --git a/i18n/case_conversion_unittest.cc b/i18n/case_conversion_unittest.cc deleted file mode 100644 index ee795bc6e..000000000 --- a/i18n/case_conversion_unittest.cc +++ /dev/null @@ -1,119 +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/i18n/case_conversion.h" -#include "base/i18n/rtl.h" -#include "base/strings/utf_string_conversions.h" -#include "base/test/icu_test_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/icu/source/i18n/unicode/usearch.h" - -namespace base { -namespace i18n { - -namespace { - -const wchar_t kNonASCIIMixed[] = - L"\xC4\xD6\xE4\xF6\x20\xCF\xEF\x20\xF7\x25" - L"\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F07\x1F0F" - L"\x20\x1E00\x1E01"; -const wchar_t kNonASCIILower[] = - L"\xE4\xF6\xE4\xF6\x20\xEF\xEF" - L"\x20\xF7\x25\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F07" - L"\x1F07\x20\x1E01\x1E01"; -const wchar_t kNonASCIIUpper[] = - L"\xC4\xD6\xC4\xD6\x20\xCF\xCF" - L"\x20\xF7\x25\xA4\x23\x2A\x5E\x60\x40\xA3\x24\x2030\x201A\x7E\x20\x1F0F" - L"\x1F0F\x20\x1E00\x1E00"; - -} // namespace - -// Test upper and lower case string conversion. -TEST(CaseConversionTest, UpperLower) { - const string16 mixed(ASCIIToUTF16("Text with UPPer & lowER casE.")); - const string16 expected_lower(ASCIIToUTF16("text with upper & lower case.")); - const string16 expected_upper(ASCIIToUTF16("TEXT WITH UPPER & LOWER CASE.")); - - string16 result = ToLower(mixed); - EXPECT_EQ(expected_lower, result); - - result = ToUpper(mixed); - EXPECT_EQ(expected_upper, result); -} - -TEST(CaseConversionTest, NonASCII) { - const string16 mixed(WideToUTF16(kNonASCIIMixed)); - const string16 expected_lower(WideToUTF16(kNonASCIILower)); - const string16 expected_upper(WideToUTF16(kNonASCIIUpper)); - - string16 result = ToLower(mixed); - EXPECT_EQ(expected_lower, result); - - result = ToUpper(mixed); - EXPECT_EQ(expected_upper, result); -} - -TEST(CaseConversionTest, TurkishLocaleConversion) { - const string16 mixed(WideToUTF16(L"\x49\x131")); - const string16 expected_lower(WideToUTF16(L"\x69\x131")); - const string16 expected_upper(WideToUTF16(L"\x49\x49")); - - test::ScopedRestoreICUDefaultLocale restore_locale; - i18n::SetICUDefaultLocale("en_US"); - - string16 result = ToLower(mixed); - EXPECT_EQ(expected_lower, result); - - result = ToUpper(mixed); - EXPECT_EQ(expected_upper, result); - - i18n::SetICUDefaultLocale("tr"); - - const string16 expected_lower_turkish(WideToUTF16(L"\x131\x131")); - const string16 expected_upper_turkish(WideToUTF16(L"\x49\x49")); - - result = ToLower(mixed); - EXPECT_EQ(expected_lower_turkish, result); - - result = ToUpper(mixed); - EXPECT_EQ(expected_upper_turkish, result); -} - -TEST(CaseConversionTest, FoldCase) { - // Simple ASCII, should lower-case. - EXPECT_EQ(ASCIIToUTF16("hello, world"), - FoldCase(ASCIIToUTF16("Hello, World"))); - - // Non-ASCII cases from above. They should all fold to the same result. - EXPECT_EQ(FoldCase(WideToUTF16(kNonASCIIMixed)), - FoldCase(WideToUTF16(kNonASCIILower))); - EXPECT_EQ(FoldCase(WideToUTF16(kNonASCIIMixed)), - FoldCase(WideToUTF16(kNonASCIIUpper))); - - // Turkish cases from above. This is the lower-case expected result from the - // US locale. It should be the same even when the current locale is Turkish. - const string16 turkish(WideToUTF16(L"\x49\x131")); - const string16 turkish_expected(WideToUTF16(L"\x69\x131")); - - test::ScopedRestoreICUDefaultLocale restore_locale; - i18n::SetICUDefaultLocale("en_US"); - EXPECT_EQ(turkish_expected, FoldCase(turkish)); - - i18n::SetICUDefaultLocale("tr"); - EXPECT_EQ(turkish_expected, FoldCase(turkish)); - - // Test a case that gets bigger when processed. - // U+130 = LATIN CAPITAL LETTER I WITH DOT ABOVE gets folded to a lower case - // "i" followed by U+307 COMBINING DOT ABOVE. - EXPECT_EQ(WideToUTF16(L"i\u0307j"), FoldCase(WideToUTF16(L"\u0130j"))); - - // U+00DF (SHARP S) and U+1E9E (CAPIRAL SHARP S) are both folded to "ss". - EXPECT_EQ(ASCIIToUTF16("ssss"), FoldCase(WideToUTF16(L"\u00DF\u1E9E"))); -} - -} // namespace i18n -} // namespace base - - - diff --git a/i18n/char_iterator.cc b/i18n/char_iterator.cc deleted file mode 100644 index d80b8b618..000000000 --- a/i18n/char_iterator.cc +++ /dev/null @@ -1,80 +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/i18n/char_iterator.h" - -#include "third_party/icu/source/common/unicode/utf8.h" -#include "third_party/icu/source/common/unicode/utf16.h" - -namespace base { -namespace i18n { - -UTF8CharIterator::UTF8CharIterator(const std::string* str) - : str_(reinterpret_cast(str->data())), - len_(str->size()), - array_pos_(0), - next_pos_(0), - char_pos_(0), - char_(0) { - if (len_) - U8_NEXT(str_, next_pos_, len_, char_); -} - -UTF8CharIterator::~UTF8CharIterator() = default; - -bool UTF8CharIterator::Advance() { - if (array_pos_ >= len_) - return false; - - array_pos_ = next_pos_; - char_pos_++; - if (next_pos_ < len_) - U8_NEXT(str_, next_pos_, len_, char_); - - return true; -} - -UTF16CharIterator::UTF16CharIterator(const string16* str) - : str_(reinterpret_cast(str->data())), - len_(str->size()), - array_pos_(0), - next_pos_(0), - char_pos_(0), - char_(0) { - if (len_) - ReadChar(); -} - -UTF16CharIterator::UTF16CharIterator(const char16* str, size_t str_len) - : str_(str), - len_(str_len), - array_pos_(0), - next_pos_(0), - char_pos_(0), - char_(0) { - if (len_) - ReadChar(); -} - -UTF16CharIterator::~UTF16CharIterator() = default; - -bool UTF16CharIterator::Advance() { - if (array_pos_ >= len_) - return false; - - array_pos_ = next_pos_; - char_pos_++; - if (next_pos_ < len_) - ReadChar(); - - return true; -} - -void UTF16CharIterator::ReadChar() { - // This is actually a huge macro, so is worth having in a separate function. - U16_NEXT(str_, next_pos_, len_, char_); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/char_iterator.h b/i18n/char_iterator.h deleted file mode 100644 index 24024d492..000000000 --- a/i18n/char_iterator.h +++ /dev/null @@ -1,134 +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_I18N_CHAR_ITERATOR_H_ -#define BASE_I18N_CHAR_ITERATOR_H_ - -#include -#include - -#include - -#include "base/i18n/base_i18n_export.h" -#include "base/macros.h" -#include "base/strings/string16.h" -#include "build/build_config.h" - -// The CharIterator classes iterate through the characters in UTF8 and -// UTF16 strings. Example usage: -// -// UTF8CharIterator iter(&str); -// while (!iter.end()) { -// VLOG(1) << iter.get(); -// iter.Advance(); -// } - -#if defined(OS_WIN) -typedef unsigned char uint8_t; -#endif - -namespace base { -namespace i18n { - -class BASE_I18N_EXPORT UTF8CharIterator { - public: - // Requires |str| to live as long as the UTF8CharIterator does. - explicit UTF8CharIterator(const std::string* str); - ~UTF8CharIterator(); - - // Return the starting array index of the current character within the - // string. - int32_t array_pos() const { return array_pos_; } - - // Return the logical index of the current character, independent of the - // number of bytes each character takes. - int32_t char_pos() const { return char_pos_; } - - // Return the current char. - int32_t get() const { return char_; } - - // Returns true if we're at the end of the string. - bool end() const { return array_pos_ == len_; } - - // Advance to the next actual character. Returns false if we're at the - // end of the string. - bool Advance(); - - private: - // The string we're iterating over. - const uint8_t* str_; - - // The length of the encoded string. - int32_t len_; - - // Array index. - int32_t array_pos_; - - // The next array index. - int32_t next_pos_; - - // Character index. - int32_t char_pos_; - - // The current character. - int32_t char_; - - DISALLOW_COPY_AND_ASSIGN(UTF8CharIterator); -}; - -class BASE_I18N_EXPORT UTF16CharIterator { - public: - // Requires |str| to live as long as the UTF16CharIterator does. - explicit UTF16CharIterator(const string16* str); - UTF16CharIterator(const char16* str, size_t str_len); - ~UTF16CharIterator(); - - // Return the starting array index of the current character within the - // string. - int32_t array_pos() const { return array_pos_; } - - // Return the logical index of the current character, independent of the - // number of codewords each character takes. - int32_t char_pos() const { return char_pos_; } - - // Return the current char. - int32_t get() const { return char_; } - - // Returns true if we're at the end of the string. - bool end() const { return array_pos_ == len_; } - - // Advance to the next actual character. Returns false if we're at the - // end of the string. - bool Advance(); - - private: - // Fills in the current character we found and advances to the next - // character, updating all flags as necessary. - void ReadChar(); - - // The string we're iterating over. - const char16* str_; - - // The length of the encoded string. - int32_t len_; - - // Array index. - int32_t array_pos_; - - // The next array index. - int32_t next_pos_; - - // Character index. - int32_t char_pos_; - - // The current character. - int32_t char_; - - DISALLOW_COPY_AND_ASSIGN(UTF16CharIterator); -}; - -} // namespace i18n -} // namespace base - -#endif // BASE_I18N_CHAR_ITERATOR_H_ diff --git a/i18n/char_iterator_unittest.cc b/i18n/char_iterator_unittest.cc deleted file mode 100644 index 0cf8e6c07..000000000 --- a/i18n/char_iterator_unittest.cc +++ /dev/null @@ -1,101 +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/i18n/char_iterator.h" - -#include "base/strings/utf_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace i18n { - -TEST(CharIteratorsTest, TestUTF8) { - std::string empty; - UTF8CharIterator empty_iter(&empty); - ASSERT_TRUE(empty_iter.end()); - ASSERT_EQ(0, empty_iter.array_pos()); - ASSERT_EQ(0, empty_iter.char_pos()); - ASSERT_FALSE(empty_iter.Advance()); - - std::string str("s\303\273r"); // [u with circumflex] - UTF8CharIterator iter(&str); - ASSERT_FALSE(iter.end()); - ASSERT_EQ(0, iter.array_pos()); - ASSERT_EQ(0, iter.char_pos()); - ASSERT_EQ('s', iter.get()); - ASSERT_TRUE(iter.Advance()); - - ASSERT_FALSE(iter.end()); - ASSERT_EQ(1, iter.array_pos()); - ASSERT_EQ(1, iter.char_pos()); - ASSERT_EQ(251, iter.get()); - ASSERT_TRUE(iter.Advance()); - - ASSERT_FALSE(iter.end()); - ASSERT_EQ(3, iter.array_pos()); - ASSERT_EQ(2, iter.char_pos()); - ASSERT_EQ('r', iter.get()); - ASSERT_TRUE(iter.Advance()); - - ASSERT_TRUE(iter.end()); - ASSERT_EQ(4, iter.array_pos()); - ASSERT_EQ(3, iter.char_pos()); - - // Don't care what it returns, but this shouldn't crash - iter.get(); - - ASSERT_FALSE(iter.Advance()); -} - -TEST(CharIteratorsTest, TestUTF16) { - string16 empty = UTF8ToUTF16(""); - UTF16CharIterator empty_iter(&empty); - ASSERT_TRUE(empty_iter.end()); - ASSERT_EQ(0, empty_iter.array_pos()); - ASSERT_EQ(0, empty_iter.char_pos()); - ASSERT_FALSE(empty_iter.Advance()); - - // This test string contains 4 characters: - // x - // u with circumflex - 2 bytes in UTF8, 1 codeword in UTF16 - // math double-struck A - 4 bytes in UTF8, 2 codewords in UTF16 - // z - string16 str = UTF8ToUTF16("x\303\273\360\235\224\270z"); - UTF16CharIterator iter(&str); - ASSERT_FALSE(iter.end()); - ASSERT_EQ(0, iter.array_pos()); - ASSERT_EQ(0, iter.char_pos()); - ASSERT_EQ('x', iter.get()); - ASSERT_TRUE(iter.Advance()); - - ASSERT_FALSE(iter.end()); - ASSERT_EQ(1, iter.array_pos()); - ASSERT_EQ(1, iter.char_pos()); - ASSERT_EQ(251, iter.get()); - ASSERT_TRUE(iter.Advance()); - - ASSERT_FALSE(iter.end()); - ASSERT_EQ(2, iter.array_pos()); - ASSERT_EQ(2, iter.char_pos()); - ASSERT_EQ(120120, iter.get()); - ASSERT_TRUE(iter.Advance()); - - ASSERT_FALSE(iter.end()); - ASSERT_EQ(4, iter.array_pos()); - ASSERT_EQ(3, iter.char_pos()); - ASSERT_EQ('z', iter.get()); - ASSERT_TRUE(iter.Advance()); - - ASSERT_TRUE(iter.end()); - ASSERT_EQ(5, iter.array_pos()); - ASSERT_EQ(4, iter.char_pos()); - - // Don't care what it returns, but this shouldn't crash - iter.get(); - - ASSERT_FALSE(iter.Advance()); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/character_encoding.cc b/i18n/character_encoding.cc deleted file mode 100644 index a1068c3c5..000000000 --- a/i18n/character_encoding.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2016 The Chromium 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/character_encoding.h" - -#include "base/macros.h" -#include "third_party/icu/source/common/unicode/ucnv.h" - -namespace base { -namespace { - -// An array of all supported canonical encoding names. -const char* const kCanonicalEncodingNames[] = { - "Big5", "EUC-JP", "EUC-KR", "gb18030", - "GBK", "IBM866", "ISO-2022-JP", "ISO-8859-10", - "ISO-8859-13", "ISO-8859-14", "ISO-8859-15", "ISO-8859-16", - "ISO-8859-2", "ISO-8859-3", "ISO-8859-4", "ISO-8859-5", - "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "ISO-8859-8-I", - "KOI8-R", "KOI8-U", "macintosh", "Shift_JIS", - "UTF-16LE", "UTF-8", "windows-1250", "windows-1251", - "windows-1252", "windows-1253", "windows-1254", "windows-1255", - "windows-1256", "windows-1257", "windows-1258", "windows-874"}; - -} // namespace - -std::string GetCanonicalEncodingNameByAliasName(const std::string& alias_name) { - for (auto* encoding_name : kCanonicalEncodingNames) { - if (alias_name == encoding_name) - return alias_name; - } - static const char* kStandards[3] = {"HTML", "MIME", "IANA"}; - for (auto* standard : kStandards) { - UErrorCode error_code = U_ZERO_ERROR; - const char* canonical_name = - ucnv_getStandardName(alias_name.c_str(), standard, &error_code); - if (U_SUCCESS(error_code) && canonical_name) - return canonical_name; - } - return std::string(); -} -} // namespace base diff --git a/i18n/character_encoding.h b/i18n/character_encoding.h deleted file mode 100644 index 974cb5a6f..000000000 --- a/i18n/character_encoding.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2016 The Chromium 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_CHARACTER_ENCODING_H_ -#define BASE_I18N_CHARACTER_ENCODING_H_ - -#include - -#include "base/i18n/base_i18n_export.h" - -namespace base { - -// Return canonical encoding name according to the encoding alias name. -BASE_I18N_EXPORT std::string GetCanonicalEncodingNameByAliasName( - const std::string& alias_name); - -} // namespace base - -#endif // BASE_I18N_CHARACTER_ENCODING_H_ diff --git a/i18n/character_encoding_unittest.cc b/i18n/character_encoding_unittest.cc deleted file mode 100644 index 3c11ba30a..000000000 --- a/i18n/character_encoding_unittest.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2016 The Chromium 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/character_encoding.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(CharacterEncodingTest, GetCanonicalEncodingNameByAliasName) { - EXPECT_EQ("Big5", GetCanonicalEncodingNameByAliasName("Big5")); - EXPECT_EQ("windows-874", GetCanonicalEncodingNameByAliasName("windows-874")); - EXPECT_EQ("ISO-8859-8", GetCanonicalEncodingNameByAliasName("ISO-8859-8")); - - // Non-canonical alias names should be converted to a canonical one. - EXPECT_EQ("UTF-8", GetCanonicalEncodingNameByAliasName("utf8")); - EXPECT_EQ("gb18030", GetCanonicalEncodingNameByAliasName("GB18030")); - EXPECT_EQ("windows-874", GetCanonicalEncodingNameByAliasName("tis-620")); - EXPECT_EQ("EUC-KR", GetCanonicalEncodingNameByAliasName("ks_c_5601-1987")); -} - -} // namespace base diff --git a/i18n/encoding_detection.cc b/i18n/encoding_detection.cc deleted file mode 100644 index fef34e4ab..000000000 --- a/i18n/encoding_detection.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2016 The Chromium 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/encoding_detection.h" - -#include "build/build_config.h" -#include "third_party/ced/src/compact_enc_det/compact_enc_det.h" - -// third_party/ced/src/util/encodings/encodings.h, which is included -// by the include above, undefs UNICODE because that is a macro used -// internally in ced. If we later in the same translation unit do -// anything related to Windows or Windows headers those will then use -// the ASCII versions which we do not want. To avoid that happening in -// jumbo builds, we redefine UNICODE again here. -#if defined(OS_WIN) -#define UNICODE 1 -#endif // OS_WIN - -namespace base { - -bool DetectEncoding(const std::string& text, std::string* encoding) { - int consumed_bytes; - bool is_reliable; - Encoding enc = CompactEncDet::DetectEncoding( - text.c_str(), text.length(), nullptr, nullptr, nullptr, - UNKNOWN_ENCODING, - UNKNOWN_LANGUAGE, - CompactEncDet::QUERY_CORPUS, // plain text - false, // Include 7-bit encodings - &consumed_bytes, - &is_reliable); - - if (enc == UNKNOWN_ENCODING) - return false; - - *encoding = MimeEncodingName(enc); - return true; -} -} // namespace base diff --git a/i18n/encoding_detection.h b/i18n/encoding_detection.h deleted file mode 100644 index c8e660c44..000000000 --- a/i18n/encoding_detection.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 The Chromium 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_ENCODING_DETECTION_H_ -#define BASE_I18N_ENCODING_DETECTION_H_ - -#include - -#include "base/compiler_specific.h" -#include "base/i18n/base_i18n_export.h" - -namespace base { - -// Detect encoding of |text| and put the name of encoding in |encoding|. -// Returns true on success. -BASE_I18N_EXPORT bool DetectEncoding(const std::string& text, - std::string* encoding) WARN_UNUSED_RESULT; -} // namespace base - -#endif // BASE_I18N_ENCODING_DETECTION_H_ diff --git a/i18n/file_util_icu.cc b/i18n/file_util_icu.cc deleted file mode 100644 index c91aea1a4..000000000 --- a/i18n/file_util_icu.cc +++ /dev/null @@ -1,179 +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. - -// File utilities that use the ICU library go in this file. - -#include "base/i18n/file_util_icu.h" - -#include - -#include - -#include "base/files/file_path.h" -#include "base/i18n/icu_string_conversions.h" -#include "base/i18n/string_compare.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/singleton.h" -#include "base/strings/string_util.h" -#include "base/strings/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" -#include "third_party/icu/source/common/unicode/uniset.h" -#include "third_party/icu/source/i18n/unicode/coll.h" - -namespace base { -namespace i18n { - -namespace { - -class IllegalCharacters { - public: - static IllegalCharacters* GetInstance() { - return Singleton::get(); - } - - bool DisallowedEverywhere(UChar32 ucs4) { - return !!illegal_anywhere_->contains(ucs4); - } - - bool DisallowedLeadingOrTrailing(UChar32 ucs4) { - return !!illegal_at_ends_->contains(ucs4); - } - - bool IsAllowedName(const string16& s) { - return s.empty() || (!!illegal_anywhere_->containsNone( - icu::UnicodeString(s.c_str(), s.size())) && - !illegal_at_ends_->contains(*s.begin()) && - !illegal_at_ends_->contains(*s.rbegin())); - } - - private: - friend class Singleton; - friend struct DefaultSingletonTraits; - - IllegalCharacters(); - ~IllegalCharacters() = default; - - // set of characters considered invalid anywhere inside a filename. - std::unique_ptr illegal_anywhere_; - - // set of characters considered invalid at either end of a filename. - std::unique_ptr illegal_at_ends_; - - DISALLOW_COPY_AND_ASSIGN(IllegalCharacters); -}; - -IllegalCharacters::IllegalCharacters() { - UErrorCode everywhere_status = U_ZERO_ERROR; - UErrorCode ends_status = U_ZERO_ERROR; - // Control characters, formatting characters, non-characters, path separators, - // and some printable ASCII characters regarded as dangerous ('"*/:<>?\\'). - // See http://blogs.msdn.com/michkap/archive/2006/11/03/941420.aspx - // and http://msdn2.microsoft.com/en-us/library/Aa365247.aspx - // Note that code points in the "Other, Format" (Cf) category are ignored on - // HFS+ despite the ZERO_WIDTH_JOINER and ZERO_WIDTH_NON-JOINER being - // legitimate in Arabic and some S/SE Asian scripts. In addition tilde (~) is - // also excluded due to the possibility of interacting poorly with short - // filenames on VFAT. (Related to CVE-2014-9390) - illegal_anywhere_.reset(new icu::UnicodeSet( - UNICODE_STRING_SIMPLE("[[\"~*/:<>?\\\\|][:Cc:][:Cf:]]"), - everywhere_status)); - illegal_at_ends_.reset(new icu::UnicodeSet( - UNICODE_STRING_SIMPLE("[[:WSpace:][.]]"), ends_status)); - DCHECK(U_SUCCESS(everywhere_status)); - DCHECK(U_SUCCESS(ends_status)); - - // Add non-characters. If this becomes a performance bottleneck by - // any chance, do not add these to |set| and change IsFilenameLegal() - // to check |ucs4 & 0xFFFEu == 0xFFFEu|, in addiition to calling - // IsAllowedName(). - illegal_anywhere_->add(0xFDD0, 0xFDEF); - for (int i = 0; i <= 0x10; ++i) { - int plane_base = 0x10000 * i; - illegal_anywhere_->add(plane_base + 0xFFFE, plane_base + 0xFFFF); - } - illegal_anywhere_->freeze(); - illegal_at_ends_->freeze(); -} - -} // namespace - -bool IsFilenameLegal(const string16& file_name) { - return IllegalCharacters::GetInstance()->IsAllowedName(file_name); -} - -void ReplaceIllegalCharactersInPath(FilePath::StringType* file_name, - char replace_char) { - IllegalCharacters* illegal = IllegalCharacters::GetInstance(); - - DCHECK(!(illegal->DisallowedEverywhere(replace_char))); - DCHECK(!(illegal->DisallowedLeadingOrTrailing(replace_char))); - - int cursor = 0; // The ICU macros expect an int. - while (cursor < static_cast(file_name->size())) { - int char_begin = cursor; - uint32_t code_point; -#if defined(OS_WIN) - // Windows uses UTF-16 encoding for filenames. - U16_NEXT(file_name->data(), cursor, static_cast(file_name->length()), - code_point); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // Mac and Chrome OS use UTF-8 encoding for filenames. - // Linux doesn't actually define file system encoding. Try to parse as - // UTF-8. - U8_NEXT(file_name->data(), cursor, static_cast(file_name->length()), - code_point); -#else -#error Unsupported platform -#endif - - if (illegal->DisallowedEverywhere(code_point) || - ((char_begin == 0 || cursor == static_cast(file_name->length())) && - illegal->DisallowedLeadingOrTrailing(code_point))) { - file_name->replace(char_begin, cursor - char_begin, 1, replace_char); - // We just made the potentially multi-byte/word char into one that only - // takes one byte/word, so need to adjust the cursor to point to the next - // character again. - cursor = char_begin + 1; - } - } -} - -bool LocaleAwareCompareFilenames(const FilePath& a, const FilePath& b) { - UErrorCode error_code = U_ZERO_ERROR; - // Use the default collator. The default locale should have been properly - // set by the time this constructor is called. - std::unique_ptr collator( - icu::Collator::createInstance(error_code)); - DCHECK(U_SUCCESS(error_code)); - // Make it case-sensitive. - collator->setStrength(icu::Collator::TERTIARY); - -#if defined(OS_WIN) - return CompareString16WithCollator(*collator, WideToUTF16(a.value()), - WideToUTF16(b.value())) == UCOL_LESS; - -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // On linux, the file system encoding is not defined. We assume - // SysNativeMBToWide takes care of it. - return CompareString16WithCollator( - *collator, WideToUTF16(SysNativeMBToWide(a.value())), - WideToUTF16(SysNativeMBToWide(b.value()))) == UCOL_LESS; -#endif -} - -void NormalizeFileNameEncoding(FilePath* file_name) { -#if defined(OS_CHROMEOS) - std::string normalized_str; - if (ConvertToUtf8AndNormalize(file_name->BaseName().value(), kCodepageUTF8, - &normalized_str) && - !normalized_str.empty()) { - *file_name = file_name->DirName().Append(FilePath(normalized_str)); - } -#endif -} - -} // namespace i18n -} // namespace base diff --git a/i18n/file_util_icu.h b/i18n/file_util_icu.h deleted file mode 100644 index f8bd9f44d..000000000 --- a/i18n/file_util_icu.h +++ /dev/null @@ -1,58 +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_I18N_FILE_UTIL_ICU_H_ -#define BASE_I18N_FILE_UTIL_ICU_H_ - -// File utilities that use the ICU library go in this file. - -#include "base/files/file_path.h" -#include "base/i18n/base_i18n_export.h" -#include "base/strings/string16.h" - -namespace base { -namespace i18n { - -// Returns true if file_name does not have any illegal character. The input -// param has the same restriction as that for ReplaceIllegalCharacters. -BASE_I18N_EXPORT bool IsFilenameLegal(const string16& file_name); - -// Replaces characters in |file_name| that are illegal for file names with -// |replace_char|. |file_name| must not be a full or relative path, but just the -// file name component (since slashes are considered illegal). Any leading or -// trailing whitespace or periods in |file_name| is also replaced with the -// |replace_char|. -// -// Example: -// "bad:file*name?.txt" will be turned into "bad_file_name_.txt" when -// |replace_char| is '_'. -// -// Warning: Do not use this function as the sole means of sanitizing a filename. -// While the resulting filename itself would be legal, it doesn't necessarily -// mean that the file will behave safely. On Windows, certain reserved names -// refer to devices rather than files (E.g. LPT1), and some filenames could be -// interpreted as shell namespace extensions (E.g. Foo.{}). -// -// On Windows, Chrome OS and Mac, the file system encoding is already known and -// parsed as UTF-8 and UTF-16 accordingly. -// On Linux, the file name will be parsed as UTF8. -// TODO(asanka): Move full filename sanitization logic here. -BASE_I18N_EXPORT void ReplaceIllegalCharactersInPath( - FilePath::StringType* file_name, - char replace_char); - -// Compares two filenames using the current locale information. This can be -// used to sort directory listings. It behaves like "operator<" for use in -// std::sort. -BASE_I18N_EXPORT bool LocaleAwareCompareFilenames(const FilePath& a, - const FilePath& b); - -// Calculates the canonical file-system representation of |file_name| base name. -// Modifies |file_name| in place. No-op if not on ChromeOS. -BASE_I18N_EXPORT void NormalizeFileNameEncoding(FilePath* file_name); - -} // namespace i18n -} // namespace base - -#endif // BASE_I18N_FILE_UTIL_ICU_H_ diff --git a/i18n/file_util_icu_unittest.cc b/i18n/file_util_icu_unittest.cc deleted file mode 100644 index 062d29b0d..000000000 --- a/i18n/file_util_icu_unittest.cc +++ /dev/null @@ -1,140 +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/i18n/file_util_icu.h" - -#include - -#include "base/files/file_util.h" -#include "base/macros.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -namespace base { -namespace i18n { - -// file_util winds up using autoreleased objects on the Mac, so this needs -// to be a PlatformTest -class FileUtilICUTest : public PlatformTest { -}; - -#if defined(OS_POSIX) && !defined(OS_MACOSX) - -// On linux, file path is parsed and filtered as UTF-8. -static const struct GoodBadPairLinux { - const char* bad_name; - const char* good_name; -} kLinuxIllegalCharacterCases[] = { - {"bad*\\/file:name?.jpg", "bad---file-name-.jpg"}, - {"**********::::.txt", "--------------.txt"}, - {"\xe9\xf0zzzz.\xff", "\xe9\xf0zzzz.\xff"}, - {" _ ", "-_-"}, - {".", "-"}, - {" .( ). ", "-.( ).-"}, - {" ", "- -"}, -}; - -TEST_F(FileUtilICUTest, ReplaceIllegalCharactersInPathLinuxTest) { - for (size_t i = 0; i < arraysize(kLinuxIllegalCharacterCases); ++i) { - std::string bad_name(kLinuxIllegalCharacterCases[i].bad_name); - ReplaceIllegalCharactersInPath(&bad_name, '-'); - EXPECT_EQ(kLinuxIllegalCharacterCases[i].good_name, bad_name); - } -} - -#endif - -// For Mac & Windows, which both do Unicode validation on filenames. These -// characters are given as wide strings since its more convenient to specify -// unicode characters. For Mac they should be converted to UTF-8. -static const struct goodbad_pair { - const wchar_t* bad_name; - const wchar_t* good_name; -} kIllegalCharacterCases[] = { - {L"bad*file:name?.jpg", L"bad-file-name-.jpg"}, - {L"**********::::.txt", L"--------------.txt"}, - // We can't use UCNs (universal character names) for C0/C1 characters and - // U+007F, but \x escape is interpreted by MSVC and gcc as we intend. - {L"bad\x0003\x0091 file\u200E\u200Fname.png", L"bad-- file--name.png"}, - {L"bad*file\\?name.jpg", L"bad-file--name.jpg"}, - {L"\t bad*file\\name/.jpg", L"- bad-file-name-.jpg"}, - {L"this_file_name is okay!.mp3", L"this_file_name is okay!.mp3"}, - {L"\u4E00\uAC00.mp3", L"\u4E00\uAC00.mp3"}, - {L"\u0635\u200C\u0644.mp3", L"\u0635-\u0644.mp3"}, - {L"\U00010330\U00010331.mp3", L"\U00010330\U00010331.mp3"}, - // Unassigned codepoints are ok. - {L"\u0378\U00040001.mp3", L"\u0378\U00040001.mp3"}, - // Non-characters are not allowed. - {L"bad\uFFFFfile\U0010FFFEname.jpg", L"bad-file-name.jpg"}, - {L"bad\uFDD0file\uFDEFname.jpg", L"bad-file-name.jpg"}, - // CVE-2014-9390 - {L"(\u200C.\u200D.\u200E.\u200F.\u202A.\u202B.\u202C.\u202D.\u202E.\u206A." - L"\u206B.\u206C.\u206D.\u206F.\uFEFF)", - L"(-.-.-.-.-.-.-.-.-.-.-.-.-.-.-)"}, - {L"config~1", L"config-1"}, - {L" _ ", L"-_-"}, - {L" ", L"-"}, - {L"\u2008.(\u2007).\u3000", L"-.(\u2007).-"}, - {L" ", L"- -"}, - {L". ", L"- -"} -}; - -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_POSIX) - -TEST_F(FileUtilICUTest, ReplaceIllegalCharactersInPathTest) { - for (size_t i = 0; i < arraysize(kIllegalCharacterCases); ++i) { -#if defined(OS_WIN) - std::wstring bad_name(kIllegalCharacterCases[i].bad_name); - ReplaceIllegalCharactersInPath(&bad_name, '-'); - EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name); -#else - std::string bad_name(WideToUTF8(kIllegalCharacterCases[i].bad_name)); - ReplaceIllegalCharactersInPath(&bad_name, '-'); - EXPECT_EQ(WideToUTF8(kIllegalCharacterCases[i].good_name), bad_name); -#endif - } -} - -#endif - -TEST_F(FileUtilICUTest, IsFilenameLegalTest) { - EXPECT_TRUE(IsFilenameLegal(string16())); - - for (const auto& test_case : kIllegalCharacterCases) { - string16 bad_name = WideToUTF16(test_case.bad_name); - string16 good_name = WideToUTF16(test_case.good_name); - - EXPECT_TRUE(IsFilenameLegal(good_name)) << good_name; - if (good_name != bad_name) - EXPECT_FALSE(IsFilenameLegal(bad_name)) << bad_name; - } -} - -#if defined(OS_CHROMEOS) -static const struct normalize_name_encoding_test_cases { - const char* original_path; - const char* normalized_path; -} kNormalizeFileNameEncodingTestCases[] = { - { "foo_na\xcc\x88me.foo", "foo_n\xc3\xa4me.foo"}, - { "foo_dir_na\xcc\x88me/foo_na\xcc\x88me.foo", - "foo_dir_na\xcc\x88me/foo_n\xc3\xa4me.foo"}, - { "", ""}, - { "foo_dir_na\xcc\x88me/", "foo_dir_n\xc3\xa4me"} -}; - -TEST_F(FileUtilICUTest, NormalizeFileNameEncoding) { - for (size_t i = 0; i < arraysize(kNormalizeFileNameEncodingTestCases); i++) { - FilePath path(kNormalizeFileNameEncodingTestCases[i].original_path); - NormalizeFileNameEncoding(&path); - EXPECT_EQ(FilePath(kNormalizeFileNameEncodingTestCases[i].normalized_path), - path); - } -} - -#endif - -} // namespace i18n -} // namespace base diff --git a/i18n/i18n_constants.cc b/i18n/i18n_constants.cc deleted file mode 100644 index 7d2f5fc05..000000000 --- a/i18n/i18n_constants.cc +++ /dev/null @@ -1,13 +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/i18n/i18n_constants.h" - -namespace base { - -const char kCodepageLatin1[] = "ISO-8859-1"; -const char kCodepageUTF8[] = "UTF-8"; - -} // namespace base - diff --git a/i18n/i18n_constants.h b/i18n/i18n_constants.h deleted file mode 100644 index c1bd87dc4..000000000 --- a/i18n/i18n_constants.h +++ /dev/null @@ -1,21 +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_I18N_I18N_CONSTANTS_H_ -#define BASE_I18N_I18N_CONSTANTS_H_ - -#include "base/i18n/base_i18n_export.h" - -namespace base { - -// Names of codepages (charsets) understood by icu. -BASE_I18N_EXPORT extern const char kCodepageLatin1[]; // a.k.a. ISO 8859-1 -BASE_I18N_EXPORT extern const char kCodepageUTF8[]; - -// The other possible options are UTF-16BE and UTF-16LE, but they are unused in -// Chromium as of this writing. - -} // namespace base - -#endif // BASE_I18N_I18N_CONSTANTS_H_ diff --git a/i18n/icu_string_conversions.cc b/i18n/icu_string_conversions.cc deleted file mode 100644 index 6ec99803a..000000000 --- a/i18n/icu_string_conversions.cc +++ /dev/null @@ -1,223 +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/i18n/icu_string_conversions.h" - -#include -#include - -#include -#include - -#include "base/logging.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "third_party/icu/source/common/unicode/normalizer2.h" -#include "third_party/icu/source/common/unicode/ucnv.h" -#include "third_party/icu/source/common/unicode/ucnv_cb.h" -#include "third_party/icu/source/common/unicode/ucnv_err.h" -#include "third_party/icu/source/common/unicode/ustring.h" - -namespace base { - -namespace { -// ToUnicodeCallbackSubstitute() is based on UCNV_TO_U_CALLBACK_SUBSTITUTE -// in source/common/ucnv_err.c. - -// Copyright (c) 1995-2006 International Business Machines Corporation -// and others -// -// All rights reserved. -// - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, and/or -// sell copies of the Software, and to permit persons to whom the Software -// is furnished to do so, provided that the above copyright notice(s) and -// this permission notice appear in all copies of the Software and that -// both the above copyright notice(s) and this permission notice appear in -// supporting documentation. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -// OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS -// INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT -// OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE -// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE -// OR PERFORMANCE OF THIS SOFTWARE. -// -// Except as contained in this notice, the name of a copyright holder -// shall not be used in advertising or otherwise to promote the sale, use -// or other dealings in this Software without prior written authorization -// of the copyright holder. - -// ___________________________________________________________________________ -// -// All trademarks and registered trademarks mentioned herein are the property -// of their respective owners. - -void ToUnicodeCallbackSubstitute(const void* context, - UConverterToUnicodeArgs *to_args, - const char* code_units, - int32_t length, - UConverterCallbackReason reason, - UErrorCode * err) { - static const UChar kReplacementChar = 0xFFFD; - if (reason <= UCNV_IRREGULAR) { - if (context == nullptr || - (*(reinterpret_cast(context)) == 'i' && - reason == UCNV_UNASSIGNED)) { - *err = U_ZERO_ERROR; - ucnv_cbToUWriteUChars(to_args, &kReplacementChar, 1, 0, err); - } - // else the caller must have set the error code accordingly. - } - // else ignore the reset, close and clone calls. -} - -bool ConvertFromUTF16(UConverter* converter, const UChar* uchar_src, - int uchar_len, OnStringConversionError::Type on_error, - std::string* encoded) { - int encoded_max_length = UCNV_GET_MAX_BYTES_FOR_STRING(uchar_len, - ucnv_getMaxCharSize(converter)); - encoded->resize(encoded_max_length); - - UErrorCode status = U_ZERO_ERROR; - - // Setup our error handler. - switch (on_error) { - case OnStringConversionError::FAIL: - ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_STOP, nullptr, - nullptr, nullptr, &status); - break; - case OnStringConversionError::SKIP: - case OnStringConversionError::SUBSTITUTE: - ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_SKIP, nullptr, - nullptr, nullptr, &status); - break; - default: - NOTREACHED(); - } - - // ucnv_fromUChars returns size not including terminating null - int actual_size = ucnv_fromUChars(converter, &(*encoded)[0], - encoded_max_length, uchar_src, uchar_len, &status); - encoded->resize(actual_size); - ucnv_close(converter); - if (U_SUCCESS(status)) - return true; - encoded->clear(); // Make sure the output is empty on error. - return false; -} - -// Set up our error handler for ToUTF-16 converters -void SetUpErrorHandlerForToUChars(OnStringConversionError::Type on_error, - UConverter* converter, UErrorCode* status) { - switch (on_error) { - case OnStringConversionError::FAIL: - ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_STOP, nullptr, nullptr, - nullptr, status); - break; - case OnStringConversionError::SKIP: - ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_SKIP, nullptr, nullptr, - nullptr, status); - break; - case OnStringConversionError::SUBSTITUTE: - ucnv_setToUCallBack(converter, ToUnicodeCallbackSubstitute, nullptr, - nullptr, nullptr, status); - break; - default: - NOTREACHED(); - } -} - -} // namespace - -// Codepage <-> Wide/UTF-16 --------------------------------------------------- - -bool UTF16ToCodepage(const string16& utf16, - const char* codepage_name, - OnStringConversionError::Type on_error, - std::string* encoded) { - encoded->clear(); - - UErrorCode status = U_ZERO_ERROR; - UConverter* converter = ucnv_open(codepage_name, &status); - if (!U_SUCCESS(status)) - return false; - - return ConvertFromUTF16(converter, utf16.c_str(), - static_cast(utf16.length()), on_error, encoded); -} - -bool CodepageToUTF16(const std::string& encoded, - const char* codepage_name, - OnStringConversionError::Type on_error, - string16* utf16) { - utf16->clear(); - - UErrorCode status = U_ZERO_ERROR; - UConverter* converter = ucnv_open(codepage_name, &status); - if (!U_SUCCESS(status)) - return false; - - // Even in the worst case, the maximum length in 2-byte units of UTF-16 - // output would be at most the same as the number of bytes in input. There - // is no single-byte encoding in which a character is mapped to a - // non-BMP character requiring two 2-byte units. - // - // Moreover, non-BMP characters in legacy multibyte encodings - // (e.g. EUC-JP, GB18030) take at least 2 bytes. The only exceptions are - // BOCU and SCSU, but we don't care about them. - size_t uchar_max_length = encoded.length() + 1; - - SetUpErrorHandlerForToUChars(on_error, converter, &status); - std::unique_ptr buffer(new char16[uchar_max_length]); - int actual_size = ucnv_toUChars(converter, buffer.get(), - static_cast(uchar_max_length), encoded.data(), - static_cast(encoded.length()), &status); - ucnv_close(converter); - if (!U_SUCCESS(status)) { - utf16->clear(); // Make sure the output is empty on error. - return false; - } - - utf16->assign(buffer.get(), actual_size); - return true; -} - -bool ConvertToUtf8AndNormalize(const std::string& text, - const std::string& charset, - std::string* result) { - result->clear(); - string16 utf16; - if (!CodepageToUTF16( - text, charset.c_str(), OnStringConversionError::FAIL, &utf16)) - return false; - - UErrorCode status = U_ZERO_ERROR; - const icu::Normalizer2* normalizer = icu::Normalizer2::getNFCInstance(status); - DCHECK(U_SUCCESS(status)); - if (U_FAILURE(status)) - return false; - int32_t utf16_length = static_cast(utf16.length()); - icu::UnicodeString normalized(utf16.data(), utf16_length); - int32_t normalized_prefix_length = - normalizer->spanQuickCheckYes(normalized, status); - if (normalized_prefix_length < utf16_length) { - icu::UnicodeString un_normalized(normalized, normalized_prefix_length); - normalized.truncate(normalized_prefix_length); - normalizer->normalizeSecondAndAppend(normalized, un_normalized, status); - } - if (U_FAILURE(status)) - return false; - normalized.toUTF8String(*result); - return true; -} - -} // namespace base diff --git a/i18n/icu_string_conversions.h b/i18n/icu_string_conversions.h deleted file mode 100644 index cbdcb99e4..000000000 --- a/i18n/icu_string_conversions.h +++ /dev/null @@ -1,57 +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_I18N_ICU_STRING_CONVERSIONS_H_ -#define BASE_I18N_ICU_STRING_CONVERSIONS_H_ - -#include - -#include "base/i18n/base_i18n_export.h" -#include "base/i18n/i18n_constants.h" -#include "base/strings/string16.h" - -namespace base { - -// Defines the error handling modes of UTF16ToCodepage and CodepageToUTF16. -class OnStringConversionError { - public: - enum Type { - // The function will return failure. The output buffer will be empty. - FAIL, - - // The offending characters are skipped and the conversion will proceed as - // if they did not exist. - SKIP, - - // When converting to Unicode, the offending byte sequences are substituted - // by Unicode replacement character (U+FFFD). When converting from Unicode, - // this is the same as SKIP. - SUBSTITUTE, - }; - - private: - OnStringConversionError() = delete; -}; - -// Converts between UTF-16 strings and the encoding specified. If the -// encoding doesn't exist or the encoding fails (when on_error is FAIL), -// returns false. -BASE_I18N_EXPORT bool UTF16ToCodepage(const string16& utf16, - const char* codepage_name, - OnStringConversionError::Type on_error, - std::string* encoded); -BASE_I18N_EXPORT bool CodepageToUTF16(const std::string& encoded, - const char* codepage_name, - OnStringConversionError::Type on_error, - string16* utf16); - -// Converts from any codepage to UTF-8 and ensures the resulting UTF-8 is -// normalized. -BASE_I18N_EXPORT bool ConvertToUtf8AndNormalize(const std::string& text, - const std::string& charset, - std::string* result); - -} // namespace base - -#endif // BASE_I18N_ICU_STRING_CONVERSIONS_H_ diff --git a/i18n/icu_string_conversions_unittest.cc b/i18n/icu_string_conversions_unittest.cc deleted file mode 100644 index d1559860c..000000000 --- a/i18n/icu_string_conversions_unittest.cc +++ /dev/null @@ -1,235 +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 -#include -#include - -#include -#include - -#include "base/format_macros.h" -#include "base/i18n/icu_string_conversions.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -// Given a null-terminated string of wchar_t with each wchar_t representing -// a UTF-16 code unit, returns a string16 made up of wchar_t's in the input. -// Each wchar_t should be <= 0xFFFF and a non-BMP character (> U+FFFF) -// should be represented as a surrogate pair (two UTF-16 units) -// *even* where wchar_t is 32-bit (Linux and Mac). -// -// This is to help write tests for functions with string16 params until -// the C++ 0x UTF-16 literal is well-supported by compilers. -string16 BuildString16(const wchar_t* s) { -#if defined(WCHAR_T_IS_UTF16) - return string16(s); -#elif defined(WCHAR_T_IS_UTF32) - string16 u16; - while (*s != 0) { - DCHECK_LE(static_cast(*s), 0xFFFFu); - u16.push_back(*s++); - } - return u16; -#endif -} - -} // namespace - -// kConverterCodepageCases is not comprehensive. There are a number of cases -// to add if we really want to have a comprehensive coverage of various -// codepages and their 'idiosyncrasies'. Currently, the only implementation -// for CodepageTo* and *ToCodepage uses ICU, which has a very extensive -// set of tests for the charset conversion. So, we can get away with a -// relatively small number of cases listed below. -// -// Note about |u16_wide| in the following struct. -// On Windows, the field is always identical to |wide|. On Mac and Linux, -// it's identical as long as there's no character outside the -// BMP (<= U+FFFF). When there is, it is different from |wide| and -// is not a real wide string (UTF-32 string) in that each wchar_t in -// the string is a UTF-16 code unit zero-extended to be 32-bit -// even when the code unit belongs to a surrogate pair. -// For instance, a Unicode string (U+0041 U+010000) is represented as -// L"\x0041\xD800\xDC00" instead of L"\x0041\x10000". -// To avoid the clutter, |u16_wide| will be set to NULL -// if it's identical to |wide| on *all* platforms. - -static const struct { - const char* codepage_name; - const char* encoded; - OnStringConversionError::Type on_error; - bool success; - const wchar_t* wide; - const wchar_t* u16_wide; -} kConvertCodepageCases[] = { - // Test a case where the input cannot be decoded, using SKIP, FAIL - // and SUBSTITUTE error handling rules. "A7 41" is valid, but "A6" isn't. - {"big5", "\xA7\x41\xA6", OnStringConversionError::FAIL, false, L"", - nullptr}, - {"big5", "\xA7\x41\xA6", OnStringConversionError::SKIP, true, L"\x4F60", - nullptr}, - {"big5", "\xA7\x41\xA6", OnStringConversionError::SUBSTITUTE, true, - L"\x4F60\xFFFD", nullptr}, - // Arabic (ISO-8859) - {"iso-8859-6", - "\xC7\xEE\xE4\xD3\xF1\xEE\xE4\xC7\xE5\xEF" - " " - "\xD9\xEE\xE4\xEE\xEA\xF2\xE3\xEF\xE5\xF2", - OnStringConversionError::FAIL, true, - L"\x0627\x064E\x0644\x0633\x0651\x064E\x0644\x0627\x0645\x064F" - L" " - L"\x0639\x064E\x0644\x064E\x064A\x0652\x0643\x064F\x0645\x0652", - nullptr}, - // Chinese Simplified (GB2312) - {"gb2312", "\xC4\xE3\xBA\xC3", OnStringConversionError::FAIL, true, - L"\x4F60\x597D", nullptr}, - // Chinese (GB18030) : 4 byte sequences mapped to BMP characters - {"gb18030", "\x81\x30\x84\x36\xA1\xA7", OnStringConversionError::FAIL, true, - L"\x00A5\x00A8", nullptr}, - // Chinese (GB18030) : A 4 byte sequence mapped to plane 2 (U+20000) - {"gb18030", "\x95\x32\x82\x36\xD2\xBB", OnStringConversionError::FAIL, true, -#if defined(WCHAR_T_IS_UTF16) - L"\xD840\xDC00\x4E00", -#elif defined(WCHAR_T_IS_UTF32) - L"\x20000\x4E00", -#endif - L"\xD840\xDC00\x4E00"}, - {"big5", "\xA7\x41\xA6\x6E", OnStringConversionError::FAIL, true, - L"\x4F60\x597D", nullptr}, - // Greek (ISO-8859) - {"iso-8859-7", - "\xE3\xE5\xE9\xDC" - " " - "\xF3\xEF\xF5", - OnStringConversionError::FAIL, true, - L"\x03B3\x03B5\x03B9\x03AC" - L" " - L"\x03C3\x03BF\x03C5", - nullptr}, - // Hebrew (Windows) - {"windows-1255", "\xF9\xD1\xC8\xEC\xE5\xC9\xED", - OnStringConversionError::FAIL, true, - L"\x05E9\x05C1\x05B8\x05DC\x05D5\x05B9\x05DD", nullptr}, - // Korean (EUC) - {"euc-kr", "\xBE\xC8\xB3\xE7\xC7\xCF\xBC\xBC\xBF\xE4", - OnStringConversionError::FAIL, true, L"\xC548\xB155\xD558\xC138\xC694", - nullptr}, - // Japanese (EUC) - {"euc-jp", "\xA4\xB3\xA4\xF3\xA4\xCB\xA4\xC1\xA4\xCF\xB0\xEC\x8E\xA6", - OnStringConversionError::FAIL, true, - L"\x3053\x3093\x306B\x3061\x306F\x4E00\xFF66", nullptr}, - // Japanese (ISO-2022) - {"iso-2022-jp", - "\x1B$B" - "\x24\x33\x24\x73\x24\x4B\x24\x41\x24\x4F\x30\x6C" - "\x1B(B" - "ab" - "\x1B(J" - "\x5C\x7E#$" - "\x1B(B", - OnStringConversionError::FAIL, true, - L"\x3053\x3093\x306B\x3061\x306F\x4E00" - L"ab\x00A5\x203E#$", - nullptr}, - // Japanese (Shift-JIS) - {"sjis", "\x82\xB1\x82\xF1\x82\xC9\x82\xBF\x82\xCD\x88\xEA\xA6", - OnStringConversionError::FAIL, true, - L"\x3053\x3093\x306B\x3061\x306F\x4E00\xFF66", nullptr}, - // Russian (KOI8) - {"koi8-r", "\xDA\xC4\xD2\xC1\xD7\xD3\xD4\xD7\xD5\xCA\xD4\xC5", - OnStringConversionError::FAIL, true, - L"\x0437\x0434\x0440\x0430\x0432\x0441\x0442\x0432" - L"\x0443\x0439\x0442\x0435", - nullptr}, - // Thai (windows-874) - {"windows-874", - "\xCA\xC7\xD1\xCA\xB4\xD5" - "\xA4\xC3\xD1\xBA", - OnStringConversionError::FAIL, true, - L"\x0E2A\x0E27\x0E31\x0E2A\x0E14\x0E35" - L"\x0E04\x0E23\x0e31\x0E1A", - nullptr}, -}; - -TEST(ICUStringConversionsTest, ConvertBetweenCodepageAndUTF16) { - for (size_t i = 0; i < arraysize(kConvertCodepageCases); ++i) { - SCOPED_TRACE(base::StringPrintf( - "Test[%" PRIuS "]: ", i, - kConvertCodepageCases[i].encoded, - kConvertCodepageCases[i].codepage_name)); - - string16 utf16; - bool success = CodepageToUTF16(kConvertCodepageCases[i].encoded, - kConvertCodepageCases[i].codepage_name, - kConvertCodepageCases[i].on_error, - &utf16); - string16 utf16_expected; - if (kConvertCodepageCases[i].u16_wide == nullptr) - utf16_expected = BuildString16(kConvertCodepageCases[i].wide); - else - utf16_expected = BuildString16(kConvertCodepageCases[i].u16_wide); - EXPECT_EQ(kConvertCodepageCases[i].success, success); - EXPECT_EQ(utf16_expected, utf16); - - // When decoding was successful and nothing was skipped, we also check the - // reverse conversion. See also the corresponding comment in - // ConvertBetweenCodepageAndWide. - if (success && - kConvertCodepageCases[i].on_error == OnStringConversionError::FAIL) { - std::string encoded; - success = UTF16ToCodepage(utf16, kConvertCodepageCases[i].codepage_name, - kConvertCodepageCases[i].on_error, &encoded); - EXPECT_EQ(kConvertCodepageCases[i].success, success); - EXPECT_EQ(kConvertCodepageCases[i].encoded, encoded); - } - } -} - -static const struct { - const char* encoded; - const char* codepage_name; - bool expected_success; - const char* expected_value; -} kConvertAndNormalizeCases[] = { - {"foo-\xe4.html", "iso-8859-1", true, "foo-\xc3\xa4.html"}, - {"foo-\xe4.html", "iso-8859-7", true, "foo-\xce\xb4.html"}, - {"foo-\xe4.html", "foo-bar", false, ""}, - // HTML Encoding spec treats US-ASCII as synonymous with windows-1252 - {"foo-\xff.html", "ascii", true, "foo-\xc3\xbf.html"}, - {"foo.html", "ascii", true, "foo.html"}, - {"foo-a\xcc\x88.html", "utf-8", true, "foo-\xc3\xa4.html"}, - {"\x95\x32\x82\x36\xD2\xBB", "gb18030", true, "\xF0\xA0\x80\x80\xE4\xB8\x80"}, - {"\xA7\x41\xA6\x6E", "big5", true, "\xE4\xBD\xA0\xE5\xA5\xBD"}, - // Windows-1258 does have a combining character at xD2 (which is U+0309). - // The sequence of (U+00E2, U+0309) is also encoded as U+1EA9. - {"foo\xE2\xD2", "windows-1258", true, "foo\xE1\xBA\xA9"}, - {"", "iso-8859-1", true, ""}, -}; -TEST(ICUStringConversionsTest, ConvertToUtf8AndNormalize) { - std::string result; - for (size_t i = 0; i < arraysize(kConvertAndNormalizeCases); ++i) { - SCOPED_TRACE(base::StringPrintf( - "Test[%" PRIuS "]: ", i, - kConvertAndNormalizeCases[i].encoded, - kConvertAndNormalizeCases[i].codepage_name)); - - bool success = ConvertToUtf8AndNormalize( - kConvertAndNormalizeCases[i].encoded, - kConvertAndNormalizeCases[i].codepage_name, &result); - EXPECT_EQ(kConvertAndNormalizeCases[i].expected_success, success); - EXPECT_EQ(kConvertAndNormalizeCases[i].expected_value, result); - } -} - -} // namespace base diff --git a/i18n/icu_util.cc b/i18n/icu_util.cc deleted file mode 100644 index 4d588c616..000000000 --- a/i18n/icu_util.cc +++ /dev/null @@ -1,333 +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/i18n/icu_util.h" - -#if defined(OS_WIN) -#include -#endif - -#include - -#include "base/debug/alias.h" -#include "base/files/file_path.h" -#include "base/files/memory_mapped_file.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/strings/string_util.h" -#include "base/strings/sys_string_conversions.h" -#include "build/build_config.h" -#include "third_party/icu/source/common/unicode/putil.h" -#include "third_party/icu/source/common/unicode/udata.h" -#if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_ANDROID) -#include "third_party/icu/source/i18n/unicode/timezone.h" -#endif - -#if defined(OS_ANDROID) -#include "base/android/apk_assets.h" -#include "base/android/timezone_utils.h" -#endif - -#if defined(OS_IOS) -#include "base/ios/ios_util.h" -#endif - -#if defined(OS_MACOSX) -#include "base/mac/foundation_util.h" -#endif - -#if defined(OS_FUCHSIA) -#include "base/base_paths_fuchsia.h" -#endif - -namespace base { -namespace i18n { - -#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED -#define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat" -#if defined(OS_WIN) -#define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll" -#endif -#endif - -namespace { -#if !defined(OS_NACL) -#if DCHECK_IS_ON() -// 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_check_called_once = true; -bool g_called_once = false; -#endif // DCHECK_IS_ON() - -#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE - -// To debug http://crbug.com/445616. -int g_debug_icu_last_error; -int g_debug_icu_load; -int g_debug_icu_pf_error_details; -int g_debug_icu_pf_last_error; -#if defined(OS_WIN) -wchar_t g_debug_icu_pf_filename[_MAX_PATH]; -#endif // OS_WIN -// Use an unversioned file name to simplify a icu version update down the road. -// No need to change the filename in multiple places (gyp files, windows -// build pkg configurations, etc). 'l' stands for Little Endian. -// This variable is exported through the header file. -const char kIcuDataFileName[] = "icudtl.dat"; -#if defined(OS_ANDROID) -const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat"; -#endif - -// File handle intentionally never closed. Not using File here because its -// Windows implementation guards against two instances owning the same -// PlatformFile (which we allow since we know it is never freed). -PlatformFile g_icudtl_pf = kInvalidPlatformFile; -MemoryMappedFile* g_icudtl_mapped_file = nullptr; -MemoryMappedFile::Region g_icudtl_region; - -void LazyInitIcuDataFile() { - if (g_icudtl_pf != kInvalidPlatformFile) { - return; - } -#if defined(OS_ANDROID) - int fd = base::android::OpenApkAsset(kAndroidAssetsIcuDataFileName, - &g_icudtl_region); - g_icudtl_pf = fd; - if (fd != -1) { - return; - } -// For unit tests, data file is located on disk, so try there as a fallback. -#endif // defined(OS_ANDROID) -#if !defined(OS_MACOSX) - FilePath data_path; - if (!PathService::Get(DIR_ASSETS, &data_path)) { - LOG(ERROR) << "Can't find " << kIcuDataFileName; - return; - } -#if defined(OS_WIN) - // TODO(brucedawson): http://crbug.com/445616 - wchar_t tmp_buffer[_MAX_PATH] = {0}; - wcscpy_s(tmp_buffer, data_path.value().c_str()); - debug::Alias(tmp_buffer); -#endif - data_path = data_path.AppendASCII(kIcuDataFileName); - -#if defined(OS_WIN) - // TODO(brucedawson): http://crbug.com/445616 - wchar_t tmp_buffer2[_MAX_PATH] = {0}; - wcscpy_s(tmp_buffer2, data_path.value().c_str()); - debug::Alias(tmp_buffer2); -#endif - -#else // !defined(OS_MACOSX) - // Assume it is in the framework bundle's Resources directory. - ScopedCFTypeRef data_file_name( - SysUTF8ToCFStringRef(kIcuDataFileName)); - FilePath data_path = mac::PathForFrameworkBundleResource(data_file_name); -#if defined(OS_IOS) - FilePath override_data_path = base::ios::FilePathOfEmbeddedICU(); - if (!override_data_path.empty()) { - data_path = override_data_path; - } -#endif // !defined(OS_IOS) - if (data_path.empty()) { - LOG(ERROR) << kIcuDataFileName << " not found in bundle"; - return; - } -#endif // !defined(OS_MACOSX) - File file(data_path, File::FLAG_OPEN | File::FLAG_READ); - if (file.IsValid()) { - // TODO(brucedawson): http://crbug.com/445616. - g_debug_icu_pf_last_error = 0; - g_debug_icu_pf_error_details = 0; -#if defined(OS_WIN) - g_debug_icu_pf_filename[0] = 0; -#endif // OS_WIN - - g_icudtl_pf = file.TakePlatformFile(); - g_icudtl_region = MemoryMappedFile::Region::kWholeFile; - } -#if defined(OS_WIN) - else { - // TODO(brucedawson): http://crbug.com/445616. - g_debug_icu_pf_last_error = ::GetLastError(); - g_debug_icu_pf_error_details = file.error_details(); - wcscpy_s(g_debug_icu_pf_filename, data_path.value().c_str()); - } -#endif // OS_WIN -} - -bool InitializeICUWithFileDescriptorInternal( - PlatformFile data_fd, - const MemoryMappedFile::Region& data_region) { - // This can be called multiple times in tests. - if (g_icudtl_mapped_file) { - g_debug_icu_load = 0; // To debug http://crbug.com/445616. - return true; - } - if (data_fd == kInvalidPlatformFile) { - g_debug_icu_load = 1; // To debug http://crbug.com/445616. - LOG(ERROR) << "Invalid file descriptor to ICU data received."; - return false; - } - - std::unique_ptr icudtl_mapped_file(new MemoryMappedFile()); - if (!icudtl_mapped_file->Initialize(File(data_fd), data_region)) { - g_debug_icu_load = 2; // To debug http://crbug.com/445616. - LOG(ERROR) << "Couldn't mmap icu data file"; - return false; - } - g_icudtl_mapped_file = icudtl_mapped_file.release(); - - UErrorCode err = U_ZERO_ERROR; - udata_setCommonData(const_cast(g_icudtl_mapped_file->data()), &err); - if (err != U_ZERO_ERROR) { - g_debug_icu_load = 3; // To debug http://crbug.com/445616. - g_debug_icu_last_error = err; - } -#if defined(OS_ANDROID) - else { - // On Android, we can't leave it up to ICU to set the default timezone - // because ICU's timezone detection does not work in many timezones (e.g. - // Australia/Sydney, Asia/Seoul, Europe/Paris ). Use JNI to detect the host - // timezone and set the ICU default timezone accordingly in advance of - // actual use. See crbug.com/722821 and - // https://ssl.icu-project.org/trac/ticket/13208 . - base::string16 timezone_id = base::android::GetDefaultTimeZoneId(); - icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone( - icu::UnicodeString(FALSE, timezone_id.data(), timezone_id.length()))); - } -#endif - // Never try to load ICU data from files. - udata_setFileAccess(UDATA_ONLY_PACKAGES, &err); - return err == U_ZERO_ERROR; -} -#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE -#endif // !defined(OS_NACL) - -} // namespace - -#if !defined(OS_NACL) -#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE -#if defined(OS_ANDROID) -bool InitializeICUWithFileDescriptor( - PlatformFile data_fd, - const MemoryMappedFile::Region& data_region) { -#if DCHECK_IS_ON() - DCHECK(!g_check_called_once || !g_called_once); - g_called_once = true; -#endif - return InitializeICUWithFileDescriptorInternal(data_fd, data_region); -} - -PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) { - CHECK_NE(g_icudtl_pf, kInvalidPlatformFile); - *out_region = g_icudtl_region; - return g_icudtl_pf; -} -#endif - -const uint8_t* GetRawIcuMemory() { - CHECK(g_icudtl_mapped_file); - return g_icudtl_mapped_file->data(); -} - -bool InitializeICUFromRawMemory(const uint8_t* raw_memory) { -#if !defined(COMPONENT_BUILD) -#if DCHECK_IS_ON() - DCHECK(!g_check_called_once || !g_called_once); - g_called_once = true; -#endif - - UErrorCode err = U_ZERO_ERROR; - udata_setCommonData(const_cast(raw_memory), &err); - // Never try to load ICU data from files. - udata_setFileAccess(UDATA_ONLY_PACKAGES, &err); - return err == U_ZERO_ERROR; -#else - return true; -#endif -} - -#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE - -bool InitializeICU() { -#if DCHECK_IS_ON() - DCHECK(!g_check_called_once || !g_called_once); - g_called_once = true; -#endif - - bool result; -#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED) - FilePath data_path; - PathService::Get(DIR_ASSETS, &data_path); - data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME); - - HMODULE module = LoadLibrary(data_path.value().c_str()); - if (!module) { - LOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME; - return false; - } - - FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL); - if (!addr) { - LOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in " - << ICU_UTIL_DATA_SHARED_MODULE_NAME; - return false; - } - - UErrorCode err = U_ZERO_ERROR; - udata_setCommonData(reinterpret_cast(addr), &err); - // Never try to load ICU data from files. - udata_setFileAccess(UDATA_ONLY_PACKAGES, &err); - result = (err == U_ZERO_ERROR); -#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) - // The ICU data is statically linked. - result = true; -#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) - // If the ICU data directory is set, ICU won't actually load the data until - // it is needed. This can fail if the process is sandboxed at that time. - // Instead, we map the file in and hand off the data so the sandbox won't - // cause any problems. - LazyInitIcuDataFile(); - result = - InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region); -#if defined(OS_WIN) - int debug_icu_load = g_debug_icu_load; - debug::Alias(&debug_icu_load); - int debug_icu_last_error = g_debug_icu_last_error; - debug::Alias(&debug_icu_last_error); - int debug_icu_pf_last_error = g_debug_icu_pf_last_error; - debug::Alias(&debug_icu_pf_last_error); - int debug_icu_pf_error_details = g_debug_icu_pf_error_details; - debug::Alias(&debug_icu_pf_error_details); - wchar_t debug_icu_pf_filename[_MAX_PATH] = {0}; - wcscpy_s(debug_icu_pf_filename, g_debug_icu_pf_filename); - debug::Alias(&debug_icu_pf_filename); - CHECK(result); // TODO(brucedawson): http://crbug.com/445616 -#endif -#endif - -// To respond to the timezone change properly, the default timezone -// cache in ICU has to be populated on starting up. -// TODO(jungshik): Some callers do not care about tz at all. If necessary, -// add a boolean argument to this function to init'd the default tz only -// when requested. -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - if (result) - std::unique_ptr zone(icu::TimeZone::createDefault()); -#endif - return result; -} -#endif // !defined(OS_NACL) - -void AllowMultipleInitializeCallsForTesting() { -#if DCHECK_IS_ON() && !defined(OS_NACL) - g_check_called_once = false; -#endif -} - -} // namespace i18n -} // namespace base diff --git a/i18n/icu_util.h b/i18n/icu_util.h deleted file mode 100644 index 5f9948fa6..000000000 --- a/i18n/icu_util.h +++ /dev/null @@ -1,67 +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_I18N_ICU_UTIL_H_ -#define BASE_I18N_ICU_UTIL_H_ - -#include - -#include "base/files/memory_mapped_file.h" -#include "base/i18n/base_i18n_export.h" -#include "build/build_config.h" - -#define ICU_UTIL_DATA_FILE 0 -#define ICU_UTIL_DATA_SHARED 1 -#define ICU_UTIL_DATA_STATIC 2 - -namespace base { -namespace i18n { - -#if !defined(OS_NACL) -// Call this function to load ICU's data tables for the current process. This -// function should be called before ICU is used. -BASE_I18N_EXPORT bool InitializeICU(); - -#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE -#if defined(OS_ANDROID) -// Returns the PlatformFile and Region that was initialized by InitializeICU(). -// Use with InitializeICUWithFileDescriptor(). -BASE_I18N_EXPORT PlatformFile GetIcuDataFileHandle( - MemoryMappedFile::Region* out_region); - -// Android uses a file descriptor passed by browser process to initialize ICU -// in render processes. -BASE_I18N_EXPORT bool InitializeICUWithFileDescriptor( - PlatformFile data_fd, - const MemoryMappedFile::Region& data_region); -#endif - -// Returns a void pointer to the memory mapped ICU data file. -// -// There are cases on Android where we would be unsafely reusing a file -// descriptor within the same process when initializing two copies of ICU from -// different binaries in the same address space. This returns an unowned -// pointer to the memory mapped icu data file; consumers copies of base must -// not outlive the copy of base that owns the memory mapped file. -BASE_I18N_EXPORT const uint8_t* GetRawIcuMemory(); - -// Initializes ICU memory -// -// This does nothing in component builds; this initialization should only be -// done in cases where there could be two copies of base in a single process in -// non-component builds. (The big example is standalone service libraries: the -// Service Manager will have a copy of base linked in, and the majority of -// service libraries will have base linked in but in non-component builds, -// these will be separate copies of base.) -BASE_I18N_EXPORT bool InitializeICUFromRawMemory(const uint8_t* raw_memory); -#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE -#endif // !defined(OS_NACL) - -// In a test binary, the call above might occur twice. -BASE_I18N_EXPORT void AllowMultipleInitializeCallsForTesting(); - -} // namespace i18n -} // namespace base - -#endif // BASE_I18N_ICU_UTIL_H_ diff --git a/i18n/message_formatter.cc b/i18n/message_formatter.cc deleted file mode 100644 index c69dd07d3..000000000 --- a/i18n/message_formatter.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/i18n/message_formatter.h" - -#include "base/i18n/unicodestring.h" -#include "base/logging.h" -#include "base/numerics/safe_conversions.h" -#include "base/time/time.h" -#include "third_party/icu/source/common/unicode/unistr.h" -#include "third_party/icu/source/common/unicode/utypes.h" -#include "third_party/icu/source/i18n/unicode/fmtable.h" -#include "third_party/icu/source/i18n/unicode/msgfmt.h" - -using icu::UnicodeString; - -namespace base { -namespace i18n { -namespace { -UnicodeString UnicodeStringFromStringPiece(StringPiece str) { - return UnicodeString::fromUTF8( - icu::StringPiece(str.data(), base::checked_cast(str.size()))); -} -} // anonymous namespace - -namespace internal { -MessageArg::MessageArg() : formattable(nullptr) {} - -MessageArg::MessageArg(const char* s) - : formattable(new icu::Formattable(UnicodeStringFromStringPiece(s))) {} - -MessageArg::MessageArg(StringPiece s) - : formattable(new icu::Formattable(UnicodeStringFromStringPiece(s))) {} - -MessageArg::MessageArg(const std::string& s) - : formattable(new icu::Formattable(UnicodeString::fromUTF8(s))) {} - -MessageArg::MessageArg(const string16& s) - : formattable(new icu::Formattable(UnicodeString(s.data(), s.size()))) {} - -MessageArg::MessageArg(int i) : formattable(new icu::Formattable(i)) {} - -MessageArg::MessageArg(int64_t i) : formattable(new icu::Formattable(i)) {} - -MessageArg::MessageArg(double d) : formattable(new icu::Formattable(d)) {} - -MessageArg::MessageArg(const Time& t) - : formattable(new icu::Formattable(static_cast(t.ToJsTime()))) {} - -MessageArg::~MessageArg() = default; - -// Tests if this argument has a value, and if so increments *count. -bool MessageArg::has_value(int *count) const { - if (formattable == nullptr) - return false; - - ++*count; - return true; -} - -} // namespace internal - -string16 MessageFormatter::FormatWithNumberedArgs( - StringPiece16 msg, - const internal::MessageArg& arg0, - const internal::MessageArg& arg1, - const internal::MessageArg& arg2, - const internal::MessageArg& arg3, - const internal::MessageArg& arg4, - const internal::MessageArg& arg5, - const internal::MessageArg& arg6) { - int32_t args_count = 0; - icu::Formattable args[] = { - arg0.has_value(&args_count) ? *arg0.formattable : icu::Formattable(), - arg1.has_value(&args_count) ? *arg1.formattable : icu::Formattable(), - arg2.has_value(&args_count) ? *arg2.formattable : icu::Formattable(), - arg3.has_value(&args_count) ? *arg3.formattable : icu::Formattable(), - arg4.has_value(&args_count) ? *arg4.formattable : icu::Formattable(), - arg5.has_value(&args_count) ? *arg5.formattable : icu::Formattable(), - arg6.has_value(&args_count) ? *arg6.formattable : icu::Formattable(), - }; - - UnicodeString msg_string(msg.data(), msg.size()); - UErrorCode error = U_ZERO_ERROR; - icu::MessageFormat format(msg_string, error); - icu::UnicodeString formatted; - icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE); - format.format(args, args_count, formatted, ignore, error); - if (U_FAILURE(error)) { - LOG(ERROR) << "MessageFormat(" << msg.as_string() << ") failed with " - << u_errorName(error); - return string16(); - } - return i18n::UnicodeStringToString16(formatted); -} - -string16 MessageFormatter::FormatWithNamedArgs( - StringPiece16 msg, - StringPiece name0, const internal::MessageArg& arg0, - StringPiece name1, const internal::MessageArg& arg1, - StringPiece name2, const internal::MessageArg& arg2, - StringPiece name3, const internal::MessageArg& arg3, - StringPiece name4, const internal::MessageArg& arg4, - StringPiece name5, const internal::MessageArg& arg5, - StringPiece name6, const internal::MessageArg& arg6) { - icu::UnicodeString names[] = { - UnicodeStringFromStringPiece(name0), - UnicodeStringFromStringPiece(name1), - UnicodeStringFromStringPiece(name2), - UnicodeStringFromStringPiece(name3), - UnicodeStringFromStringPiece(name4), - UnicodeStringFromStringPiece(name5), - UnicodeStringFromStringPiece(name6), - }; - int32_t args_count = 0; - icu::Formattable args[] = { - arg0.has_value(&args_count) ? *arg0.formattable : icu::Formattable(), - arg1.has_value(&args_count) ? *arg1.formattable : icu::Formattable(), - arg2.has_value(&args_count) ? *arg2.formattable : icu::Formattable(), - arg3.has_value(&args_count) ? *arg3.formattable : icu::Formattable(), - arg4.has_value(&args_count) ? *arg4.formattable : icu::Formattable(), - arg5.has_value(&args_count) ? *arg5.formattable : icu::Formattable(), - arg6.has_value(&args_count) ? *arg6.formattable : icu::Formattable(), - }; - - UnicodeString msg_string(msg.data(), msg.size()); - UErrorCode error = U_ZERO_ERROR; - icu::MessageFormat format(msg_string, error); - - icu::UnicodeString formatted; - format.format(names, args, args_count, formatted, error); - if (U_FAILURE(error)) { - LOG(ERROR) << "MessageFormat(" << msg.as_string() << ") failed with " - << u_errorName(error); - return string16(); - } - return i18n::UnicodeStringToString16(formatted); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/message_formatter.h b/i18n/message_formatter.h deleted file mode 100644 index 36a656d77..000000000 --- a/i18n/message_formatter.h +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_I18N_MESSAGE_FORMATTER_H_ -#define BASE_I18N_MESSAGE_FORMATTER_H_ - -#include - -#include -#include - -#include "base/i18n/base_i18n_export.h" -#include "base/macros.h" -#include "base/strings/string16.h" -#include "base/strings/string_piece.h" -#include "third_party/icu/source/common/unicode/uversion.h" - -U_NAMESPACE_BEGIN -class Formattable; -U_NAMESPACE_END - -namespace base { - -class Time; - -namespace i18n { - -class MessageFormatter; - -namespace internal { - -class BASE_I18N_EXPORT MessageArg { - public: - MessageArg(const char* s); - MessageArg(StringPiece s); - MessageArg(const std::string& s); - MessageArg(const string16& s); - MessageArg(int i); - MessageArg(int64_t i); - MessageArg(double d); - MessageArg(const Time& t); - ~MessageArg(); - - private: - friend class base::i18n::MessageFormatter; - MessageArg(); - // Tests if this argument has a value, and if so increments *count. - bool has_value(int* count) const; - std::unique_ptr formattable; - DISALLOW_COPY_AND_ASSIGN(MessageArg); -}; - -} // namespace internal - -// Message Formatter with the ICU message format syntax support. -// It can format strings (UTF-8 and UTF-16), numbers and base::Time with -// plural, gender and other 'selectors' support. This is handy if you -// have multiple parameters of differnt types and some of them require -// plural or gender/selector support. -// -// To use this API for locale-sensitive formatting, retrieve a 'message -// template' in the ICU message format from a message bundle (e.g. with -// l10n_util::GetStringUTF16()) and pass it to FormatWith{Named,Numbered}Args. -// -// MessageFormat specs: -// http://icu-project.org/apiref/icu4j/com/ibm/icu/text/MessageFormat.html -// http://icu-project.org/apiref/icu4c/classicu_1_1DecimalFormat.html#details -// Examples: -// http://userguide.icu-project.org/formatparse/messages -// message_formatter_unittest.cc -// go/plurals inside Google. -// TODO(jshin): Document this API in md format docs. -// Caveat: -// When plural/select/gender is used along with other format specifiers such -// as date or number, plural/select/gender should be at the top level. It's -// not an ICU restriction but a constraint imposed by Google's translation -// infrastructure. Message A does not work. It must be revised to Message B. -// -// A. -// Rated {0, number,0.0}3.2 -// by {1, plural, =1{a user} other{# users}} -// -// B. -// {1, plural, -// =1{Rated {0, number,0.0}3.2 -// by a user.} -// other{Rated {0, number,0.0}3.2 -// by # users.}} - -class BASE_I18N_EXPORT MessageFormatter { - public: - static string16 FormatWithNamedArgs( - StringPiece16 msg, - StringPiece name0 = StringPiece(), - const internal::MessageArg& arg0 = internal::MessageArg(), - StringPiece name1 = StringPiece(), - const internal::MessageArg& arg1 = internal::MessageArg(), - StringPiece name2 = StringPiece(), - const internal::MessageArg& arg2 = internal::MessageArg(), - StringPiece name3 = StringPiece(), - const internal::MessageArg& arg3 = internal::MessageArg(), - StringPiece name4 = StringPiece(), - const internal::MessageArg& arg4 = internal::MessageArg(), - StringPiece name5 = StringPiece(), - const internal::MessageArg& arg5 = internal::MessageArg(), - StringPiece name6 = StringPiece(), - const internal::MessageArg& arg6 = internal::MessageArg()); - - static string16 FormatWithNumberedArgs( - StringPiece16 msg, - const internal::MessageArg& arg0 = internal::MessageArg(), - const internal::MessageArg& arg1 = internal::MessageArg(), - const internal::MessageArg& arg2 = internal::MessageArg(), - const internal::MessageArg& arg3 = internal::MessageArg(), - const internal::MessageArg& arg4 = internal::MessageArg(), - const internal::MessageArg& arg5 = internal::MessageArg(), - const internal::MessageArg& arg6 = internal::MessageArg()); - - private: - MessageFormatter() = delete; - DISALLOW_COPY_AND_ASSIGN(MessageFormatter); -}; - -} // namespace i18n -} // namespace base - -#endif // BASE_I18N_MESSAGE_FORMATTER_H_ diff --git a/i18n/message_formatter_unittest.cc b/i18n/message_formatter_unittest.cc deleted file mode 100644 index a6f461370..000000000 --- a/i18n/message_formatter_unittest.cc +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/i18n/message_formatter.h" - -#include - -#include "base/i18n/rtl.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/icu/source/common/unicode/unistr.h" -#include "third_party/icu/source/i18n/unicode/datefmt.h" -#include "third_party/icu/source/i18n/unicode/msgfmt.h" - -typedef testing::Test MessageFormatterTest; - -namespace base { -namespace i18n { - -class MessageFormatterTest : public testing::Test { - protected: - MessageFormatterTest() { - original_locale_ = GetConfiguredLocale(); - SetICUDefaultLocale("en-US"); - } - ~MessageFormatterTest() override { - SetICUDefaultLocale(original_locale_); - } - - private: - std::string original_locale_; -}; - -namespace { - -void AppendFormattedDateTime(const std::unique_ptr& df, - const Time& now, - std::string* result) { - icu::UnicodeString formatted; - df->format(static_cast(now.ToJsTime()), formatted). - toUTF8String(*result); -} - -} // namespace - -TEST_F(MessageFormatterTest, PluralNamedArgs) { - const string16 pattern = ASCIIToUTF16( - "{num_people, plural, " - "=0 {I met nobody in {place}.}" - "=1 {I met a person in {place}.}" - "other {I met # people in {place}.}}"); - - std::string result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs( - pattern, "num_people", 0, "place", "Paris")); - EXPECT_EQ("I met nobody in Paris.", result); - result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs( - pattern, "num_people", 1, "place", "Paris")); - EXPECT_EQ("I met a person in Paris.", result); - result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs( - pattern, "num_people", 5, "place", "Paris")); - EXPECT_EQ("I met 5 people in Paris.", result); -} - -TEST_F(MessageFormatterTest, PluralNamedArgsWithOffset) { - const string16 pattern = ASCIIToUTF16( - "{num_people, plural, offset:1 " - "=0 {I met nobody in {place}.}" - "=1 {I met {person} in {place}.}" - "=2 {I met {person} and one other person in {place}.}" - "=13 {I met {person} and a dozen other people in {place}.}" - "other {I met {person} and # other people in {place}.}}"); - - std::string result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs( - pattern, "num_people", 0, "place", "Paris")); - EXPECT_EQ("I met nobody in Paris.", result); - // {person} is ignored if {num_people} is 0. - result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs( - pattern, "num_people", 0, "place", "Paris", "person", "Peter")); - EXPECT_EQ("I met nobody in Paris.", result); - result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs( - pattern, "num_people", 1, "place", "Paris", "person", "Peter")); - EXPECT_EQ("I met Peter in Paris.", result); - result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs( - pattern, "num_people", 2, "place", "Paris", "person", "Peter")); - EXPECT_EQ("I met Peter and one other person in Paris.", result); - result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs( - pattern, "num_people", 13, "place", "Paris", "person", "Peter")); - EXPECT_EQ("I met Peter and a dozen other people in Paris.", result); - result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs( - pattern, "num_people", 50, "place", "Paris", "person", "Peter")); - EXPECT_EQ("I met Peter and 49 other people in Paris.", result); -} - -TEST_F(MessageFormatterTest, PluralNumberedArgs) { - const string16 pattern = ASCIIToUTF16( - "{1, plural, " - "=1 {The cert for {0} expired yesterday.}" - "=7 {The cert for {0} expired a week ago.}" - "other {The cert for {0} expired # days ago.}}"); - - std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs( - pattern, "example.com", 1)); - EXPECT_EQ("The cert for example.com expired yesterday.", result); - result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs( - pattern, "example.com", 7)); - EXPECT_EQ("The cert for example.com expired a week ago.", result); - result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs( - pattern, "example.com", 15)); - EXPECT_EQ("The cert for example.com expired 15 days ago.", result); -} - -TEST_F(MessageFormatterTest, PluralNumberedArgsWithDate) { - const string16 pattern = ASCIIToUTF16( - "{1, plural, " - "=1 {The cert for {0} expired yesterday. Today is {2,date,full}}" - "other {The cert for {0} expired # days ago. Today is {2,date,full}}}"); - - base::Time now = base::Time::Now(); - using icu::DateFormat; - std::unique_ptr df( - DateFormat::createDateInstance(DateFormat::FULL)); - std::string second_sentence = " Today is "; - AppendFormattedDateTime(df, now, &second_sentence); - - std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs( - pattern, "example.com", 1, now)); - EXPECT_EQ("The cert for example.com expired yesterday." + second_sentence, - result); - result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs( - pattern, "example.com", 15, now)); - EXPECT_EQ("The cert for example.com expired 15 days ago." + second_sentence, - result); -} - -TEST_F(MessageFormatterTest, DateTimeAndNumber) { - // Note that using 'mph' for all locales is not a good i18n practice. - const string16 pattern = ASCIIToUTF16( - "At {0,time, short} on {0,date, medium}, " - "there was {1} at building {2,number,integer}. " - "The speed of the wind was {3,number,###.#} mph."); - - using icu::DateFormat; - std::unique_ptr tf( - DateFormat::createTimeInstance(DateFormat::SHORT)); - std::unique_ptr df( - DateFormat::createDateInstance(DateFormat::MEDIUM)); - - base::Time now = base::Time::Now(); - std::string expected = "At "; - AppendFormattedDateTime(tf, now, &expected); - expected.append(" on "); - AppendFormattedDateTime(df, now, &expected); - expected.append(", there was an explosion at building 3. " - "The speed of the wind was 37.4 mph."); - - EXPECT_EQ(expected, UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs( - pattern, now, "an explosion", 3, 37.413))); -} - -TEST_F(MessageFormatterTest, SelectorSingleOrMultiple) { - const string16 pattern = ASCIIToUTF16( - "{0, select," - "single {Select a file to upload.}" - "multiple {Select files to upload.}" - "other {UNUSED}}"); - - std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs( - pattern, "single")); - EXPECT_EQ("Select a file to upload.", result); - result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs( - pattern, "multiple")); - EXPECT_EQ("Select files to upload.", result); - - // fallback if a parameter is not selectors specified in the message pattern. - result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs( - pattern, "foobar")); - EXPECT_EQ("UNUSED", result); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/number_formatting.cc b/i18n/number_formatting.cc deleted file mode 100644 index 0ab031eca..000000000 --- a/i18n/number_formatting.cc +++ /dev/null @@ -1,97 +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/i18n/number_formatting.h" - -#include - -#include - -#include "base/format_macros.h" -#include "base/i18n/message_formatter.h" -#include "base/i18n/unicodestring.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "third_party/icu/source/common/unicode/ustring.h" -#include "third_party/icu/source/i18n/unicode/numfmt.h" - -namespace base { - -namespace { - -// A simple wrapper around icu::NumberFormat that allows for resetting it -// (as LazyInstance does not). -struct NumberFormatWrapper { - NumberFormatWrapper() { - Reset(); - } - - void Reset() { - // There's no ICU call to destroy a NumberFormat object other than - // operator delete, so use the default Delete, which calls operator delete. - // This can cause problems if a different allocator is used by this file - // than by ICU. - UErrorCode status = U_ZERO_ERROR; - number_format.reset(icu::NumberFormat::createInstance(status)); - DCHECK(U_SUCCESS(status)); - } - - std::unique_ptr number_format; -}; - -LazyInstance::DestructorAtExit g_number_format_int = - LAZY_INSTANCE_INITIALIZER; -LazyInstance::DestructorAtExit g_number_format_float = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -string16 FormatNumber(int64_t number) { - icu::NumberFormat* number_format = - g_number_format_int.Get().number_format.get(); - - if (!number_format) { - // As a fallback, just return the raw number in a string. - return ASCIIToUTF16(StringPrintf("%" PRId64, number)); - } - icu::UnicodeString ustr; - number_format->format(number, ustr); - - return i18n::UnicodeStringToString16(ustr); -} - -string16 FormatDouble(double number, int fractional_digits) { - icu::NumberFormat* number_format = - g_number_format_float.Get().number_format.get(); - - if (!number_format) { - // As a fallback, just return the raw number in a string. - return ASCIIToUTF16(StringPrintf("%f", number)); - } - number_format->setMaximumFractionDigits(fractional_digits); - number_format->setMinimumFractionDigits(fractional_digits); - icu::UnicodeString ustr; - number_format->format(number, ustr); - - return i18n::UnicodeStringToString16(ustr); -} - -string16 FormatPercent(int number) { - return i18n::MessageFormatter::FormatWithNumberedArgs( - ASCIIToUTF16("{0,number,percent}"), static_cast(number) / 100.0); -} - -namespace testing { - -void ResetFormatters() { - g_number_format_int.Get().Reset(); - g_number_format_float.Get().Reset(); -} - -} // namespace testing - -} // namespace base diff --git a/i18n/number_formatting.h b/i18n/number_formatting.h deleted file mode 100644 index 9636bf4d1..000000000 --- a/i18n/number_formatting.h +++ /dev/null @@ -1,38 +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_I18N_NUMBER_FORMATTING_H_ -#define BASE_I18N_NUMBER_FORMATTING_H_ - -#include - -#include "base/i18n/base_i18n_export.h" -#include "base/strings/string16.h" - -namespace base { - -// Return a number formatted with separators in the user's locale. -// Ex: FormatNumber(1234567) => "1,234,567" in English, "1.234.567" in German -BASE_I18N_EXPORT string16 FormatNumber(int64_t number); - -// Return a number formatted with separators in the user's locale. -// Ex: FormatDouble(1234567.8, 1) -// => "1,234,567.8" in English, "1.234.567,8" in German -BASE_I18N_EXPORT string16 FormatDouble(double number, int fractional_digits); - -// Return a percentage formatted with space and symbol in the user's locale. -// Ex: FormatPercent(12) => "12%" in English, "12 %" in Romanian -BASE_I18N_EXPORT string16 FormatPercent(int number); - -namespace testing { - -// Causes cached formatters to be discarded and recreated. Only useful for -// testing. -BASE_I18N_EXPORT void ResetFormatters(); - -} // namespace testing - -} // namespace base - -#endif // BASE_I18N_NUMBER_FORMATTING_H_ diff --git a/i18n/number_formatting_unittest.cc b/i18n/number_formatting_unittest.cc deleted file mode 100644 index d2eb56879..000000000 --- a/i18n/number_formatting_unittest.cc +++ /dev/null @@ -1,142 +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 -#include - -#include - -#include "base/i18n/number_formatting.h" -#include "base/i18n/rtl.h" -#include "base/macros.h" -#include "base/strings/utf_string_conversions.h" -#include "base/test/icu_test_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/icu/source/i18n/unicode/usearch.h" - -namespace base { -namespace { - -TEST(NumberFormattingTest, FormatNumber) { - static const struct { - int64_t number; - const char* expected_english; - const char* expected_german; - } cases[] = { - {0, "0", "0"}, - {1024, "1,024", "1.024"}, - {std::numeric_limits::max(), - "9,223,372,036,854,775,807", "9.223.372.036.854.775.807"}, - {std::numeric_limits::min(), - "-9,223,372,036,854,775,808", "-9.223.372.036.854.775.808"}, - {-42, "-42", "-42"}, - }; - - test::ScopedRestoreICUDefaultLocale restore_locale; - - for (size_t i = 0; i < arraysize(cases); ++i) { - i18n::SetICUDefaultLocale("en"); - testing::ResetFormatters(); - EXPECT_EQ(cases[i].expected_english, - UTF16ToUTF8(FormatNumber(cases[i].number))); - i18n::SetICUDefaultLocale("de"); - testing::ResetFormatters(); - EXPECT_EQ(cases[i].expected_german, - UTF16ToUTF8(FormatNumber(cases[i].number))); - } -} - -TEST(NumberFormattingTest, FormatDouble) { - static const struct { - double number; - int frac_digits; - const char* expected_english; - const char* expected_german; - } cases[] = { - {0.0, 0, "0", "0"}, -#if !defined(OS_ANDROID) - // Bionic can't printf negative zero correctly. - {-0.0, 4, "-0.0000", "-0,0000"}, -#endif - {1024.2, 0, "1,024", "1.024"}, - {-1024.223, 2, "-1,024.22", "-1.024,22"}, - {std::numeric_limits::max(), 6, - "179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,000,000," - "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000," - "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000," - "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000," - "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000," - "000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000," - "000.000000", - "179.769.313.486.231.570.000.000.000.000.000.000.000.000.000.000.000." - "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000." - "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000." - "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000." - "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000." - "000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000." - "000,000000"}, - {std::numeric_limits::min(), 2, "0.00", "0,00"}, - {-42.7, 3, "-42.700", "-42,700"}, - }; - - test::ScopedRestoreICUDefaultLocale restore_locale; - for (size_t i = 0; i < arraysize(cases); ++i) { - i18n::SetICUDefaultLocale("en"); - testing::ResetFormatters(); - EXPECT_EQ(cases[i].expected_english, - UTF16ToUTF8(FormatDouble(cases[i].number, cases[i].frac_digits))); - i18n::SetICUDefaultLocale("de"); - testing::ResetFormatters(); - EXPECT_EQ(cases[i].expected_german, - UTF16ToUTF8(FormatDouble(cases[i].number, cases[i].frac_digits))); - } -} - -TEST(NumberFormattingTest, FormatPercent) { - static const struct { - int64_t number; - const char* expected_english; - const char* expected_german; // Note: Space before % isn't \x20. - // Note: Eastern Arabic-Indic digits (U+06Fx) for Persian and - // Arabic-Indic digits (U+066x) for Arabic in Egypt(ar-EG). In Arabic (ar), - // uses European digits (Google-patch). - // See https://unicode.org/cldr/trac/ticket/9040 for details. - // See also https://unicode.org/cldr/trac/ticket/10176 . - // For now, take what CLDR 32 has (percent sign to the right of - // a number in Persian). - const char* expected_persian; - const char* expected_arabic; - const char* expected_arabic_egypt; - } cases[] = { - {0, "0%", u8"0\u00a0%", u8"\u06f0\u066a", u8"0\u200e%\u200e", - u8"\u0660\u066a\u061c"}, - {42, "42%", "42\u00a0%", u8"\u06f4\u06f2\u066a", u8"42\u200e%\u200e", - "\u0664\u0662\u066a\u061c"}, - {1024, "1,024%", "1.024\u00a0%", u8"\u06f1\u066c\u06f0\u06f2\u06f4\u066a", - "1,024\u200e%\u200e", "\u0661\u066c\u0660\u0662\u0664\u066a\u061c"}, - }; - - test::ScopedRestoreICUDefaultLocale restore_locale; - for (size_t i = 0; i < arraysize(cases); ++i) { - i18n::SetICUDefaultLocale("en"); - EXPECT_EQ(ASCIIToUTF16(cases[i].expected_english), - FormatPercent(cases[i].number)); - i18n::SetICUDefaultLocale("de"); - EXPECT_EQ(UTF8ToUTF16(cases[i].expected_german), - FormatPercent(cases[i].number)); - i18n::SetICUDefaultLocale("fa"); - EXPECT_EQ(UTF8ToUTF16(cases[i].expected_persian), - FormatPercent(cases[i].number)); - i18n::SetICUDefaultLocale("ar"); - EXPECT_EQ(UTF8ToUTF16(cases[i].expected_arabic), - FormatPercent(cases[i].number)); - i18n::SetICUDefaultLocale("ar-EG"); - EXPECT_EQ(UTF8ToUTF16(cases[i].expected_arabic_egypt), - FormatPercent(cases[i].number)); - } -} - -} // namespace -} // namespace base diff --git a/i18n/rtl.cc b/i18n/rtl.cc deleted file mode 100644 index bba0d449c..000000000 --- a/i18n/rtl.cc +++ /dev/null @@ -1,491 +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/i18n/rtl.h" - -#include -#include - -#include - -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/i18n/base_i18n_switches.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" -#include "third_party/icu/source/common/unicode/locid.h" -#include "third_party/icu/source/common/unicode/uchar.h" -#include "third_party/icu/source/common/unicode/uscript.h" -#include "third_party/icu/source/i18n/unicode/coll.h" - -#if defined(OS_IOS) -#include "base/debug/crash_logging.h" -#include "base/ios/ios_util.h" -#endif - -namespace { - -// Extract language, country and variant, but ignore keywords. For example, -// en-US, ca@valencia, ca-ES@valencia. -std::string GetLocaleString(const icu::Locale& locale) { - const char* language = locale.getLanguage(); - const char* country = locale.getCountry(); - const char* variant = locale.getVariant(); - - std::string result = - (language != nullptr && *language != '\0') ? language : "und"; - - if (country != nullptr && *country != '\0') { - result += '-'; - result += country; - } - - if (variant != nullptr && *variant != '\0') - result += '@' + base::ToLowerASCII(variant); - - return result; -} - -// Returns LEFT_TO_RIGHT or RIGHT_TO_LEFT if |character| has strong -// directionality, returns UNKNOWN_DIRECTION if it doesn't. Please refer to -// http://unicode.org/reports/tr9/ for more information. -base::i18n::TextDirection GetCharacterDirection(UChar32 character) { - static bool has_switch = base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kForceTextDirection); - if (has_switch) { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - std::string force_flag = - command_line->GetSwitchValueASCII(switches::kForceTextDirection); - - if (force_flag == switches::kForceDirectionRTL) - return base::i18n::RIGHT_TO_LEFT; - if (force_flag == switches::kForceDirectionLTR) - return base::i18n::LEFT_TO_RIGHT; - } - // Now that we have the character, we use ICU in order to query for the - // appropriate Unicode BiDi character type. - int32_t property = u_getIntPropertyValue(character, UCHAR_BIDI_CLASS); - if ((property == U_RIGHT_TO_LEFT) || - (property == U_RIGHT_TO_LEFT_ARABIC) || - (property == U_RIGHT_TO_LEFT_EMBEDDING) || - (property == U_RIGHT_TO_LEFT_OVERRIDE)) { - return base::i18n::RIGHT_TO_LEFT; - } else if ((property == U_LEFT_TO_RIGHT) || - (property == U_LEFT_TO_RIGHT_EMBEDDING) || - (property == U_LEFT_TO_RIGHT_OVERRIDE)) { - return base::i18n::LEFT_TO_RIGHT; - } - return base::i18n::UNKNOWN_DIRECTION; -} - -} // namespace - -namespace base { -namespace i18n { - -// Represents the locale-specific ICU text direction. -static TextDirection g_icu_text_direction = UNKNOWN_DIRECTION; - -// Convert the ICU default locale to a string. -std::string GetConfiguredLocale() { - return GetLocaleString(icu::Locale::getDefault()); -} - -// Convert the ICU canonicalized locale to a string. -std::string GetCanonicalLocale(const std::string& locale) { - return GetLocaleString(icu::Locale::createCanonical(locale.c_str())); -} - -// Convert Chrome locale name to ICU locale name -std::string ICULocaleName(const std::string& locale_string) { - // If not Spanish, just return it. - if (locale_string.substr(0, 2) != "es") - return locale_string; - // Expand es to es-ES. - if (LowerCaseEqualsASCII(locale_string, "es")) - return "es-ES"; - // Map es-419 (Latin American Spanish) to es-FOO depending on the system - // locale. If it's es-RR other than es-ES, map to es-RR. Otherwise, map - // to es-MX (the most populous in Spanish-speaking Latin America). - if (LowerCaseEqualsASCII(locale_string, "es-419")) { - const icu::Locale& locale = icu::Locale::getDefault(); - std::string language = locale.getLanguage(); - const char* country = locale.getCountry(); - if (LowerCaseEqualsASCII(language, "es") && - !LowerCaseEqualsASCII(country, "es")) { - language += '-'; - language += country; - return language; - } - return "es-MX"; - } - // Currently, Chrome has only "es" and "es-419", but later we may have - // more specific "es-RR". - return locale_string; -} - -void SetICUDefaultLocale(const std::string& locale_string) { -#if defined(OS_IOS) - static base::debug::CrashKeyString* crash_key_locale = - base::debug::AllocateCrashKeyString("icu_locale_input", - base::debug::CrashKeySize::Size256); - base::debug::SetCrashKeyString(crash_key_locale, locale_string); -#endif - icu::Locale locale(ICULocaleName(locale_string).c_str()); - UErrorCode error_code = U_ZERO_ERROR; - const char* lang = locale.getLanguage(); - if (lang != nullptr && *lang != '\0') { - icu::Locale::setDefault(locale, error_code); - } else { - LOG(ERROR) << "Failed to set the ICU default locale to " << locale_string - << ". Falling back to en-US."; - icu::Locale::setDefault(icu::Locale::getUS(), error_code); - } - g_icu_text_direction = UNKNOWN_DIRECTION; -} - -bool IsRTL() { - return ICUIsRTL(); -} - -bool ICUIsRTL() { - if (g_icu_text_direction == UNKNOWN_DIRECTION) { - const icu::Locale& locale = icu::Locale::getDefault(); - g_icu_text_direction = GetTextDirectionForLocaleInStartUp(locale.getName()); - } - return g_icu_text_direction == RIGHT_TO_LEFT; -} - -TextDirection GetForcedTextDirection() { -// On iOS, check for RTL forcing. -#if defined(OS_IOS) - if (base::ios::IsInForcedRTL()) - return base::i18n::RIGHT_TO_LEFT; -#endif - - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(switches::kForceUIDirection)) { - std::string force_flag = - command_line->GetSwitchValueASCII(switches::kForceUIDirection); - - if (force_flag == switches::kForceDirectionLTR) - return base::i18n::LEFT_TO_RIGHT; - - if (force_flag == switches::kForceDirectionRTL) - return base::i18n::RIGHT_TO_LEFT; - } - - return base::i18n::UNKNOWN_DIRECTION; -} - -TextDirection GetTextDirectionForLocaleInStartUp(const char* locale_name) { - // Check for direction forcing. - TextDirection forced_direction = GetForcedTextDirection(); - if (forced_direction != UNKNOWN_DIRECTION) - return forced_direction; - - // This list needs to be updated in alphabetical order if we add more RTL - // locales. - static const char kRTLLanguageCodes[][3] = {"ar", "fa", "he", "iw", "ur"}; - std::vector locale_split = - SplitStringPiece(locale_name, "-_", KEEP_WHITESPACE, SPLIT_WANT_ALL); - const StringPiece& language_code = locale_split[0]; - if (std::binary_search(kRTLLanguageCodes, - kRTLLanguageCodes + arraysize(kRTLLanguageCodes), - language_code)) - return RIGHT_TO_LEFT; - return LEFT_TO_RIGHT; -} - -TextDirection GetTextDirectionForLocale(const char* locale_name) { - // Check for direction forcing. - TextDirection forced_direction = GetForcedTextDirection(); - if (forced_direction != UNKNOWN_DIRECTION) - return forced_direction; - - UErrorCode status = U_ZERO_ERROR; - ULayoutType layout_dir = uloc_getCharacterOrientation(locale_name, &status); - DCHECK(U_SUCCESS(status)); - // Treat anything other than RTL as LTR. - return (layout_dir != ULOC_LAYOUT_RTL) ? LEFT_TO_RIGHT : RIGHT_TO_LEFT; -} - -TextDirection GetFirstStrongCharacterDirection(const string16& text) { - const UChar* string = text.c_str(); - size_t length = text.length(); - size_t position = 0; - while (position < length) { - UChar32 character; - size_t next_position = position; - U16_NEXT(string, next_position, length, character); - TextDirection direction = GetCharacterDirection(character); - if (direction != UNKNOWN_DIRECTION) - return direction; - position = next_position; - } - 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(); - size_t position = 0; - - TextDirection result(UNKNOWN_DIRECTION); - while (position < length) { - UChar32 character; - size_t next_position = position; - U16_NEXT(string, next_position, length, character); - TextDirection direction = GetCharacterDirection(character); - if (direction != UNKNOWN_DIRECTION) { - if (result != UNKNOWN_DIRECTION && result != direction) - return UNKNOWN_DIRECTION; - result = direction; - } - position = next_position; - } - - // Handle the case of a string not containing any strong directionality - // characters defaulting to LEFT_TO_RIGHT. - if (result == UNKNOWN_DIRECTION) - return LEFT_TO_RIGHT; - - return result; -} - -#if defined(OS_WIN) -bool AdjustStringForLocaleDirection(string16* text) { - if (!IsRTL() || text->empty()) - return false; - - // Marking the string as LTR if the locale is RTL and the string does not - // contain strong RTL characters. Otherwise, mark the string as RTL. - bool has_rtl_chars = StringContainsStrongRTLChars(*text); - if (!has_rtl_chars) - WrapStringWithLTRFormatting(text); - else - WrapStringWithRTLFormatting(text); - - return true; -} - -bool UnadjustStringForLocaleDirection(string16* text) { - if (!IsRTL() || text->empty()) - return false; - - *text = StripWrappingBidiControlCharacters(*text); - return true; -} -#else -bool AdjustStringForLocaleDirection(string16* text) { - // On OS X & GTK the directionality of a label is determined by the first - // strongly directional character. - // However, we want to make sure that in an LTR-language-UI all strings are - // left aligned and vice versa. - // A problem can arise if we display a string which starts with user input. - // User input may be of the opposite directionality to the UI. So the whole - // string will be displayed in the opposite directionality, e.g. if we want to - // display in an LTR UI [such as US English]: - // - // EMAN_NOISNETXE is now installed. - // - // Since EXTENSION_NAME begins with a strong RTL char, the label's - // directionality will be set to RTL and the string will be displayed visually - // as: - // - // .is now installed EMAN_NOISNETXE - // - // In order to solve this issue, we prepend an LRM to the string. An LRM is a - // strongly directional LTR char. - // We also append an LRM at the end, which ensures that we're in an LTR - // context. - - // Unlike Windows, Linux and OS X can correctly display RTL glyphs out of the - // box so there is no issue with displaying zero-width bidi control characters - // on any system. Thus no need for the !IsRTL() check here. - if (text->empty()) - return false; - - bool ui_direction_is_rtl = IsRTL(); - - bool has_rtl_chars = StringContainsStrongRTLChars(*text); - if (!ui_direction_is_rtl && has_rtl_chars) { - WrapStringWithRTLFormatting(text); - text->insert(static_cast(0), static_cast(1), - kLeftToRightMark); - text->push_back(kLeftToRightMark); - } else if (ui_direction_is_rtl && has_rtl_chars) { - WrapStringWithRTLFormatting(text); - text->insert(static_cast(0), static_cast(1), - kRightToLeftMark); - text->push_back(kRightToLeftMark); - } else if (ui_direction_is_rtl) { - WrapStringWithLTRFormatting(text); - text->insert(static_cast(0), static_cast(1), - kRightToLeftMark); - text->push_back(kRightToLeftMark); - } else { - return false; - } - - return true; -} - -bool UnadjustStringForLocaleDirection(string16* text) { - if (text->empty()) - return false; - - size_t begin_index = 0; - char16 begin = text->at(begin_index); - if (begin == kLeftToRightMark || - begin == kRightToLeftMark) { - ++begin_index; - } - - size_t end_index = text->length() - 1; - char16 end = text->at(end_index); - if (end == kLeftToRightMark || - end == kRightToLeftMark) { - --end_index; - } - - string16 unmarked_text = - text->substr(begin_index, end_index - begin_index + 1); - *text = StripWrappingBidiControlCharacters(unmarked_text); - return true; -} - -#endif // !OS_WIN - -void EnsureTerminatedDirectionalFormatting(string16* text) { - int count = 0; - for (auto c : *text) { - if (c == kLeftToRightEmbeddingMark || c == kRightToLeftEmbeddingMark || - c == kLeftToRightOverride || c == kRightToLeftOverride) { - ++count; - } else if (c == kPopDirectionalFormatting && count > 0) { - --count; - } - } - for (int j = 0; j < count; j++) - text->push_back(kPopDirectionalFormatting); -} - -void SanitizeUserSuppliedString(string16* text) { - EnsureTerminatedDirectionalFormatting(text); - AdjustStringForLocaleDirection(text); -} - -bool StringContainsStrongRTLChars(const string16& text) { - const UChar* string = text.c_str(); - size_t length = text.length(); - size_t position = 0; - while (position < length) { - UChar32 character; - size_t next_position = position; - U16_NEXT(string, next_position, length, character); - - // Now that we have the character, we use ICU in order to query for the - // appropriate Unicode BiDi character type. - int32_t property = u_getIntPropertyValue(character, UCHAR_BIDI_CLASS); - if ((property == U_RIGHT_TO_LEFT) || (property == U_RIGHT_TO_LEFT_ARABIC)) - return true; - - position = next_position; - } - - return false; -} - -void WrapStringWithLTRFormatting(string16* text) { - if (text->empty()) - return; - - // Inserting an LRE (Left-To-Right Embedding) mark as the first character. - text->insert(static_cast(0), static_cast(1), - kLeftToRightEmbeddingMark); - - // Inserting a PDF (Pop Directional Formatting) mark as the last character. - text->push_back(kPopDirectionalFormatting); -} - -void WrapStringWithRTLFormatting(string16* text) { - if (text->empty()) - return; - - // Inserting an RLE (Right-To-Left Embedding) mark as the first character. - text->insert(static_cast(0), static_cast(1), - kRightToLeftEmbeddingMark); - - // Inserting a PDF (Pop Directional Formatting) mark as the last character. - text->push_back(kPopDirectionalFormatting); -} - -void WrapPathWithLTRFormatting(const FilePath& path, - string16* rtl_safe_path) { - // Wrap the overall path with LRE-PDF pair which essentialy marks the - // string as a Left-To-Right string. - // Inserting an LRE (Left-To-Right Embedding) mark as the first character. - rtl_safe_path->push_back(kLeftToRightEmbeddingMark); -#if defined(OS_MACOSX) - rtl_safe_path->append(UTF8ToUTF16(path.value())); -#elif defined(OS_WIN) - rtl_safe_path->append(path.value()); -#else // defined(OS_POSIX) && !defined(OS_MACOSX) - std::wstring wide_path = base::SysNativeMBToWide(path.value()); - rtl_safe_path->append(WideToUTF16(wide_path)); -#endif - // Inserting a PDF (Pop Directional Formatting) mark as the last character. - rtl_safe_path->push_back(kPopDirectionalFormatting); -} - -string16 GetDisplayStringInLTRDirectionality(const string16& text) { - // Always wrap the string in RTL UI (it may be appended to RTL string). - // Also wrap strings with an RTL first strong character direction in LTR UI. - if (IsRTL() || GetFirstStrongCharacterDirection(text) == RIGHT_TO_LEFT) { - string16 text_mutable(text); - WrapStringWithLTRFormatting(&text_mutable); - return text_mutable; - } - return text; -} - -string16 StripWrappingBidiControlCharacters(const string16& text) { - if (text.empty()) - return text; - size_t begin_index = 0; - char16 begin = text[begin_index]; - if (begin == kLeftToRightEmbeddingMark || - begin == kRightToLeftEmbeddingMark || - begin == kLeftToRightOverride || - begin == kRightToLeftOverride) - ++begin_index; - size_t end_index = text.length() - 1; - if (text[end_index] == kPopDirectionalFormatting) - --end_index; - return text.substr(begin_index, end_index - begin_index + 1); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/rtl.h b/i18n/rtl.h deleted file mode 100644 index 532597090..000000000 --- a/i18n/rtl.h +++ /dev/null @@ -1,168 +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_I18N_RTL_H_ -#define BASE_I18N_RTL_H_ - -#include - -#include "base/compiler_specific.h" -#include "base/i18n/base_i18n_export.h" -#include "base/strings/string16.h" -#include "build/build_config.h" - -namespace base { - -class FilePath; - -namespace i18n { - -const char16 kRightToLeftMark = 0x200F; -const char16 kLeftToRightMark = 0x200E; -const char16 kLeftToRightEmbeddingMark = 0x202A; -const char16 kRightToLeftEmbeddingMark = 0x202B; -const char16 kPopDirectionalFormatting = 0x202C; -const char16 kLeftToRightOverride = 0x202D; -const char16 kRightToLeftOverride = 0x202E; - -// Locale.java mirrored this enum TextDirection. Please keep in sync. -enum TextDirection { - UNKNOWN_DIRECTION = 0, - RIGHT_TO_LEFT = 1, - LEFT_TO_RIGHT = 2, - TEXT_DIRECTION_MAX = LEFT_TO_RIGHT, -}; - -// Get the locale that the currently running process has been configured to use. -// The return value is of the form language[-country] (e.g., en-US) where the -// language is the 2 or 3 letter code from ISO-639. -BASE_I18N_EXPORT std::string GetConfiguredLocale(); - -// Canonicalize a string (eg. a POSIX locale string) to a Chrome locale name. -BASE_I18N_EXPORT std::string GetCanonicalLocale(const std::string& locale); - -// Sets the default locale of ICU. -// Once the application locale of Chrome in GetApplicationLocale is determined, -// the default locale of ICU need to be changed to match the application locale -// so that ICU functions work correctly in a locale-dependent manner. -// This is handy in that we don't have to call GetApplicationLocale() -// everytime we call locale-dependent ICU APIs as long as we make sure -// that this is called before any locale-dependent API is called. -BASE_I18N_EXPORT void SetICUDefaultLocale(const std::string& locale_string); - -// Returns true if the application text direction is right-to-left. -BASE_I18N_EXPORT bool IsRTL(); - -// Returns whether the text direction for the default ICU locale is RTL. This -// assumes that SetICUDefaultLocale has been called to set the default locale to -// the UI locale of Chrome. -// NOTE: Generally, you should call IsRTL() instead of this. -BASE_I18N_EXPORT bool ICUIsRTL(); - -// Gets the explicitly forced text direction for debugging. If no forcing is -// applied, returns UNKNOWN_DIRECTION. -BASE_I18N_EXPORT TextDirection GetForcedTextDirection(); - -// Returns the text direction for |locale_name|. -// As a startup optimization, this method checks the locale against a list of -// Chrome-supported RTL locales. -BASE_I18N_EXPORT TextDirection -GetTextDirectionForLocaleInStartUp(const char* locale_name); - -// Returns the text direction for |locale_name|. -BASE_I18N_EXPORT TextDirection GetTextDirectionForLocale( - const char* locale_name); - -// 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 -// directionality characters. Please refer to http://unicode.org/reports/tr9/ -// 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 -// directionality. It returns UNKNOWN_DIRECTION if the string contains a mix of -// LTR and RTL strong directionality characters. Defaults to LEFT_TO_RIGHT if -// the string does not contain directionality characters. Please refer to -// http://unicode.org/reports/tr9/ for more information. -BASE_I18N_EXPORT TextDirection GetStringDirection(const string16& text); - -// Given the string in |text|, this function modifies the string in place with -// the appropriate Unicode formatting marks that mark the string direction -// (either left-to-right or right-to-left). The function checks both the current -// locale and the contents of the string in order to determine the direction of -// the returned string. The function returns true if the string in |text| was -// properly adjusted. -// -// Certain LTR strings are not rendered correctly when the context is RTL. For -// example, the string "Foo!" will appear as "!Foo" if it is rendered as is in -// an RTL context. Calling this function will make sure the returned localized -// string is always treated as a right-to-left string. This is done by -// inserting certain Unicode formatting marks into the returned string. -// -// ** Notes about the Windows version of this function: -// TODO(idana) bug 6806: this function adjusts the string in question only -// if the current locale is right-to-left. The function does not take care of -// the opposite case (an RTL string displayed in an LTR context) since -// adjusting the string involves inserting Unicode formatting characters that -// Windows does not handle well unless right-to-left language support is -// installed. Since the English version of Windows doesn't have right-to-left -// language support installed by default, inserting the direction Unicode mark -// results in Windows displaying squares. -BASE_I18N_EXPORT bool AdjustStringForLocaleDirection(string16* text); - -// Undoes the actions of the above function (AdjustStringForLocaleDirection). -BASE_I18N_EXPORT bool UnadjustStringForLocaleDirection(string16* text); - -// Ensures |text| contains no unterminated directional formatting characters, by -// appending the appropriate pop-directional-formatting characters to the end of -// |text|. -BASE_I18N_EXPORT void EnsureTerminatedDirectionalFormatting(string16* text); - -// Sanitizes the |text| by terminating any directional override/embedding -// characters and then adjusting the string for locale direction. -BASE_I18N_EXPORT void SanitizeUserSuppliedString(string16* text); - -// Returns true if the string contains at least one character with strong right -// to left directionality; that is, a character with either R or AL Unicode -// BiDi character type. -BASE_I18N_EXPORT bool StringContainsStrongRTLChars(const string16& text); - -// Wraps a string with an LRE-PDF pair which essentialy marks the string as a -// Left-To-Right string. Doing this is useful in order to make sure LTR -// strings are rendered properly in an RTL context. -BASE_I18N_EXPORT void WrapStringWithLTRFormatting(string16* text); - -// Wraps a string with an RLE-PDF pair which essentialy marks the string as a -// Right-To-Left string. Doing this is useful in order to make sure RTL -// strings are rendered properly in an LTR context. -BASE_I18N_EXPORT void WrapStringWithRTLFormatting(string16* text); - -// Wraps file path to get it to display correctly in RTL UI. All filepaths -// should be passed through this function before display in UI for RTL locales. -BASE_I18N_EXPORT void WrapPathWithLTRFormatting(const FilePath& path, - string16* rtl_safe_path); - -// Return the string in |text| wrapped with LRE (Left-To-Right Embedding) and -// PDF (Pop Directional Formatting) marks, if needed for UI display purposes. -BASE_I18N_EXPORT string16 GetDisplayStringInLTRDirectionality( - const string16& text) WARN_UNUSED_RESULT; - -// Strip the beginning (U+202A..U+202B, U+202D..U+202E) and/or ending (U+202C) -// explicit bidi control characters from |text|, if there are any. Otherwise, -// return the text itself. Explicit bidi control characters display and have -// semantic effect. They can be deleted so they might not always appear in a -// pair. -BASE_I18N_EXPORT string16 StripWrappingBidiControlCharacters( - const string16& text) WARN_UNUSED_RESULT; - -} // namespace i18n -} // namespace base - -#endif // BASE_I18N_RTL_H_ diff --git a/i18n/rtl_unittest.cc b/i18n/rtl_unittest.cc deleted file mode 100644 index 313d2b440..000000000 --- a/i18n/rtl_unittest.cc +++ /dev/null @@ -1,567 +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/i18n/rtl.h" - -#include - -#include - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/strings/string_util.h" -#include "base/strings/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "base/test/icu_test_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" -#include "third_party/icu/source/common/unicode/locid.h" -#include "third_party/icu/source/i18n/unicode/usearch.h" - -namespace base { -namespace i18n { - -namespace { - -// A test utility function to set the application default text direction. -void SetRTL(bool rtl) { - // Override the current locale/direction. - SetICUDefaultLocale(rtl ? "he" : "en"); - EXPECT_EQ(rtl, IsRTL()); -} - -} // namespace - -class RTLTest : public PlatformTest { -}; - -TEST_F(RTLTest, GetFirstStrongCharacterDirection) { - 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 first character with strong directionality - // is a character with type L. - { L"foo \x05d0 bar", LEFT_TO_RIGHT }, - // Test bidi string in which the first character with strong directionality - // is a character with type R. - { L"\x05d0 foo bar", RIGHT_TO_LEFT }, - // Test bidi string which starts with a character with weak directionality - // and in which the first character with strong directionality is a - // character with type L. - { L"!foo \x05d0 bar", LEFT_TO_RIGHT }, - // Test bidi string which starts with a character with weak directionality - // and in which the first character with strong directionality is a - // character with type R. - { L",\x05d0 foo bar", RIGHT_TO_LEFT }, - // Test bidi string in which the first character with strong directionality - // is a character with type LRE. - { L"\x202a \x05d0 foo bar", LEFT_TO_RIGHT }, - // Test bidi string in which the first character with strong directionality - // is a character with type LRO. - { L"\x202d \x05d0 foo bar", LEFT_TO_RIGHT }, - // Test bidi string in which the first character with strong directionality - // is a character with type RLE. - { L"\x202b foo \x05d0 bar", RIGHT_TO_LEFT }, - // Test bidi string in which the first character with strong directionality - // is a character with type RLO. - { L"\x202e foo \x05d0 bar", RIGHT_TO_LEFT }, - // Test bidi string in which the first character with strong directionality - // is a character with type AL. - { L"\x0622 foo \x05d0 bar", 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" ! \x10910" L"abc 123", -#elif defined(WCHAR_T_IS_UTF16) - L" ! \xd802\xdd10" L"abc 123", -#else -#error wchar_t should be either UTF-16 or UTF-32 -#endif - RIGHT_TO_LEFT }, - { -#if defined(WCHAR_T_IS_UTF32) - L" ! \x10401" L"abc 123", -#elif defined(WCHAR_T_IS_UTF16) - L" ! \xd801\xdc01" L"abc 123", -#else -#error wchar_t should be either UTF-16 or UTF-32 -#endif - LEFT_TO_RIGHT }, - }; - - for (size_t i = 0; i < arraysize(cases); ++i) - EXPECT_EQ(cases[i].direction, - 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(cases); ++i) - EXPECT_EQ(cases[i].direction, - GetLastStrongCharacterDirection(WideToUTF16(cases[i].text))); -} - -TEST_F(RTLTest, GetStringDirection) { - struct { - const wchar_t* text; - TextDirection direction; - } cases[] = { - // Test pure LTR string. - { L"foobar", LEFT_TO_RIGHT }, - { L".foobar", LEFT_TO_RIGHT }, - { L"foo, bar", LEFT_TO_RIGHT }, - // Test pure LTR with strong directionality characters of type LRE. - { L"\x202a\x202a", LEFT_TO_RIGHT }, - { L".\x202a\x202a", LEFT_TO_RIGHT }, - { L"\x202a, \x202a", LEFT_TO_RIGHT }, - // Test pure LTR with strong directionality characters of type LRO. - { L"\x202d\x202d", LEFT_TO_RIGHT }, - { L".\x202d\x202d", LEFT_TO_RIGHT }, - { L"\x202d, \x202d", LEFT_TO_RIGHT }, - // Test pure LTR with various types of strong directionality characters. - { L"foo \x202a\x202d", LEFT_TO_RIGHT }, - { L".\x202d foo \x202a", LEFT_TO_RIGHT }, - { L"\x202a, \x202d foo", LEFT_TO_RIGHT }, - // Test pure RTL with strong directionality characters of type R. - { L"\x05d0\x05d0", RIGHT_TO_LEFT }, - { L".\x05d0\x05d0", RIGHT_TO_LEFT }, - { L"\x05d0, \x05d0", RIGHT_TO_LEFT }, - // Test pure RTL with strong directionality characters of type RLE. - { L"\x202b\x202b", RIGHT_TO_LEFT }, - { L".\x202b\x202b", RIGHT_TO_LEFT }, - { L"\x202b, \x202b", RIGHT_TO_LEFT }, - // Test pure RTL with strong directionality characters of type RLO. - { L"\x202e\x202e", RIGHT_TO_LEFT }, - { L".\x202e\x202e", RIGHT_TO_LEFT }, - { L"\x202e, \x202e", RIGHT_TO_LEFT }, - // Test pure RTL with strong directionality characters of type AL. - { L"\x0622\x0622", RIGHT_TO_LEFT }, - { L".\x0622\x0622", RIGHT_TO_LEFT }, - { L"\x0622, \x0622", RIGHT_TO_LEFT }, - // Test pure RTL with various types of strong directionality characters. - { L"\x05d0\x202b\x202e\x0622", RIGHT_TO_LEFT }, - { L".\x202b\x202e\x0622\x05d0", RIGHT_TO_LEFT }, - { L"\x0622\x202e, \x202b\x05d0", RIGHT_TO_LEFT }, - // Test bidi strings. - { L"foo \x05d0 bar", UNKNOWN_DIRECTION }, - { L"\x202b foo bar", UNKNOWN_DIRECTION }, - { L"!foo \x0622 bar", UNKNOWN_DIRECTION }, - { L"\x202a\x202b", UNKNOWN_DIRECTION }, - { L"\x202e\x202d", UNKNOWN_DIRECTION }, - { L"\x0622\x202a", UNKNOWN_DIRECTION }, - { L"\x202d\x05d0", UNKNOWN_DIRECTION }, - // Test a string without strong directionality characters. - { L",!.{}", LEFT_TO_RIGHT }, - // Test empty string. - { L"", LEFT_TO_RIGHT }, - { -#if defined(WCHAR_T_IS_UTF32) - L" ! \x10910" L"abc 123", -#elif defined(WCHAR_T_IS_UTF16) - L" ! \xd802\xdd10" L"abc 123", -#else -#error wchar_t should be either UTF-16 or UTF-32 -#endif - UNKNOWN_DIRECTION }, - { -#if defined(WCHAR_T_IS_UTF32) - L" ! \x10401" L"abc 123", -#elif defined(WCHAR_T_IS_UTF16) - L" ! \xd801\xdc01" L"abc 123", -#else -#error wchar_t should be either UTF-16 or UTF-32 -#endif - LEFT_TO_RIGHT }, - }; - - for (size_t i = 0; i < arraysize(cases); ++i) - EXPECT_EQ(cases[i].direction, - GetStringDirection(WideToUTF16(cases[i].text))); -} - -TEST_F(RTLTest, WrapPathWithLTRFormatting) { - const wchar_t* cases[] = { - // Test common path, such as "c:\foo\bar". - L"c:/foo/bar", - // Test path with file name, such as "c:\foo\bar\test.jpg". - L"c:/foo/bar/test.jpg", - // Test path ending with punctuation, such as "c:\(foo)\bar.". - L"c:/(foo)/bar.", - // Test path ending with separator, such as "c:\foo\bar\". - L"c:/foo/bar/", - // Test path with RTL character. - L"c:/\x05d0", - // Test path with 2 level RTL directory names. - L"c:/\x05d0/\x0622", - // Test path with mixed RTL/LTR directory names and ending with punctuation. - L"c:/\x05d0/\x0622/(foo)/b.a.r.", - // Test path without driver name, such as "/foo/bar/test/jpg". - L"/foo/bar/test.jpg", - // Test path start with current directory, such as "./foo". - L"./foo", - // Test path start with parent directory, such as "../foo/bar.jpg". - L"../foo/bar.jpg", - // Test absolute path, such as "//foo/bar.jpg". - L"//foo/bar.jpg", - // Test path with mixed RTL/LTR directory names. - L"c:/foo/\x05d0/\x0622/\x05d1.jpg", - // Test empty path. - L"" - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - FilePath path; -#if defined(OS_WIN) - std::wstring win_path(cases[i]); - std::replace(win_path.begin(), win_path.end(), '/', '\\'); - path = FilePath(win_path); - std::wstring wrapped_expected = - std::wstring(L"\x202a") + win_path + L"\x202c"; -#else - path = FilePath(base::SysWideToNativeMB(cases[i])); - std::wstring wrapped_expected = - std::wstring(L"\x202a") + cases[i] + L"\x202c"; -#endif - string16 localized_file_path_string; - WrapPathWithLTRFormatting(path, &localized_file_path_string); - - std::wstring wrapped_actual = UTF16ToWide(localized_file_path_string); - EXPECT_EQ(wrapped_expected, wrapped_actual); - } -} - -TEST_F(RTLTest, WrapString) { - const wchar_t* cases[] = { - L" . ", - L"abc", - L"a" L"\x5d0\x5d1", - L"a" L"\x5d1" L"b", - L"\x5d0\x5d1\x5d2", - L"\x5d0\x5d1" L"a", - L"\x5d0" L"a" L"\x5d1", - }; - - const bool was_rtl = IsRTL(); - - test::ScopedRestoreICUDefaultLocale restore_locale; - for (size_t i = 0; i < 2; ++i) { - // Toggle the application default text direction (to try each direction). - SetRTL(!IsRTL()); - - string16 empty; - WrapStringWithLTRFormatting(&empty); - EXPECT_TRUE(empty.empty()); - WrapStringWithRTLFormatting(&empty); - EXPECT_TRUE(empty.empty()); - - for (size_t i = 0; i < arraysize(cases); ++i) { - string16 input = WideToUTF16(cases[i]); - string16 ltr_wrap = input; - WrapStringWithLTRFormatting(<r_wrap); - EXPECT_EQ(ltr_wrap[0], kLeftToRightEmbeddingMark); - EXPECT_EQ(ltr_wrap.substr(1, ltr_wrap.length() - 2), input); - EXPECT_EQ(ltr_wrap[ltr_wrap.length() -1], kPopDirectionalFormatting); - - string16 rtl_wrap = input; - WrapStringWithRTLFormatting(&rtl_wrap); - EXPECT_EQ(rtl_wrap[0], kRightToLeftEmbeddingMark); - EXPECT_EQ(rtl_wrap.substr(1, rtl_wrap.length() - 2), input); - EXPECT_EQ(rtl_wrap[rtl_wrap.length() -1], kPopDirectionalFormatting); - } - } - - EXPECT_EQ(was_rtl, IsRTL()); -} - -TEST_F(RTLTest, GetDisplayStringInLTRDirectionality) { - struct { - const wchar_t* path; - bool wrap_ltr; - bool wrap_rtl; - } cases[] = { - { L"test", false, true }, - { L"test.html", false, true }, - { L"\x05d0\x05d1\x05d2", true, true }, - { L"\x05d0\x05d1\x05d2.txt", true, true }, - { L"\x05d0" L"abc", true, true }, - { L"\x05d0" L"abc.txt", true, true }, - { L"abc\x05d0\x05d1", false, true }, - { L"abc\x05d0\x05d1.jpg", false, true }, - }; - - const bool was_rtl = IsRTL(); - - test::ScopedRestoreICUDefaultLocale restore_locale; - for (size_t i = 0; i < 2; ++i) { - // Toggle the application default text direction (to try each direction). - SetRTL(!IsRTL()); - for (size_t i = 0; i < arraysize(cases); ++i) { - string16 input = WideToUTF16(cases[i].path); - string16 output = GetDisplayStringInLTRDirectionality(input); - // Test the expected wrapping behavior for the current UI directionality. - if (IsRTL() ? cases[i].wrap_rtl : cases[i].wrap_ltr) - EXPECT_NE(output, input); - else - EXPECT_EQ(output, input); - } - } - - EXPECT_EQ(was_rtl, IsRTL()); -} - -TEST_F(RTLTest, GetTextDirection) { - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("ar")); - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("ar_EG")); - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("he")); - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("he_IL")); - // iw is an obsolete code for Hebrew. - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("iw")); - // Although we're not yet localized to Farsi and Urdu, we - // do have the text layout direction information for them. - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("fa")); - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("ur")); -#if 0 - // Enable these when we include the minimal locale data for Azerbaijani - // written in Arabic and Dhivehi. At the moment, our copy of - // ICU data does not have entries for them. - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("az_Arab")); - // Dhivehi that uses Thaana script. - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocale("dv")); -#endif - EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("en")); - // Chinese in China with '-'. - EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("zh-CN")); - // Filipino : 3-letter code - EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("fil")); - // Russian - EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("ru")); - // Japanese that uses multiple scripts - EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocale("ja")); -} - -TEST_F(RTLTest, GetTextDirectionForLocaleInStartUp) { - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("ar")); - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("ar_EG")); - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("he")); - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("he_IL")); - // iw is an obsolete code for Hebrew. - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("iw")); - // Although we're not yet localized to Farsi and Urdu, we - // do have the text layout direction information for them. - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("fa")); - EXPECT_EQ(RIGHT_TO_LEFT, GetTextDirectionForLocaleInStartUp("ur")); - EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocaleInStartUp("en")); - // Chinese in China with '-'. - EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocaleInStartUp("zh-CN")); - // Filipino : 3-letter code - EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocaleInStartUp("fil")); - // Russian - EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocaleInStartUp("ru")); - // Japanese that uses multiple scripts - EXPECT_EQ(LEFT_TO_RIGHT, GetTextDirectionForLocaleInStartUp("ja")); -} - -TEST_F(RTLTest, UnadjustStringForLocaleDirection) { - // These test strings are borrowed from WrapPathWithLTRFormatting - const wchar_t* cases[] = { - L"foo bar", - L"foo \x05d0 bar", - L"\x05d0 foo bar", - L"!foo \x05d0 bar", - L",\x05d0 foo bar", - L"\x202a \x05d0 foo bar", - L"\x202d \x05d0 foo bar", - L"\x202b foo \x05d0 bar", - L"\x202e foo \x05d0 bar", - L"\x0622 foo \x05d0 bar", - }; - - const bool was_rtl = IsRTL(); - - test::ScopedRestoreICUDefaultLocale restore_locale; - for (size_t i = 0; i < 2; ++i) { - // Toggle the application default text direction (to try each direction). - SetRTL(!IsRTL()); - - for (size_t i = 0; i < arraysize(cases); ++i) { - string16 test_case = WideToUTF16(cases[i]); - string16 adjusted_string = test_case; - - if (!AdjustStringForLocaleDirection(&adjusted_string)) - continue; - - EXPECT_NE(test_case, adjusted_string); - EXPECT_TRUE(UnadjustStringForLocaleDirection(&adjusted_string)); - EXPECT_EQ(test_case, adjusted_string) << " for test case [" << test_case - << "] with IsRTL() == " << IsRTL(); - } - } - - EXPECT_EQ(was_rtl, IsRTL()); -} - -TEST_F(RTLTest, EnsureTerminatedDirectionalFormatting) { - struct { - const wchar_t* unformated_text; - const wchar_t* formatted_text; - } cases[] = { - // Tests string without any dir-formatting characters. - {L"google.com", L"google.com"}, - // Tests string with properly terminated dir-formatting character. - {L"\x202egoogle.com\x202c", L"\x202egoogle.com\x202c"}, - // Tests string with over-terminated dir-formatting characters. - {L"\x202egoogle\x202c.com\x202c", L"\x202egoogle\x202c.com\x202c"}, - // Tests string beginning with a dir-formatting character. - {L"\x202emoc.elgoog", L"\x202emoc.elgoog\x202c"}, - // Tests string that over-terminates then re-opens. - {L"\x202egoogle\x202c\x202c.\x202eom", - L"\x202egoogle\x202c\x202c.\x202eom\x202c"}, - // Tests string containing a dir-formatting character in the middle. - {L"google\x202e.com", L"google\x202e.com\x202c"}, - // Tests string with multiple dir-formatting characters. - {L"\x202egoogle\x202e.com/\x202eguest", - L"\x202egoogle\x202e.com/\x202eguest\x202c\x202c\x202c"}, - // Test the other dir-formatting characters (U+202A, U+202B, and U+202D). - {L"\x202agoogle.com", L"\x202agoogle.com\x202c"}, - {L"\x202bgoogle.com", L"\x202bgoogle.com\x202c"}, - {L"\x202dgoogle.com", L"\x202dgoogle.com\x202c"}, - }; - - const bool was_rtl = IsRTL(); - - test::ScopedRestoreICUDefaultLocale restore_locale; - for (size_t i = 0; i < 2; ++i) { - // Toggle the application default text direction (to try each direction). - SetRTL(!IsRTL()); - for (size_t i = 0; i < arraysize(cases); ++i) { - string16 unsanitized_text = WideToUTF16(cases[i].unformated_text); - string16 sanitized_text = WideToUTF16(cases[i].formatted_text); - EnsureTerminatedDirectionalFormatting(&unsanitized_text); - EXPECT_EQ(sanitized_text, unsanitized_text); - } - } - EXPECT_EQ(was_rtl, IsRTL()); -} - -TEST_F(RTLTest, SanitizeUserSuppliedString) { - struct { - const wchar_t* unformatted_text; - const wchar_t* formatted_text; - } cases[] = { - // Tests RTL string with properly terminated dir-formatting character. - {L"\x202eكبير Google التطبيق\x202c", L"\x202eكبير Google التطبيق\x202c"}, - // Tests RTL string with over-terminated dir-formatting characters. - {L"\x202eكبير Google\x202cالتطبيق\x202c", - L"\x202eكبير Google\x202cالتطبيق\x202c"}, - // Tests RTL string that over-terminates then re-opens. - {L"\x202eكبير Google\x202c\x202cالتطبيق\x202e", - L"\x202eكبير Google\x202c\x202cالتطبيق\x202e\x202c"}, - // Tests RTL string with multiple dir-formatting characters. - {L"\x202eك\x202eبير Google الت\x202eطبيق", - L"\x202eك\x202eبير Google الت\x202eطبيق\x202c\x202c\x202c"}, - // Test the other dir-formatting characters (U+202A, U+202B, and U+202D). - {L"\x202aكبير Google التطبيق", L"\x202aكبير Google التطبيق\x202c"}, - {L"\x202bكبير Google التطبيق", L"\x202bكبير Google التطبيق\x202c"}, - {L"\x202dكبير Google التطبيق", L"\x202dكبير Google التطبيق\x202c"}, - - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - // On Windows for an LTR locale, no changes to the string are made. - string16 prefix, suffix = WideToUTF16(L""); -#if !defined(OS_WIN) - prefix = WideToUTF16(L"\x200e\x202b"); - suffix = WideToUTF16(L"\x202c\x200e"); -#endif // !OS_WIN - string16 unsanitized_text = WideToUTF16(cases[i].unformatted_text); - string16 sanitized_text = - prefix + WideToUTF16(cases[i].formatted_text) + suffix; - SanitizeUserSuppliedString(&unsanitized_text); - EXPECT_EQ(sanitized_text, unsanitized_text); - } -} - -class SetICULocaleTest : public PlatformTest {}; - -TEST_F(SetICULocaleTest, OverlongLocaleId) { - test::ScopedRestoreICUDefaultLocale restore_locale; - std::string id("fr-ca-x-foo"); - while (id.length() < 152) - id.append("-x-foo"); - SetICUDefaultLocale(id); - EXPECT_STRNE("en_US", icu::Locale::getDefault().getName()); - id.append("zzz"); - SetICUDefaultLocale(id); - EXPECT_STREQ("en_US", icu::Locale::getDefault().getName()); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/streaming_utf8_validator.cc b/i18n/streaming_utf8_validator.cc deleted file mode 100644 index 19c86a37a..000000000 --- a/i18n/streaming_utf8_validator.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This 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_t StateTableLookup(uint8_t 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_t 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_t shift_amount = StateTableLookup(state); - const uint8_t 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/i18n/streaming_utf8_validator.h b/i18n/streaming_utf8_validator.h deleted file mode 100644 index ebf38a69b..000000000 --- a/i18n/streaming_utf8_validator.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// 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 -#include - -#include - -#include "base/i18n/base_i18n_export.h" -#include "base/macros.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_t 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/i18n/streaming_utf8_validator_perftest.cc b/i18n/streaming_utf8_validator_perftest.cc deleted file mode 100644 index ad328f886..000000000 --- a/i18n/streaming_utf8_validator_perftest.cc +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// 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 - -#include - -#include "base/bind.h" -#include "base/callback.h" -#include "base/macros.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(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; -}; - -bool IsStringUTF8(const std::string& str) { - return base::IsStringUTF8(base::StringPiece(str)); -} - -// IsString7Bit is intentionally placed last so it can be excluded easily. -const TestFunctionDescription kTestFunctions[] = { - {&StreamingUtf8Validator::Validate, "StreamingUtf8Validator"}, - {&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 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(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/i18n/streaming_utf8_validator_unittest.cc b/i18n/streaming_utf8_validator_unittest.cc deleted file mode 100644 index f9772d098..000000000 --- a/i18n/streaming_utf8_validator_unittest.cc +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/i18n/streaming_utf8_validator.h" - -#include -#include -#include -#include - -#include - -#include "base/macros.h" -#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/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/lock.h" -#include "base/task_scheduler/post_task.h" -#include "base/task_scheduler/task_scheduler.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_t kThoroughTestChunkSize = 1 << 24; - -class StreamingUtf8ValidatorThoroughTest : public ::testing::Test { - protected: - StreamingUtf8ValidatorThoroughTest() - : 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_t src_len) { - int32_t char_index = 0; - - while (char_index < src_len) { - int32_t 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_t 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_t begin, uint32_t size) { - for (uint32_t i = 0; i < size; ++i) { - TestNumber(begin + i); - } - base::AutoLock al(lock_); - ++tasks_finished_; - LOG(INFO) << tasks_finished_ << " / " << tasks_dispatched_ - << " tasks done\n"; - } - - protected: - base::Lock lock_; - int tasks_dispatched_; - int tasks_finished_; -}; - -TEST_F(StreamingUtf8ValidatorThoroughTest, TestEverything) { - base::TaskScheduler::CreateAndStartWithDefaultParams( - "StreamingUtf8ValidatorThoroughTest"); - { - base::AutoLock al(lock_); - uint32_t begin = 0; - do { - base::PostTaskWithTraits( - FROM_HERE, {base::TaskShutdownBehavior::BLOCK_SHUTDOWN}, - base::BindOnce(&StreamingUtf8ValidatorThoroughTest::TestRange, - base::Unretained(this), begin, - kThoroughTestChunkSize)); - ++tasks_dispatched_; - begin += kThoroughTestChunkSize; - } while (begin != 0); - } - base::TaskScheduler::GetInstance()->Shutdown(); - base::TaskScheduler::GetInstance()->JoinForTesting(); - base::TaskScheduler::SetInstance(nullptr); -} - -#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 - 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 - 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 - 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/i18n/string_compare.cc b/i18n/string_compare.cc deleted file mode 100644 index 649c28119..000000000 --- a/i18n/string_compare.cc +++ /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 "base/i18n/string_compare.h" - -#include "base/logging.h" -#include "base/strings/utf_string_conversions.h" -#include "third_party/icu/source/common/unicode/unistr.h" - -namespace base { -namespace i18n { - -// Compares the character data stored in two different string16 strings by -// specified Collator instance. -UCollationResult CompareString16WithCollator(const icu::Collator& collator, - const string16& lhs, - const string16& rhs) { - UErrorCode error = U_ZERO_ERROR; - UCollationResult result = collator.compare( - icu::UnicodeString(FALSE, lhs.c_str(), static_cast(lhs.length())), - icu::UnicodeString(FALSE, rhs.c_str(), static_cast(rhs.length())), - error); - DCHECK(U_SUCCESS(error)); - return result; -} - -} // namespace i18n -} // namespace base diff --git a/i18n/string_compare.h b/i18n/string_compare.h deleted file mode 100644 index 5fcc5feae..000000000 --- a/i18n/string_compare.h +++ /dev/null @@ -1,28 +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_I18N_STRING_COMPARE_H_ -#define BASE_I18N_STRING_COMPARE_H_ - -#include -#include -#include - -#include "base/i18n/base_i18n_export.h" -#include "base/strings/string16.h" -#include "third_party/icu/source/i18n/unicode/coll.h" - -namespace base { -namespace i18n { - -// Compares the two strings using the specified collator. -BASE_I18N_EXPORT UCollationResult -CompareString16WithCollator(const icu::Collator& collator, - const string16& lhs, - const string16& rhs); - -} // namespace i18n -} // namespace base - -#endif // BASE_I18N_STRING_COMPARE_H_ diff --git a/i18n/string_search.cc b/i18n/string_search.cc deleted file mode 100644 index 2f6fee4fe..000000000 --- a/i18n/string_search.cc +++ /dev/null @@ -1,81 +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 - -#include "base/i18n/string_search.h" -#include "base/logging.h" - -#include "third_party/icu/source/i18n/unicode/usearch.h" - -namespace base { -namespace i18n { - -FixedPatternStringSearchIgnoringCaseAndAccents:: -FixedPatternStringSearchIgnoringCaseAndAccents(const string16& find_this) - : find_this_(find_this) { - // usearch_open requires a valid string argument to be searched, even if we - // want to set it by usearch_setText afterwards. So, supplying a dummy text. - const string16& dummy = find_this_; - - UErrorCode status = U_ZERO_ERROR; - search_ = usearch_open(find_this_.data(), find_this_.size(), dummy.data(), - dummy.size(), uloc_getDefault(), - nullptr, // breakiter - &status); - if (U_SUCCESS(status)) { - UCollator* collator = usearch_getCollator(search_); - ucol_setStrength(collator, UCOL_PRIMARY); - usearch_reset(search_); - } -} - -FixedPatternStringSearchIgnoringCaseAndAccents:: -~FixedPatternStringSearchIgnoringCaseAndAccents() { - if (search_) - usearch_close(search_); -} - -bool FixedPatternStringSearchIgnoringCaseAndAccents::Search( - const string16& in_this, size_t* match_index, size_t* match_length) { - UErrorCode status = U_ZERO_ERROR; - usearch_setText(search_, in_this.data(), in_this.size(), &status); - - // Default to basic substring search if usearch fails. According to - // http://icu-project.org/apiref/icu4c/usearch_8h.html, usearch_open will fail - // if either |find_this| or |in_this| are empty. In either case basic - // substring search will give the correct return value. - if (!U_SUCCESS(status)) { - size_t index = in_this.find(find_this_); - if (index == string16::npos) { - return false; - } else { - if (match_index) - *match_index = index; - if (match_length) - *match_length = find_this_.size(); - return true; - } - } - - int32_t index = usearch_first(search_, &status); - if (!U_SUCCESS(status) || index == USEARCH_DONE) - return false; - if (match_index) - *match_index = static_cast(index); - if (match_length) - *match_length = static_cast(usearch_getMatchedLength(search_)); - return true; -} - -bool StringSearchIgnoringCaseAndAccents(const string16& find_this, - const string16& in_this, - size_t* match_index, - size_t* match_length) { - return FixedPatternStringSearchIgnoringCaseAndAccents(find_this).Search( - in_this, match_index, match_length); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/string_search.h b/i18n/string_search.h deleted file mode 100644 index 07a29c19b..000000000 --- a/i18n/string_search.h +++ /dev/null @@ -1,55 +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_I18N_STRING_SEARCH_H_ -#define BASE_I18N_STRING_SEARCH_H_ - -#include - -#include "base/i18n/base_i18n_export.h" -#include "base/strings/string16.h" - -struct UStringSearch; - -namespace base { -namespace i18n { - -// Returns true if |in_this| contains |find_this|. If |match_index| or -// |match_length| are non-NULL, they are assigned the start position and total -// length of the match. -// -// Only differences between base letters are taken into consideration. Case and -// accent differences are ignored. Please refer to 'primary level' in -// http://userguide.icu-project.org/collation/concepts for additional details. -BASE_I18N_EXPORT - bool StringSearchIgnoringCaseAndAccents(const string16& find_this, - const string16& in_this, - size_t* match_index, - size_t* match_length); - -// This class is for speeding up multiple StringSearchIgnoringCaseAndAccents() -// with the same |find_this| argument. |find_this| is passed as the constructor -// argument, and precomputation for searching is done only at that timing. -class BASE_I18N_EXPORT FixedPatternStringSearchIgnoringCaseAndAccents { - public: - explicit FixedPatternStringSearchIgnoringCaseAndAccents( - const string16& find_this); - ~FixedPatternStringSearchIgnoringCaseAndAccents(); - - // Returns true if |in_this| contains |find_this|. If |match_index| or - // |match_length| are non-NULL, they are assigned the start position and total - // length of the match. - bool Search(const string16& in_this, - size_t* match_index, - size_t* match_length); - - private: - string16 find_this_; - UStringSearch* search_; -}; - -} // namespace i18n -} // namespace base - -#endif // BASE_I18N_STRING_SEARCH_H_ diff --git a/i18n/string_search_unittest.cc b/i18n/string_search_unittest.cc deleted file mode 100644 index 69501d6c9..000000000 --- a/i18n/string_search_unittest.cc +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include - -#include "base/i18n/rtl.h" -#include "base/i18n/string_search.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/icu/source/i18n/unicode/usearch.h" - -namespace base { -namespace i18n { - -// Note on setting default locale for testing: The current default locale on -// the Mac trybot is en_US_POSIX, with which primary-level collation strength -// string search is case-sensitive, when normally it should be -// case-insensitive. In other locales (including en_US which English speakers -// in the U.S. use), this search would be case-insensitive as expected. - -TEST(StringSearchTest, ASCII) { - std::string default_locale(uloc_getDefault()); - bool locale_is_posix = (default_locale == "en_US_POSIX"); - if (locale_is_posix) - SetICUDefaultLocale("en_US"); - - size_t index = 0; - size_t length = 0; - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - ASCIIToUTF16("hello"), ASCIIToUTF16("hello world"), &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(5U, length); - - EXPECT_FALSE(StringSearchIgnoringCaseAndAccents( - ASCIIToUTF16("h e l l o"), ASCIIToUTF16("h e l l o"), - &index, &length)); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - ASCIIToUTF16("aabaaa"), ASCIIToUTF16("aaabaabaaa"), &index, &length)); - EXPECT_EQ(4U, index); - EXPECT_EQ(6U, length); - - EXPECT_FALSE(StringSearchIgnoringCaseAndAccents( - ASCIIToUTF16("searching within empty string"), string16(), - &index, &length)); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - string16(), ASCIIToUTF16("searching for empty string"), &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(0U, length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - ASCIIToUTF16("case insensitivity"), ASCIIToUTF16("CaSe InSeNsItIvItY"), - &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(18U, length); - - if (locale_is_posix) - SetICUDefaultLocale(default_locale.data()); -} - -TEST(StringSearchTest, UnicodeLocaleIndependent) { - // Base characters - const string16 e_base = WideToUTF16(L"e"); - const string16 E_base = WideToUTF16(L"E"); - const string16 a_base = WideToUTF16(L"a"); - - // Composed characters - const string16 e_with_acute_accent = WideToUTF16(L"\u00e9"); - const string16 E_with_acute_accent = WideToUTF16(L"\u00c9"); - const string16 e_with_grave_accent = WideToUTF16(L"\u00e8"); - const string16 E_with_grave_accent = WideToUTF16(L"\u00c8"); - const string16 a_with_acute_accent = WideToUTF16(L"\u00e1"); - - // Decomposed characters - const string16 e_with_acute_combining_mark = WideToUTF16(L"e\u0301"); - const string16 E_with_acute_combining_mark = WideToUTF16(L"E\u0301"); - const string16 e_with_grave_combining_mark = WideToUTF16(L"e\u0300"); - const string16 E_with_grave_combining_mark = WideToUTF16(L"E\u0300"); - const string16 a_with_acute_combining_mark = WideToUTF16(L"a\u0301"); - - std::string default_locale(uloc_getDefault()); - bool locale_is_posix = (default_locale == "en_US_POSIX"); - if (locale_is_posix) - SetICUDefaultLocale("en_US"); - - size_t index = 0; - size_t length = 0; - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - e_base, e_with_acute_accent, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_acute_accent.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - e_with_acute_accent, e_base, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_base.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - e_base, e_with_acute_combining_mark, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_acute_combining_mark.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - e_with_acute_combining_mark, e_base, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_base.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - e_with_acute_combining_mark, e_with_acute_accent, - &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_acute_accent.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - e_with_acute_accent, e_with_acute_combining_mark, - &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_acute_combining_mark.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - e_with_acute_combining_mark, e_with_grave_combining_mark, - &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_grave_combining_mark.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - e_with_grave_combining_mark, e_with_acute_combining_mark, - &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_acute_combining_mark.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - e_with_acute_combining_mark, e_with_grave_accent, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_grave_accent.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - e_with_grave_accent, e_with_acute_combining_mark, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_acute_combining_mark.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - E_with_acute_accent, e_with_acute_accent, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_acute_accent.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - E_with_grave_accent, e_with_acute_accent, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_acute_accent.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - E_with_acute_combining_mark, e_with_grave_accent, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_grave_accent.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - E_with_grave_combining_mark, e_with_acute_accent, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_acute_accent.size(), length); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents( - E_base, e_with_grave_accent, &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(e_with_grave_accent.size(), length); - - EXPECT_FALSE(StringSearchIgnoringCaseAndAccents( - a_with_acute_accent, e_with_acute_accent, &index, &length)); - - EXPECT_FALSE(StringSearchIgnoringCaseAndAccents( - a_with_acute_combining_mark, e_with_acute_combining_mark, - &index, &length)); - - if (locale_is_posix) - SetICUDefaultLocale(default_locale.data()); -} - -TEST(StringSearchTest, UnicodeLocaleDependent) { - // Base characters - const string16 a_base = WideToUTF16(L"a"); - - // Composed characters - const string16 a_with_ring = WideToUTF16(L"\u00e5"); - - EXPECT_TRUE(StringSearchIgnoringCaseAndAccents(a_base, a_with_ring, nullptr, - nullptr)); - - const char* default_locale = uloc_getDefault(); - SetICUDefaultLocale("da"); - - EXPECT_FALSE(StringSearchIgnoringCaseAndAccents(a_base, a_with_ring, nullptr, - nullptr)); - - SetICUDefaultLocale(default_locale); -} - -TEST(StringSearchTest, FixedPatternMultipleSearch) { - std::string default_locale(uloc_getDefault()); - bool locale_is_posix = (default_locale == "en_US_POSIX"); - if (locale_is_posix) - SetICUDefaultLocale("en_US"); - - size_t index = 0; - size_t length = 0; - - // Search "hello" over multiple texts. - FixedPatternStringSearchIgnoringCaseAndAccents query(ASCIIToUTF16("hello")); - EXPECT_TRUE(query.Search(ASCIIToUTF16("12hello34"), &index, &length)); - EXPECT_EQ(2U, index); - EXPECT_EQ(5U, length); - EXPECT_FALSE(query.Search(ASCIIToUTF16("bye"), &index, &length)); - EXPECT_TRUE(query.Search(ASCIIToUTF16("hELLo"), &index, &length)); - EXPECT_EQ(0U, index); - EXPECT_EQ(5U, length); - - if (locale_is_posix) - SetICUDefaultLocale(default_locale.data()); -} - -} // namespace i18n -} // namespace base diff --git a/i18n/time_formatting.cc b/i18n/time_formatting.cc deleted file mode 100644 index 3a5394ae3..000000000 --- a/i18n/time_formatting.cc +++ /dev/null @@ -1,301 +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/i18n/time_formatting.h" - -#include - -#include - -#include "base/i18n/unicodestring.h" -#include "base/logging.h" -#include "base/strings/utf_string_conversions.h" -#include "base/time/time.h" -#include "third_party/icu/source/common/unicode/utypes.h" -#include "third_party/icu/source/i18n/unicode/datefmt.h" -#include "third_party/icu/source/i18n/unicode/dtitvfmt.h" -#include "third_party/icu/source/i18n/unicode/dtptngen.h" -#include "third_party/icu/source/i18n/unicode/fmtable.h" -#include "third_party/icu/source/i18n/unicode/measfmt.h" -#include "third_party/icu/source/i18n/unicode/smpdtfmt.h" - -namespace base { -namespace { - -string16 TimeFormat(const icu::DateFormat* formatter, - const Time& time) { - DCHECK(formatter); - icu::UnicodeString date_string; - - formatter->format(static_cast(time.ToDoubleT() * 1000), date_string); - return i18n::UnicodeStringToString16(date_string); -} - -string16 TimeFormatWithoutAmPm(const icu::DateFormat* formatter, - const Time& time) { - DCHECK(formatter); - icu::UnicodeString time_string; - - icu::FieldPosition ampm_field(icu::DateFormat::kAmPmField); - formatter->format( - static_cast(time.ToDoubleT() * 1000), time_string, ampm_field); - int ampm_length = ampm_field.getEndIndex() - ampm_field.getBeginIndex(); - if (ampm_length) { - int begin = ampm_field.getBeginIndex(); - // Doesn't include any spacing before the field. - if (begin) - begin--; - time_string.removeBetween(begin, ampm_field.getEndIndex()); - } - return i18n::UnicodeStringToString16(time_string); -} - -icu::SimpleDateFormat CreateSimpleDateFormatter(const char* pattern) { - // Generate a locale-dependent format pattern. The generator will take - // care of locale-dependent formatting issues like which separator to - // use (some locales use '.' instead of ':'), and where to put the am/pm - // marker. - UErrorCode status = U_ZERO_ERROR; - std::unique_ptr generator( - icu::DateTimePatternGenerator::createInstance(status)); - DCHECK(U_SUCCESS(status)); - icu::UnicodeString generated_pattern = - generator->getBestPattern(icu::UnicodeString(pattern), status); - DCHECK(U_SUCCESS(status)); - - // Then, format the time using the generated pattern. - icu::SimpleDateFormat formatter(generated_pattern, status); - DCHECK(U_SUCCESS(status)); - - return formatter; -} - -UMeasureFormatWidth DurationWidthToMeasureWidth(DurationFormatWidth width) { - switch (width) { - case DURATION_WIDTH_WIDE: return UMEASFMT_WIDTH_WIDE; - case DURATION_WIDTH_SHORT: return UMEASFMT_WIDTH_SHORT; - case DURATION_WIDTH_NARROW: return UMEASFMT_WIDTH_NARROW; - case DURATION_WIDTH_NUMERIC: return UMEASFMT_WIDTH_NUMERIC; - } - NOTREACHED(); - return UMEASFMT_WIDTH_COUNT; -} - -const char* DateFormatToString(DateFormat format) { - switch (format) { - case DATE_FORMAT_YEAR_MONTH: - return UDAT_YEAR_MONTH; - case DATE_FORMAT_MONTH_WEEKDAY_DAY: - return UDAT_MONTH_WEEKDAY_DAY; - } - NOTREACHED(); - return UDAT_YEAR_MONTH_DAY; -} - -} // namespace - -string16 TimeFormatTimeOfDay(const Time& time) { - // We can omit the locale parameter because the default should match - // Chrome's application locale. - std::unique_ptr formatter( - icu::DateFormat::createTimeInstance(icu::DateFormat::kShort)); - return TimeFormat(formatter.get(), time); -} - -string16 TimeFormatTimeOfDayWithMilliseconds(const Time& time) { - icu::SimpleDateFormat formatter = CreateSimpleDateFormatter("HmsSSS"); - return TimeFormatWithoutAmPm(&formatter, time); -} - -string16 TimeFormatTimeOfDayWithHourClockType(const Time& time, - HourClockType type, - AmPmClockType ampm) { - // Just redirect to the normal function if the default type matches the - // given type. - HourClockType default_type = GetHourClockType(); - if (default_type == type && (type == k24HourClock || ampm == kKeepAmPm)) { - return TimeFormatTimeOfDay(time); - } - - const char* base_pattern = (type == k12HourClock ? "ahm" : "Hm"); - icu::SimpleDateFormat formatter = CreateSimpleDateFormatter(base_pattern); - - if (ampm == kKeepAmPm) { - return TimeFormat(&formatter, time); - } else { - return TimeFormatWithoutAmPm(&formatter, time); - } -} - -string16 TimeFormatShortDate(const Time& time) { - std::unique_ptr formatter( - icu::DateFormat::createDateInstance(icu::DateFormat::kMedium)); - return TimeFormat(formatter.get(), time); -} - -string16 TimeFormatShortDateNumeric(const Time& time) { - std::unique_ptr formatter( - icu::DateFormat::createDateInstance(icu::DateFormat::kShort)); - return TimeFormat(formatter.get(), time); -} - -string16 TimeFormatShortDateAndTime(const Time& time) { - std::unique_ptr formatter( - icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort)); - return TimeFormat(formatter.get(), time); -} - -string16 TimeFormatShortDateAndTimeWithTimeZone(const Time& time) { - std::unique_ptr formatter( - icu::DateFormat::createDateTimeInstance(icu::DateFormat::kShort, - icu::DateFormat::kLong)); - return TimeFormat(formatter.get(), time); -} - -string16 TimeFormatMonthAndYear(const Time& time) { - icu::SimpleDateFormat formatter = - CreateSimpleDateFormatter(DateFormatToString(DATE_FORMAT_YEAR_MONTH)); - return TimeFormat(&formatter, time); -} - -string16 TimeFormatFriendlyDateAndTime(const Time& time) { - std::unique_ptr formatter( - icu::DateFormat::createDateTimeInstance(icu::DateFormat::kFull)); - return TimeFormat(formatter.get(), time); -} - -string16 TimeFormatFriendlyDate(const Time& time) { - std::unique_ptr formatter( - icu::DateFormat::createDateInstance(icu::DateFormat::kFull)); - return TimeFormat(formatter.get(), time); -} - -string16 TimeFormatWithPattern(const Time& time, const char* pattern) { - icu::SimpleDateFormat formatter = CreateSimpleDateFormatter(pattern); - return TimeFormat(&formatter, time); -} - -bool TimeDurationFormat(const TimeDelta time, - const DurationFormatWidth width, - string16* out) { - DCHECK(out); - UErrorCode status = U_ZERO_ERROR; - const int total_minutes = static_cast(time.InSecondsF() / 60 + 0.5); - const int hours = total_minutes / 60; - const int minutes = total_minutes % 60; - UMeasureFormatWidth u_width = DurationWidthToMeasureWidth(width); - - // TODO(derat): Delete the |status| checks and LOG(ERROR) calls throughout - // this function once the cause of http://crbug.com/677043 is tracked down. - const icu::Measure measures[] = { - icu::Measure(hours, icu::MeasureUnit::createHour(status), status), - icu::Measure(minutes, icu::MeasureUnit::createMinute(status), status)}; - if (U_FAILURE(status)) { - LOG(ERROR) << "Creating MeasureUnit or Measure for " << hours << "h" - << minutes << "m failed: " << u_errorName(status); - return false; - } - - icu::MeasureFormat measure_format(icu::Locale::getDefault(), u_width, status); - if (U_FAILURE(status)) { - LOG(ERROR) << "Creating MeasureFormat for " - << icu::Locale::getDefault().getName() - << " failed: " << u_errorName(status); - return false; - } - - icu::UnicodeString formatted; - icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE); - measure_format.formatMeasures(measures, 2, formatted, ignore, status); - if (U_FAILURE(status)) { - LOG(ERROR) << "formatMeasures failed: " << u_errorName(status); - return false; - } - - *out = i18n::UnicodeStringToString16(formatted); - return true; -} - -bool TimeDurationFormatWithSeconds(const TimeDelta time, - const DurationFormatWidth width, - string16* out) { - DCHECK(out); - UErrorCode status = U_ZERO_ERROR; - const int64_t total_seconds = static_cast(time.InSecondsF() + 0.5); - const int hours = total_seconds / 3600; - const int minutes = (total_seconds - hours * 3600) / 60; - const int seconds = total_seconds % 60; - UMeasureFormatWidth u_width = DurationWidthToMeasureWidth(width); - - const icu::Measure measures[] = { - icu::Measure(hours, icu::MeasureUnit::createHour(status), status), - icu::Measure(minutes, icu::MeasureUnit::createMinute(status), status), - icu::Measure(seconds, icu::MeasureUnit::createSecond(status), status)}; - icu::MeasureFormat measure_format(icu::Locale::getDefault(), u_width, status); - icu::UnicodeString formatted; - icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE); - measure_format.formatMeasures(measures, 3, formatted, ignore, status); - *out = i18n::UnicodeStringToString16(formatted); - return U_SUCCESS(status) == TRUE; -} - -string16 DateIntervalFormat(const Time& begin_time, - const Time& end_time, - DateFormat format) { - UErrorCode status = U_ZERO_ERROR; - - std::unique_ptr formatter( - icu::DateIntervalFormat::createInstance(DateFormatToString(format), - status)); - - icu::FieldPosition pos = 0; - UDate start_date = static_cast(begin_time.ToDoubleT() * 1000); - UDate end_date = static_cast(end_time.ToDoubleT() * 1000); - icu::DateInterval interval(start_date, end_date); - icu::UnicodeString formatted; - formatter->format(&interval, formatted, pos, status); - return i18n::UnicodeStringToString16(formatted); -} - -HourClockType GetHourClockType() { - // TODO(satorux,jshin): Rework this with ures_getByKeyWithFallback() - // once it becomes public. The short time format can be found at - // "calendar/gregorian/DateTimePatterns/3" in the resources. - std::unique_ptr formatter( - static_cast( - icu::DateFormat::createTimeInstance(icu::DateFormat::kShort))); - // Retrieve the short time format. - icu::UnicodeString pattern_unicode; - formatter->toPattern(pattern_unicode); - - // Determine what hour clock type the current locale uses, by checking - // "a" (am/pm marker) in the short time format. This is reliable as "a" - // is used by all of 12-hour clock formats, but not any of 24-hour clock - // formats, as shown below. - // - // % grep -A4 DateTimePatterns third_party/icu/source/data/locales/*.txt | - // grep -B1 -- -- |grep -v -- '--' | - // perl -nle 'print $1 if /^\S+\s+"(.*)"/' |sort -u - // - // H.mm - // H:mm - // HH.mm - // HH:mm - // a h:mm - // ah:mm - // ahh:mm - // h-mm a - // h:mm a - // hh:mm a - // - // See http://userguide.icu-project.org/formatparse/datetime for details - // about the date/time format syntax. - if (pattern_unicode.indexOf('a') == -1) { - return k24HourClock; - } else { - return k12HourClock; - } -} - -} // namespace base diff --git a/i18n/time_formatting.h b/i18n/time_formatting.h deleted file mode 100644 index 41793b339..000000000 --- a/i18n/time_formatting.h +++ /dev/null @@ -1,142 +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. - -// Basic time formatting methods. These methods use the current locale -// formatting for displaying the time. - -#ifndef BASE_I18N_TIME_FORMATTING_H_ -#define BASE_I18N_TIME_FORMATTING_H_ - -#include "base/compiler_specific.h" -#include "base/i18n/base_i18n_export.h" -#include "base/strings/string16.h" - -namespace base { - -class Time; -class TimeDelta; - -// Argument type used to specify the hour clock type. -enum HourClockType { - k12HourClock, // Uses 1-12. e.g., "3:07 PM" - k24HourClock, // Uses 0-23. e.g., "15:07" -}; - -// Argument type used to specify whether or not to include AM/PM sign. -enum AmPmClockType { - kDropAmPm, // Drops AM/PM sign. e.g., "3:07" - kKeepAmPm, // Keeps AM/PM sign. e.g., "3:07 PM" -}; - -// Should match UMeasureFormatWidth in measfmt.h; replicated here to avoid -// requiring third_party/icu dependencies with this file. -enum DurationFormatWidth { - DURATION_WIDTH_WIDE, // "3 hours, 7 minutes" - DURATION_WIDTH_SHORT, // "3 hr, 7 min" - DURATION_WIDTH_NARROW, // "3h 7m" - DURATION_WIDTH_NUMERIC // "3:07" -}; - -// Date formats from third_party/icu/source/i18n/unicode/udat.h. Add more as -// necessary. -enum DateFormat { - // November 2007 - DATE_FORMAT_YEAR_MONTH, - // Tuesday, 7 November - DATE_FORMAT_MONTH_WEEKDAY_DAY, -}; - -// TODO(derat@chromium.org): Update all of these functions to return boolean -// "success" values and use out-params for formatted strings: -// http://crbug.com/698802 - -// Returns the time of day, e.g., "3:07 PM". -BASE_I18N_EXPORT string16 TimeFormatTimeOfDay(const Time& time); - -// Returns the time of day in 24-hour clock format with millisecond accuracy, -// e.g., "15:07:30.568" -BASE_I18N_EXPORT string16 TimeFormatTimeOfDayWithMilliseconds(const Time& time); - -// Returns the time of day in the specified hour clock type. e.g. -// "3:07 PM" (type == k12HourClock, ampm == kKeepAmPm). -// "3:07" (type == k12HourClock, ampm == kDropAmPm). -// "15:07" (type == k24HourClock). -BASE_I18N_EXPORT string16 TimeFormatTimeOfDayWithHourClockType( - const Time& time, - HourClockType type, - AmPmClockType ampm); - -// Returns a shortened date, e.g. "Nov 7, 2007" -BASE_I18N_EXPORT string16 TimeFormatShortDate(const Time& time); - -// Returns a numeric date such as 12/13/52. -BASE_I18N_EXPORT string16 TimeFormatShortDateNumeric(const Time& time); - -// Returns a numeric date and time such as "12/13/52 2:44:30 PM". -BASE_I18N_EXPORT string16 TimeFormatShortDateAndTime(const Time& time); - -// Returns a month and year, e.g. "November 2007" -BASE_I18N_EXPORT string16 TimeFormatMonthAndYear(const Time& time); - -// Returns a numeric date and time with time zone such as -// "12/13/52 2:44:30 PM PST". -BASE_I18N_EXPORT string16 -TimeFormatShortDateAndTimeWithTimeZone(const Time& time); - -// Formats a time in a friendly sentence format, e.g. -// "Monday, March 6, 2008 2:44:30 PM". -BASE_I18N_EXPORT string16 TimeFormatFriendlyDateAndTime(const Time& time); - -// Formats a time in a friendly sentence format, e.g. -// "Monday, March 6, 2008". -BASE_I18N_EXPORT string16 TimeFormatFriendlyDate(const Time& time); - -// Formats a time using a skeleton to produce a format for different locales -// when an unusual time format is needed, e.g. "Feb. 2, 18:00". -// -// See http://userguide.icu-project.org/formatparse/datetime for details. -BASE_I18N_EXPORT string16 TimeFormatWithPattern(const Time& time, - const char* pattern); - -// Formats a time duration of hours and minutes into various formats, e.g., -// "3:07" or "3 hours, 7 minutes", and returns true on success. See -// DurationFormatWidth for details. -// -// Please don't use width = DURATION_WIDTH_NUMERIC when the time duration -// can possibly be larger than 24h, as the hour value will be cut below 24 -// after formatting. -// TODO(chengx): fix function output when width = DURATION_WIDTH_NUMERIC -// (http://crbug.com/675791) -BASE_I18N_EXPORT bool TimeDurationFormat(const TimeDelta time, - const DurationFormatWidth width, - string16* out) WARN_UNUSED_RESULT; - -// Formats a time duration of hours, minutes and seconds into various formats, -// e.g., "3:07:30" or "3 hours, 7 minutes, 30 seconds", and returns true on -// success. See DurationFormatWidth for details. -// -// Please don't use width = DURATION_WIDTH_NUMERIC when the time duration -// can possibly be larger than 24h, as the hour value will be cut below 24 -// after formatting. -// TODO(chengx): fix function output when width = DURATION_WIDTH_NUMERIC -// (http://crbug.com/675791) -BASE_I18N_EXPORT bool TimeDurationFormatWithSeconds( - const TimeDelta time, - const DurationFormatWidth width, - string16* out) WARN_UNUSED_RESULT; - -// Formats a date interval into various formats, e.g. "2 December - 4 December" -// or "March 2016 - December 2016". See DateFormat for details. -BASE_I18N_EXPORT string16 DateIntervalFormat(const Time& begin_time, - const Time& end_time, - DateFormat format); - -// Gets the hour clock type of the current locale. e.g. -// k12HourClock (en-US). -// k24HourClock (en-GB). -BASE_I18N_EXPORT HourClockType GetHourClockType(); - -} // namespace base - -#endif // BASE_I18N_TIME_FORMATTING_H_ diff --git a/i18n/time_formatting_unittest.cc b/i18n/time_formatting_unittest.cc deleted file mode 100644 index 29b25972d..000000000 --- a/i18n/time_formatting_unittest.cc +++ /dev/null @@ -1,433 +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/i18n/time_formatting.h" - -#include - -#include "base/i18n/rtl.h" -#include "base/i18n/unicodestring.h" -#include "base/strings/utf_string_conversions.h" -#include "base/test/icu_test_util.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/icu/source/common/unicode/uversion.h" -#include "third_party/icu/source/i18n/unicode/calendar.h" -#include "third_party/icu/source/i18n/unicode/timezone.h" -#include "third_party/icu/source/i18n/unicode/tzfmt.h" - -namespace base { -namespace { - -const Time::Exploded kTestDateTimeExploded = { - 2011, 4, 6, 30, // Sat, Apr 30, 2011 - 22, 42, 7, 0 // 22:42:07.000 in UTC = 15:42:07 in US PDT. -}; - -// Returns difference between the local time and GMT formatted as string. -// This function gets |time| because the difference depends on time, -// see https://en.wikipedia.org/wiki/Daylight_saving_time for details. -string16 GetShortTimeZone(const Time& time) { - UErrorCode status = U_ZERO_ERROR; - std::unique_ptr zone(icu::TimeZone::createDefault()); - std::unique_ptr zone_formatter( - icu::TimeZoneFormat::createInstance(icu::Locale::getDefault(), status)); - EXPECT_TRUE(U_SUCCESS(status)); - icu::UnicodeString name; - zone_formatter->format(UTZFMT_STYLE_SPECIFIC_SHORT, *zone, - static_cast(time.ToDoubleT() * 1000), - name, nullptr); - return i18n::UnicodeStringToString16(name); -} - -// Calls TimeDurationFormat() with |delta| and |width| and returns the resulting -// string. On failure, adds a failed expectation and returns an empty string. -string16 TimeDurationFormatString(const TimeDelta& delta, - DurationFormatWidth width) { - string16 str; - EXPECT_TRUE(TimeDurationFormat(delta, width, &str)) - << "Failed to format " << delta.ToInternalValue() << " with width " - << width; - return str; -} - -// Calls TimeDurationFormatWithSeconds() with |delta| and |width| and returns -// the resulting string. On failure, adds a failed expectation and returns an -// empty string. -string16 TimeDurationFormatWithSecondsString(const TimeDelta& delta, - DurationFormatWidth width) { - string16 str; - EXPECT_TRUE(TimeDurationFormatWithSeconds(delta, width, &str)) - << "Failed to format " << delta.ToInternalValue() << " with width " - << width; - return str; -} - -class ScopedRestoreDefaultTimezone { - public: - ScopedRestoreDefaultTimezone(const char* zoneid) { - original_zone_.reset(icu::TimeZone::createDefault()); - icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone(zoneid)); - } - ~ScopedRestoreDefaultTimezone() { - icu::TimeZone::adoptDefault(original_zone_.release()); - } - - ScopedRestoreDefaultTimezone(const ScopedRestoreDefaultTimezone&) = delete; - ScopedRestoreDefaultTimezone& operator=(const ScopedRestoreDefaultTimezone&) = - delete; - - private: - std::unique_ptr original_zone_; -}; - -TEST(TimeFormattingTest, TimeFormatTimeOfDayDefault12h) { - // Test for a locale defaulted to 12h clock. - // As an instance, we use third_party/icu/source/data/locales/en.txt. - test::ScopedRestoreICUDefaultLocale restore_locale; - i18n::SetICUDefaultLocale("en_US"); - ScopedRestoreDefaultTimezone la_time("America/Los_Angeles"); - - Time time; - EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time)); - string16 clock24h(ASCIIToUTF16("15:42")); - string16 clock12h_pm(ASCIIToUTF16("3:42 PM")); - string16 clock12h(ASCIIToUTF16("3:42")); - string16 clock24h_millis(ASCIIToUTF16("15:42:07.000")); - - // The default is 12h clock. - EXPECT_EQ(clock12h_pm, TimeFormatTimeOfDay(time)); - EXPECT_EQ(clock24h_millis, TimeFormatTimeOfDayWithMilliseconds(time)); - EXPECT_EQ(k12HourClock, GetHourClockType()); - // k{Keep,Drop}AmPm should not affect for 24h clock. - EXPECT_EQ(clock24h, - TimeFormatTimeOfDayWithHourClockType(time, - k24HourClock, - kKeepAmPm)); - EXPECT_EQ(clock24h, - TimeFormatTimeOfDayWithHourClockType(time, - k24HourClock, - kDropAmPm)); - // k{Keep,Drop}AmPm affects for 12h clock. - EXPECT_EQ(clock12h_pm, - TimeFormatTimeOfDayWithHourClockType(time, - k12HourClock, - kKeepAmPm)); - EXPECT_EQ(clock12h, - TimeFormatTimeOfDayWithHourClockType(time, - k12HourClock, - kDropAmPm)); -} - -TEST(TimeFormattingTest, TimeFormatTimeOfDayDefault24h) { - // Test for a locale defaulted to 24h clock. - // As an instance, we use third_party/icu/source/data/locales/en_GB.txt. - test::ScopedRestoreICUDefaultLocale restore_locale; - i18n::SetICUDefaultLocale("en_GB"); - ScopedRestoreDefaultTimezone la_time("America/Los_Angeles"); - - Time time; - EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time)); - string16 clock24h(ASCIIToUTF16("15:42")); - string16 clock12h_pm(ASCIIToUTF16("3:42 pm")); - string16 clock12h(ASCIIToUTF16("3:42")); - string16 clock24h_millis(ASCIIToUTF16("15:42:07.000")); - - // The default is 24h clock. - EXPECT_EQ(clock24h, TimeFormatTimeOfDay(time)); - EXPECT_EQ(clock24h_millis, TimeFormatTimeOfDayWithMilliseconds(time)); - EXPECT_EQ(k24HourClock, GetHourClockType()); - // k{Keep,Drop}AmPm should not affect for 24h clock. - EXPECT_EQ(clock24h, - TimeFormatTimeOfDayWithHourClockType(time, - k24HourClock, - kKeepAmPm)); - EXPECT_EQ(clock24h, - TimeFormatTimeOfDayWithHourClockType(time, - k24HourClock, - kDropAmPm)); - // k{Keep,Drop}AmPm affects for 12h clock. - EXPECT_EQ(clock12h_pm, - TimeFormatTimeOfDayWithHourClockType(time, - k12HourClock, - kKeepAmPm)); - EXPECT_EQ(clock12h, - TimeFormatTimeOfDayWithHourClockType(time, - k12HourClock, - kDropAmPm)); -} - -TEST(TimeFormattingTest, TimeFormatTimeOfDayJP) { - // Test for a locale that uses different mark than "AM" and "PM". - // As an instance, we use third_party/icu/source/data/locales/ja.txt. - test::ScopedRestoreICUDefaultLocale restore_locale; - i18n::SetICUDefaultLocale("ja_JP"); - ScopedRestoreDefaultTimezone la_time("America/Los_Angeles"); - - Time time; - EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time)); - string16 clock24h(ASCIIToUTF16("15:42")); - string16 clock12h_pm(UTF8ToUTF16(u8"午後3:42")); - string16 clock12h(ASCIIToUTF16("3:42")); - - // The default is 24h clock. - EXPECT_EQ(clock24h, TimeFormatTimeOfDay(time)); - EXPECT_EQ(k24HourClock, GetHourClockType()); - // k{Keep,Drop}AmPm should not affect for 24h clock. - EXPECT_EQ(clock24h, TimeFormatTimeOfDayWithHourClockType(time, k24HourClock, - kKeepAmPm)); - EXPECT_EQ(clock24h, TimeFormatTimeOfDayWithHourClockType(time, k24HourClock, - kDropAmPm)); - // k{Keep,Drop}AmPm affects for 12h clock. - EXPECT_EQ(clock12h_pm, TimeFormatTimeOfDayWithHourClockType( - time, k12HourClock, kKeepAmPm)); - EXPECT_EQ(clock12h, TimeFormatTimeOfDayWithHourClockType(time, k12HourClock, - kDropAmPm)); -} - -TEST(TimeFormattingTest, TimeFormatTimeOfDayDE) { - // German uses 24h by default, but uses 'AM', 'PM' for 12h format. - test::ScopedRestoreICUDefaultLocale restore_locale; - i18n::SetICUDefaultLocale("de"); - ScopedRestoreDefaultTimezone la_time("America/Los_Angeles"); - - Time time; - EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time)); - string16 clock24h(ASCIIToUTF16("15:42")); - string16 clock12h_pm(UTF8ToUTF16("3:42 PM")); - string16 clock12h(ASCIIToUTF16("3:42")); - - // The default is 24h clock. - EXPECT_EQ(clock24h, TimeFormatTimeOfDay(time)); - EXPECT_EQ(k24HourClock, GetHourClockType()); - // k{Keep,Drop}AmPm should not affect for 24h clock. - EXPECT_EQ(clock24h, - TimeFormatTimeOfDayWithHourClockType(time, - k24HourClock, - kKeepAmPm)); - EXPECT_EQ(clock24h, - TimeFormatTimeOfDayWithHourClockType(time, - k24HourClock, - kDropAmPm)); - // k{Keep,Drop}AmPm affects for 12h clock. - EXPECT_EQ(clock12h_pm, - TimeFormatTimeOfDayWithHourClockType(time, - k12HourClock, - kKeepAmPm)); - EXPECT_EQ(clock12h, - TimeFormatTimeOfDayWithHourClockType(time, - k12HourClock, - kDropAmPm)); -} - -TEST(TimeFormattingTest, TimeFormatDateUS) { - // See third_party/icu/source/data/locales/en.txt. - // The date patterns are "EEEE, MMMM d, y", "MMM d, y", and "M/d/yy". - test::ScopedRestoreICUDefaultLocale restore_locale; - i18n::SetICUDefaultLocale("en_US"); - ScopedRestoreDefaultTimezone la_time("America/Los_Angeles"); - - Time time; - EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time)); - - EXPECT_EQ(ASCIIToUTF16("Apr 30, 2011"), TimeFormatShortDate(time)); - EXPECT_EQ(ASCIIToUTF16("4/30/11"), TimeFormatShortDateNumeric(time)); - - EXPECT_EQ(ASCIIToUTF16("4/30/11, 3:42:07 PM"), - TimeFormatShortDateAndTime(time)); - EXPECT_EQ(ASCIIToUTF16("4/30/11, 3:42:07 PM ") + GetShortTimeZone(time), - TimeFormatShortDateAndTimeWithTimeZone(time)); - - EXPECT_EQ(ASCIIToUTF16("April 2011"), TimeFormatMonthAndYear(time)); - - EXPECT_EQ(ASCIIToUTF16("Saturday, April 30, 2011 at 3:42:07 PM"), - TimeFormatFriendlyDateAndTime(time)); - - EXPECT_EQ(ASCIIToUTF16("Saturday, April 30, 2011"), - TimeFormatFriendlyDate(time)); -} - -TEST(TimeFormattingTest, TimeFormatDateGB) { - // See third_party/icu/source/data/locales/en_GB.txt. - // The date patterns are "EEEE, d MMMM y", "d MMM y", and "dd/MM/yyyy". - test::ScopedRestoreICUDefaultLocale restore_locale; - i18n::SetICUDefaultLocale("en_GB"); - ScopedRestoreDefaultTimezone la_time("America/Los_Angeles"); - - Time time; - EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time)); - - EXPECT_EQ(ASCIIToUTF16("30 Apr 2011"), TimeFormatShortDate(time)); - EXPECT_EQ(ASCIIToUTF16("30/04/2011"), TimeFormatShortDateNumeric(time)); - EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07"), - TimeFormatShortDateAndTime(time)); - EXPECT_EQ(ASCIIToUTF16("30/04/2011, 15:42:07 ") + GetShortTimeZone(time), - TimeFormatShortDateAndTimeWithTimeZone(time)); - EXPECT_EQ(ASCIIToUTF16("April 2011"), TimeFormatMonthAndYear(time)); - EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011 at 15:42:07"), - TimeFormatFriendlyDateAndTime(time)); - EXPECT_EQ(ASCIIToUTF16("Saturday, 30 April 2011"), - TimeFormatFriendlyDate(time)); -} - -TEST(TimeFormattingTest, TimeFormatWithPattern) { - test::ScopedRestoreICUDefaultLocale restore_locale; - ScopedRestoreDefaultTimezone la_time("America/Los_Angeles"); - - Time time; - EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &time)); - - i18n::SetICUDefaultLocale("en_US"); - EXPECT_EQ(ASCIIToUTF16("Apr 30, 2011"), TimeFormatWithPattern(time, "yMMMd")); - EXPECT_EQ(ASCIIToUTF16("April 30, 3:42:07 PM"), - TimeFormatWithPattern(time, "MMMMdjmmss")); - - i18n::SetICUDefaultLocale("en_GB"); - EXPECT_EQ(ASCIIToUTF16("30 Apr 2011"), TimeFormatWithPattern(time, "yMMMd")); - EXPECT_EQ(ASCIIToUTF16("30 April, 15:42:07"), - TimeFormatWithPattern(time, "MMMMdjmmss")); - - i18n::SetICUDefaultLocale("ja_JP"); - EXPECT_EQ(UTF8ToUTF16(u8"2011年4月30日"), - TimeFormatWithPattern(time, "yMMMd")); - EXPECT_EQ(UTF8ToUTF16(u8"4月30日 15:42:07"), - TimeFormatWithPattern(time, "MMMMdjmmss")); -} - -TEST(TimeFormattingTest, TimeDurationFormat) { - test::ScopedRestoreICUDefaultLocale restore_locale; - TimeDelta delta = TimeDelta::FromMinutes(15 * 60 + 42); - - // US English. - i18n::SetICUDefaultLocale("en_US"); - EXPECT_EQ(ASCIIToUTF16("15 hours, 42 minutes"), - TimeDurationFormatString(delta, DURATION_WIDTH_WIDE)); - EXPECT_EQ(ASCIIToUTF16("15 hr, 42 min"), - TimeDurationFormatString(delta, DURATION_WIDTH_SHORT)); - EXPECT_EQ(ASCIIToUTF16("15h 42m"), - TimeDurationFormatString(delta, DURATION_WIDTH_NARROW)); - EXPECT_EQ(ASCIIToUTF16("15:42"), - TimeDurationFormatString(delta, DURATION_WIDTH_NUMERIC)); - - // Danish, with Latin alphabet but different abbreviations and punctuation. - i18n::SetICUDefaultLocale("da"); - EXPECT_EQ(ASCIIToUTF16("15 timer og 42 minutter"), - TimeDurationFormatString(delta, DURATION_WIDTH_WIDE)); - EXPECT_EQ(ASCIIToUTF16("15 t og 42 min."), - TimeDurationFormatString(delta, DURATION_WIDTH_SHORT)); - EXPECT_EQ(ASCIIToUTF16("15 t og 42 min"), - TimeDurationFormatString(delta, DURATION_WIDTH_NARROW)); - EXPECT_EQ(ASCIIToUTF16("15.42"), - TimeDurationFormatString(delta, DURATION_WIDTH_NUMERIC)); - - // Persian, with non-Arabic numbers. - i18n::SetICUDefaultLocale("fa"); - string16 fa_wide = UTF8ToUTF16( - u8"\u06f1\u06f5 \u0633\u0627\u0639\u062a \u0648 \u06f4\u06f2 \u062f\u0642" - u8"\u06cc\u0642\u0647"); - string16 fa_short = UTF8ToUTF16( - u8"\u06f1\u06f5 \u0633\u0627\u0639\u062a\u060c\u200f \u06f4\u06f2 \u062f" - u8"\u0642\u06cc\u0642\u0647"); - string16 fa_narrow = UTF8ToUTF16( - u8"\u06f1\u06f5 \u0633\u0627\u0639\u062a \u06f4\u06f2 \u062f\u0642\u06cc" - u8"\u0642\u0647"); - string16 fa_numeric = UTF8ToUTF16(u8"\u06f1\u06f5:\u06f4\u06f2"); - EXPECT_EQ(fa_wide, TimeDurationFormatString(delta, DURATION_WIDTH_WIDE)); - EXPECT_EQ(fa_short, TimeDurationFormatString(delta, DURATION_WIDTH_SHORT)); - EXPECT_EQ(fa_narrow, TimeDurationFormatString(delta, DURATION_WIDTH_NARROW)); - EXPECT_EQ(fa_numeric, - TimeDurationFormatString(delta, DURATION_WIDTH_NUMERIC)); -} - -TEST(TimeFormattingTest, TimeDurationFormatWithSeconds) { - test::ScopedRestoreICUDefaultLocale restore_locale; - - // US English. - i18n::SetICUDefaultLocale("en_US"); - - // Test different formats. - TimeDelta delta = TimeDelta::FromSeconds(15 * 3600 + 42 * 60 + 30); - EXPECT_EQ(ASCIIToUTF16("15 hours, 42 minutes, 30 seconds"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE)); - EXPECT_EQ(ASCIIToUTF16("15 hr, 42 min, 30 sec"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT)); - EXPECT_EQ(ASCIIToUTF16("15h 42m 30s"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW)); - EXPECT_EQ(ASCIIToUTF16("15:42:30"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NUMERIC)); - - // Test edge case when hour >= 100. - delta = TimeDelta::FromSeconds(125 * 3600 + 42 * 60 + 30); - EXPECT_EQ(ASCIIToUTF16("125 hours, 42 minutes, 30 seconds"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE)); - EXPECT_EQ(ASCIIToUTF16("125 hr, 42 min, 30 sec"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT)); - EXPECT_EQ(ASCIIToUTF16("125h 42m 30s"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW)); - - // Test edge case when minute = 0. - delta = TimeDelta::FromSeconds(15 * 3600 + 0 * 60 + 30); - EXPECT_EQ(ASCIIToUTF16("15 hours, 0 minutes, 30 seconds"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE)); - EXPECT_EQ(ASCIIToUTF16("15 hr, 0 min, 30 sec"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT)); - EXPECT_EQ(ASCIIToUTF16("15h 0m 30s"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW)); - EXPECT_EQ(ASCIIToUTF16("15:00:30"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NUMERIC)); - - // Test edge case when second = 0. - delta = TimeDelta::FromSeconds(15 * 3600 + 42 * 60 + 0); - EXPECT_EQ(ASCIIToUTF16("15 hours, 42 minutes, 0 seconds"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_WIDE)); - EXPECT_EQ(ASCIIToUTF16("15 hr, 42 min, 0 sec"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_SHORT)); - EXPECT_EQ(ASCIIToUTF16("15h 42m 0s"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NARROW)); - EXPECT_EQ(ASCIIToUTF16("15:42:00"), - TimeDurationFormatWithSecondsString(delta, DURATION_WIDTH_NUMERIC)); -} - -TEST(TimeFormattingTest, TimeIntervalFormat) { - test::ScopedRestoreICUDefaultLocale restore_locale; - i18n::SetICUDefaultLocale("en_US"); - ScopedRestoreDefaultTimezone la_time("America/Los_Angeles"); - - const Time::Exploded kTestIntervalEndTimeExploded = { - 2011, 5, 6, 28, // Sat, May 28, 2012 - 22, 42, 7, 0 // 22:42:07.000 - }; - - Time begin_time; - EXPECT_TRUE(Time::FromUTCExploded(kTestDateTimeExploded, &begin_time)); - Time end_time; - EXPECT_TRUE(Time::FromUTCExploded(kTestIntervalEndTimeExploded, &end_time)); - - EXPECT_EQ( - UTF8ToUTF16(u8"Saturday, April 30 – Saturday, May 28"), - DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY)); - - const Time::Exploded kTestIntervalBeginTimeExploded = { - 2011, 5, 1, 16, // Mon, May 16, 2012 - 22, 42, 7, 0 // 22:42:07.000 - }; - EXPECT_TRUE( - Time::FromUTCExploded(kTestIntervalBeginTimeExploded, &begin_time)); - EXPECT_EQ( - UTF8ToUTF16(u8"Monday, May 16 – Saturday, May 28"), - DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY)); - - i18n::SetICUDefaultLocale("en_GB"); - EXPECT_EQ( - UTF8ToUTF16(u8"Monday 16 – Saturday 28 May"), - DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY)); - - i18n::SetICUDefaultLocale("ja"); - EXPECT_EQ( - UTF8ToUTF16(u8"5月16日(月曜日)~28日(土曜日)"), - DateIntervalFormat(begin_time, end_time, DATE_FORMAT_MONTH_WEEKDAY_DAY)); -} - -} // namespace -} // namespace base diff --git a/i18n/timezone.cc b/i18n/timezone.cc deleted file mode 100644 index 8624e07e7..000000000 --- a/i18n/timezone.cc +++ /dev/null @@ -1,34 +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/i18n/timezone.h" - -#include -#include - -#include "third_party/icu/source/common/unicode/unistr.h" -#include "third_party/icu/source/i18n/unicode/timezone.h" - -namespace base { - -std::string CountryCodeForCurrentTimezone() { - std::unique_ptr zone(icu::TimeZone::createDefault()); - icu::UnicodeString id; - // ICU returns '001' (world) for Etc/GMT. Preserve the old behavior - // only for Etc/GMT while returning an empty string for Etc/UTC and - // Etc/UCT because they're less likely to be chosen by mistake in UK in - // place of Europe/London (Briitish Time). - if (zone->getID(id) == UNICODE_STRING_SIMPLE("Etc/GMT")) - return "GB"; - char region_code[4]; - UErrorCode status = U_ZERO_ERROR; - int length = zone->getRegion(id, region_code, 4, status); - // Return an empty string if region_code is a 3-digit numeric code such - // as 001 (World) for Etc/UTC, Etc/UCT. - return (U_SUCCESS(status) && length == 2) - ? std::string(region_code, static_cast(length)) - : std::string(); -} - -} // namespace base diff --git a/i18n/timezone.h b/i18n/timezone.h deleted file mode 100644 index 7557d44f3..000000000 --- a/i18n/timezone.h +++ /dev/null @@ -1,24 +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_I18N_TIMEZONE_H_ -#define BASE_I18N_TIMEZONE_H_ - -#include - -#include "base/i18n/base_i18n_export.h" - -namespace base { - -// Checks the system timezone and turns it into a two-character ISO 3166 country -// code. This may fail (for example, it used to always fail on Android), in -// which case it will return an empty string. It'll also return an empty string -// when the timezone is Etc/UTC or Etc/UCT, but will return 'GB" for Etc/GMT -// because people in the UK tends to select Etc/GMT by mistake instead of -// Europe/London (British Time). -BASE_I18N_EXPORT std::string CountryCodeForCurrentTimezone(); - -} // namespace base - -#endif // BASE_I18N_TIMEZONE_H_ diff --git a/i18n/timezone_unittest.cc b/i18n/timezone_unittest.cc deleted file mode 100644 index 57467dced..000000000 --- a/i18n/timezone_unittest.cc +++ /dev/null @@ -1,27 +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/i18n/timezone.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -TEST(TimezoneTest, CountryCodeForCurrentTimezone) { - std::string country_code = CountryCodeForCurrentTimezone(); - // On some systems (such as Android or some flavors of Linux), ICU may come up - // empty. With https://chromium-review.googlesource.com/c/512282/ , ICU will - // not fail any more. See also http://bugs.icu-project.org/trac/ticket/13208 . - // Even with that, ICU returns '001' (world) for region-agnostic timezones - // such as Etc/UTC and |CountryCodeForCurrentTimezone| returns an empty - // string so that the next fallback can be tried by a customer. - // TODO(jshin): Revise this to test for actual timezones using - // use ScopedRestoreICUDefaultTimezone. - if (!country_code.empty()) - EXPECT_EQ(2U, country_code.size()) << "country_code = " << country_code; -} - -} // namespace -} // namespace base diff --git a/i18n/unicodestring.h b/i18n/unicodestring.h deleted file mode 100644 index b62c5264d..000000000 --- a/i18n/unicodestring.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2017 The Chromium 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_UNICODESTRING_H_ -#define BASE_I18N_UNICODESTRING_H_ - -#include "base/strings/string16.h" -#include "third_party/icu/source/common/unicode/unistr.h" -#include "third_party/icu/source/common/unicode/uvernum.h" - -#if U_ICU_VERSION_MAJOR_NUM >= 59 -#include "third_party/icu/source/common/unicode/char16ptr.h" -#endif - -namespace base { -namespace i18n { - -inline string16 UnicodeStringToString16(const icu::UnicodeString& unistr) { -#if U_ICU_VERSION_MAJOR_NUM >= 59 - return base::string16(icu::toUCharPtr(unistr.getBuffer()), - static_cast(unistr.length())); -#else - return base::string16(unistr.getBuffer(), - static_cast(unistr.length())); -#endif -} - -} // namespace i18n -} // namespace base - -#endif // BASE_UNICODESTRING_H_ diff --git a/i18n/utf8_validator_tables.cc b/i18n/utf8_validator_tables.cc deleted file mode 100644 index 913afc77c..000000000 --- a/i18n/utf8_validator_tables.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file is auto-generated by build_utf8_validator_tables. -// DO NOT EDIT. - -#include "base/i18n/utf8_validator_tables.h" - -namespace base { -namespace internal { - -const uint8_t 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/i18n/utf8_validator_tables.h b/i18n/utf8_validator_tables.h deleted file mode 100644 index 939616b80..000000000 --- a/i18n/utf8_validator_tables.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_I18N_UTF8_VALIDATOR_TABLES_H_ -#define BASE_I18N_UTF8_VALIDATOR_TABLES_H_ - -#include -#include - -#include "base/macros.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_t 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/ios/OWNERS b/ios/OWNERS deleted file mode 100644 index bdb59ec47..000000000 --- a/ios/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -eugenebut@chromium.org -rohitrao@chromium.org -sdefresne@chromium.org diff --git a/ios/block_types.h b/ios/block_types.h deleted file mode 100644 index e4dde798a..000000000 --- a/ios/block_types.h +++ /dev/null @@ -1,14 +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_IOS_BLOCK_TYPES_H_ -#define BASE_IOS_BLOCK_TYPES_H_ - -// A generic procedural block type that takes no arguments and returns nothing. -typedef void (^ProceduralBlock)(void); - -// A block that takes no arguments and returns a bool. -typedef bool (^ConditionBlock)(void); - -#endif // BASE_IOS_BLOCK_TYPES_H_ diff --git a/ios/crb_protocol_observers.h b/ios/crb_protocol_observers.h deleted file mode 100644 index 8ff587883..000000000 --- a/ios/crb_protocol_observers.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_IOS_CRB_PROTOCOL_OBSERVERS_H_ -#define BASE_IOS_CRB_PROTOCOL_OBSERVERS_H_ - -#import - -typedef void (^ExecutionWithObserverBlock)(id); - -// Implements a container for observers that implement a specific Objective-C -// protocol. The container forwards method invocations to its contained -// observers, so that sending a message to all the observers is as simple as -// sending the message to the container. -// It is safe for an observer to remove itself or another observer while being -// notified. It is also safe to add an other observer while being notified but -// the newly added observer will not be notified as part of the current -// notification dispatch. -@interface CRBProtocolObservers : NSObject - -// The Objective-C protocol that the observers in this container conform to. -@property(nonatomic, readonly) Protocol* protocol; - -// Returns a CRBProtocolObservers container for observers that conform to -// |protocol|. -+ (instancetype)observersWithProtocol:(Protocol*)protocol; - -// Adds |observer| to this container. -- (void)addObserver:(id)observer; - -// Remove |observer| from this container. -- (void)removeObserver:(id)observer; - -// Returns true if there are currently no observers. -- (BOOL)empty; - -// Executes callback on every observer. |callback| cannot be nil. -- (void)executeOnObservers:(ExecutionWithObserverBlock)callback; - -@end - -#endif // BASE_IOS_CRB_PROTOCOL_OBSERVERS_H_ diff --git a/ios/crb_protocol_observers.mm b/ios/crb_protocol_observers.mm deleted file mode 100644 index 1a3b9f73d..000000000 --- a/ios/crb_protocol_observers.mm +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "base/ios/crb_protocol_observers.h" - -#include -#include -#include -#include - -#include "base/logging.h" -#include "base/mac/scoped_nsobject.h" -#include "base/stl_util.h" - -@interface CRBProtocolObservers () { - base::scoped_nsobject _protocol; - // ivars declared here are private to the implementation but must be - // public for allowing the C++ |Iterator| class access to those ivars. - @public - // vector of weak pointers to observers. - std::vector<__unsafe_unretained id> _observers; - // The nested level of observer iteration. - // A depth of 0 means nobody is currently iterating on the list of observers. - int _invocationDepth; -} - -// Removes nil observers from the list and is called when the -// |_invocationDepth| reaches 0. -- (void)compact; - -@end - -namespace { - -class Iterator { - public: - explicit Iterator(CRBProtocolObservers* protocol_observers); - ~Iterator(); - id GetNext(); - - private: - CRBProtocolObservers* protocol_observers_; - size_t index_; - size_t max_index_; -}; - -Iterator::Iterator(CRBProtocolObservers* protocol_observers) - : protocol_observers_(protocol_observers), - index_(0), - max_index_(protocol_observers->_observers.size()) { - DCHECK(protocol_observers_); - ++protocol_observers->_invocationDepth; -} - -Iterator::~Iterator() { - if (protocol_observers_ && --protocol_observers_->_invocationDepth == 0) - [protocol_observers_ compact]; -} - -id Iterator::GetNext() { - if (!protocol_observers_) - return nil; - auto& observers = protocol_observers_->_observers; - // Skip nil elements. - size_t max_index = std::min(max_index_, observers.size()); - while (index_ < max_index && !observers[index_]) - ++index_; - return index_ < max_index ? observers[index_++] : nil; -} -} - -@interface CRBProtocolObservers () - -// Designated initializer. -- (id)initWithProtocol:(Protocol*)protocol; - -@end - -@implementation CRBProtocolObservers - -+ (instancetype)observersWithProtocol:(Protocol*)protocol { - return [[[self alloc] initWithProtocol:protocol] autorelease]; -} - -- (id)init { - NOTREACHED(); - return nil; -} - -- (id)initWithProtocol:(Protocol*)protocol { - self = [super init]; - if (self) { - _protocol.reset([protocol retain]); - } - return self; -} - -- (Protocol*)protocol { - return _protocol.get(); -} - -- (void)addObserver:(id)observer { - DCHECK(observer); - DCHECK([observer conformsToProtocol:self.protocol]); - - if (base::ContainsValue(_observers, observer)) - return; - - _observers.push_back(observer); -} - -- (void)removeObserver:(id)observer { - DCHECK(observer); - auto it = std::find(_observers.begin(), _observers.end(), observer); - if (it != _observers.end()) { - if (_invocationDepth) - *it = nil; - else - _observers.erase(it); - } -} - -- (BOOL)empty { - int count = 0; - for (id observer : _observers) { - if (observer != nil) - ++count; - } - return count == 0; -} - -#pragma mark - NSObject - -- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector { - NSMethodSignature* signature = [super methodSignatureForSelector:selector]; - if (signature) - return signature; - - // Look for a required method in the protocol. protocol_getMethodDescription - // returns a struct whose fields are null if a method for the selector was - // not found. - struct objc_method_description description = - protocol_getMethodDescription(self.protocol, selector, YES, YES); - if (description.types) - return [NSMethodSignature signatureWithObjCTypes:description.types]; - - // Look for an optional method in the protocol. - description = protocol_getMethodDescription(self.protocol, selector, NO, YES); - if (description.types) - return [NSMethodSignature signatureWithObjCTypes:description.types]; - - // There is neither a required nor optional method with this selector in the - // protocol, so invoke -[NSObject doesNotRecognizeSelector:] to raise - // NSInvalidArgumentException. - [self doesNotRecognizeSelector:selector]; - return nil; -} - -- (void)forwardInvocation:(NSInvocation*)invocation { - DCHECK(invocation); - if (_observers.empty()) - return; - SEL selector = [invocation selector]; - Iterator it(self); - id observer; - while ((observer = it.GetNext()) != nil) { - if ([observer respondsToSelector:selector]) - [invocation invokeWithTarget:observer]; - } -} - -- (void)executeOnObservers:(ExecutionWithObserverBlock)callback { - DCHECK(callback); - if (_observers.empty()) - return; - Iterator it(self); - id observer; - while ((observer = it.GetNext()) != nil) - callback(observer); -} - -#pragma mark - Private - -- (void)compact { - DCHECK(!_invocationDepth); - _observers.erase(std::remove(_observers.begin(), _observers.end(), nil), - _observers.end()); -} - -@end diff --git a/ios/crb_protocol_observers_unittest.mm b/ios/crb_protocol_observers_unittest.mm deleted file mode 100644 index 07f5cff03..000000000 --- a/ios/crb_protocol_observers_unittest.mm +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "base/ios/crb_protocol_observers.h" -#include "base/ios/weak_nsobject.h" -#include "base/logging.h" -#include "base/mac/scoped_nsautorelease_pool.h" -#include "base/mac/scoped_nsobject.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/gtest_mac.h" -#include "testing/platform_test.h" - -@protocol TestObserver - -@required -- (void)requiredMethod; -- (void)reset; - -@optional -- (void)optionalMethod; -- (void)mutateByAddingObserver:(id)observer; -- (void)mutateByRemovingObserver:(id)observer; -- (void)nestedMutateByAddingObserver:(id)observer; -- (void)nestedMutateByRemovingObserver:(id)observer; - -@end - -// Implements only the required methods in the TestObserver protocol. -@interface TestPartialObserver : NSObject -@property(nonatomic, readonly) BOOL requiredMethodInvoked; -@end - -// Implements all the methods in the TestObserver protocol. -@interface TestCompleteObserver : TestPartialObserver -@property(nonatomic, readonly) BOOL optionalMethodInvoked; -@end - -@interface TestMutateObserver : TestCompleteObserver -- (instancetype)initWithObserver:(CRBProtocolObservers*)observer - NS_DESIGNATED_INITIALIZER; -- (instancetype)init NS_UNAVAILABLE; -@end - -namespace { - -class CRBProtocolObserversTest : public PlatformTest { - public: - CRBProtocolObserversTest() {} - - protected: - void SetUp() override { - PlatformTest::SetUp(); - - observers_.reset([[CRBProtocolObservers observersWithProtocol: - @protocol(TestObserver)] retain]); - - partial_observer_.reset([[TestPartialObserver alloc] init]); - EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); - - complete_observer_.reset([[TestCompleteObserver alloc] init]); - EXPECT_FALSE([complete_observer_ requiredMethodInvoked]); - EXPECT_FALSE([complete_observer_ optionalMethodInvoked]); - - mutate_observer_.reset( - [[TestMutateObserver alloc] initWithObserver:observers_.get()]); - EXPECT_FALSE([mutate_observer_ requiredMethodInvoked]); - } - - base::scoped_nsobject observers_; - base::scoped_nsobject partial_observer_; - base::scoped_nsobject complete_observer_; - base::scoped_nsobject mutate_observer_; -}; - -// Verifies basic functionality of -[CRBProtocolObservers addObserver:] and -// -[CRBProtocolObservers removeObserver:]. -TEST_F(CRBProtocolObserversTest, AddRemoveObserver) { - // Add an observer and verify that the CRBProtocolObservers instance forwards - // an invocation to it. - [observers_ addObserver:partial_observer_]; - [observers_ requiredMethod]; - EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); - - [partial_observer_ reset]; - EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); - - // Remove the observer and verify that the CRBProtocolObservers instance no - // longer forwards an invocation to it. - [observers_ removeObserver:partial_observer_]; - [observers_ requiredMethod]; - EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); -} - -// Verifies that CRBProtocolObservers correctly forwards the invocation of a -// required method in the protocol. -TEST_F(CRBProtocolObserversTest, RequiredMethods) { - [observers_ addObserver:partial_observer_]; - [observers_ addObserver:complete_observer_]; - [observers_ requiredMethod]; - EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); - EXPECT_TRUE([complete_observer_ requiredMethodInvoked]); -} - -// Verifies that CRBProtocolObservers correctly forwards the invocation of an -// optional method in the protocol. -TEST_F(CRBProtocolObserversTest, OptionalMethods) { - [observers_ addObserver:partial_observer_]; - [observers_ addObserver:complete_observer_]; - [observers_ optionalMethod]; - EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); - EXPECT_FALSE([complete_observer_ requiredMethodInvoked]); - EXPECT_TRUE([complete_observer_ optionalMethodInvoked]); -} - -// Verifies that CRBProtocolObservers only holds a weak reference to an -// observer. -TEST_F(CRBProtocolObserversTest, WeakReference) { - base::WeakNSObject weak_observer( - partial_observer_); - EXPECT_TRUE(weak_observer); - - [observers_ addObserver:partial_observer_]; - - { - // Need an autorelease pool here, because - // -[CRBProtocolObservers forwardInvocation:] creates a temporary - // autoreleased array that holds all the observers. - base::mac::ScopedNSAutoreleasePool pool; - [observers_ requiredMethod]; - EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); - } - - partial_observer_.reset(); - EXPECT_FALSE(weak_observer.get()); -} - -// Verifies that an observer can safely remove itself as observer while being -// notified. -TEST_F(CRBProtocolObserversTest, SelfMutateObservers) { - [observers_ addObserver:mutate_observer_]; - EXPECT_FALSE([observers_ empty]); - - [observers_ requiredMethod]; - EXPECT_TRUE([mutate_observer_ requiredMethodInvoked]); - - [mutate_observer_ reset]; - - [observers_ nestedMutateByRemovingObserver:mutate_observer_]; - EXPECT_FALSE([mutate_observer_ requiredMethodInvoked]); - - [observers_ addObserver:partial_observer_]; - - [observers_ requiredMethod]; - EXPECT_FALSE([mutate_observer_ requiredMethodInvoked]); - EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); - - [observers_ removeObserver:partial_observer_]; - EXPECT_TRUE([observers_ empty]); -} - -// Verifies that - [CRBProtocolObservers addObserver:] and -// - [CRBProtocolObservers removeObserver:] can be called while methods are -// being forwarded. -TEST_F(CRBProtocolObserversTest, MutateObservers) { - // Indirectly add an observer while forwarding an observer method. - [observers_ addObserver:mutate_observer_]; - - [observers_ mutateByAddingObserver:partial_observer_]; - EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); - - // Check that methods are correctly forwared to the indirectly added observer. - [mutate_observer_ reset]; - [observers_ requiredMethod]; - EXPECT_TRUE([mutate_observer_ requiredMethodInvoked]); - EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); - - [mutate_observer_ reset]; - [partial_observer_ reset]; - - // Indirectly remove an observer while forwarding an observer method. - [observers_ mutateByRemovingObserver:partial_observer_]; - - // Check that method is not forwared to the indirectly removed observer. - [observers_ requiredMethod]; - EXPECT_TRUE([mutate_observer_ requiredMethodInvoked]); - EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); -} - -// Verifies that - [CRBProtocolObservers addObserver:] and -// - [CRBProtocolObservers removeObserver:] can be called while methods are -// being forwarded with a nested invocation depth > 0. -TEST_F(CRBProtocolObserversTest, NestedMutateObservers) { - // Indirectly add an observer while forwarding an observer method. - [observers_ addObserver:mutate_observer_]; - - [observers_ nestedMutateByAddingObserver:partial_observer_]; - EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); - - // Check that methods are correctly forwared to the indirectly added observer. - [mutate_observer_ reset]; - [observers_ requiredMethod]; - EXPECT_TRUE([mutate_observer_ requiredMethodInvoked]); - EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); - - [mutate_observer_ reset]; - [partial_observer_ reset]; - - // Indirectly remove an observer while forwarding an observer method. - [observers_ nestedMutateByRemovingObserver:partial_observer_]; - - // Check that method is not forwared to the indirectly removed observer. - [observers_ requiredMethod]; - EXPECT_TRUE([mutate_observer_ requiredMethodInvoked]); - EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); -} - -} // namespace - -@implementation TestPartialObserver { - BOOL _requiredMethodInvoked; -} - -- (BOOL)requiredMethodInvoked { - return _requiredMethodInvoked; -} - -- (void)requiredMethod { - _requiredMethodInvoked = YES; -} - -- (void)reset { - _requiredMethodInvoked = NO; -} - -@end - -@implementation TestCompleteObserver { - BOOL _optionalMethodInvoked; -} - -- (BOOL)optionalMethodInvoked { - return _optionalMethodInvoked; -} - -- (void)optionalMethod { - _optionalMethodInvoked = YES; -} - -- (void)reset { - [super reset]; - _optionalMethodInvoked = NO; -} - -@end - -@implementation TestMutateObserver { - id _observers; // weak -} - -- (instancetype)initWithObserver:(CRBProtocolObservers*)observers { - self = [super init]; - if (self) { - _observers = observers; - } - return self; -} - -- (instancetype)init { - NOTREACHED(); - return nil; -} - -- (void)mutateByAddingObserver:(id)observer { - [_observers addObserver:observer]; -} - -- (void)mutateByRemovingObserver:(id)observer { - [_observers removeObserver:observer]; -} - -- (void)nestedMutateByAddingObserver:(id)observer { - [_observers mutateByAddingObserver:observer]; -} - -- (void)nestedMutateByRemovingObserver:(id)observer { - [_observers mutateByRemovingObserver:observer]; -} - -@end diff --git a/ios/device_util.h b/ios/device_util.h deleted file mode 100644 index b1bed5c70..000000000 --- a/ios/device_util.h +++ /dev/null @@ -1,86 +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_IOS_DEVICE_UTIL_H_ -#define BASE_IOS_DEVICE_UTIL_H_ - -#include - -#include - -namespace ios { -namespace device_util { - -// Returns the hardware version of the device the app is running on. -// -// The returned string is the string returned by sysctlbyname() with name -// "hw.machine". Possible (known) values include: -// -// iPhone1,1 -> iPhone 1G -// iPhone1,2 -> iPhone 3G -// iPhone2,1 -> iPhone 3GS -// iPhone3,1 -> iPhone 4/AT&T -// iPhone3,2 -> iPhone 4/Other Carrier? -// iPhone3,3 -> iPhone 4/Other Carrier? -// iPhone4,1 -> iPhone 4S -// -// iPod1,1 -> iPod touch 1G -// iPod2,1 -> iPod touch 2G -// iPod2,2 -> ? -// iPod3,1 -> iPod touch 3G -// iPod4,1 -> iPod touch 4G -// iPod5,1 -> ? -// -// iPad1,1 -> iPad 1G, WiFi -// iPad1,? -> iPad 1G, 3G <- needs 3G owner to test -// iPad2,1 -> iPad 2G, WiFi -// -// AppleTV2,1 -> AppleTV 2 -// -// i386 -> Simulator -// x86_64 -> Simulator -std::string GetPlatform(); - -// 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(); - -// Returns the MAC address of the interface with name |interface_name|. -std::string GetMacAddress(const std::string& interface_name); - -// Returns a random UUID. -std::string GetRandomId(); - -// Returns an identifier for the device, using the given |salt|. A global -// identifier is generated the first time this method is called, and the salt -// is used to be able to generate distinct identifiers for the same device. If -// |salt| is NULL, a default value is used. Unless you are using this value for -// 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 - -#endif // BASE_IOS_DEVICE_UTIL_H_ diff --git a/ios/device_util.mm b/ios/device_util.mm deleted file mode 100644 index 5ec1e69e2..000000000 --- a/ios/device_util.mm +++ /dev/null @@ -1,178 +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/ios/device_util.h" - -#include -#import -#include -#include -#include -#include -#include -#include - -#include - -#include "base/logging.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/sys_string_conversions.h" - -namespace { - -// Client ID key in the user preferences. -NSString* const kLegacyClientIdPreferenceKey = @"ChromiumClientID"; -NSString* const kClientIdPreferenceKey = @"ChromeClientID"; -// Current hardware type. This is used to detect that a device has been backed -// up and restored to another device, and allows regenerating a new device id. -NSString* const kHardwareTypePreferenceKey = @"ClientIDGenerationHardwareType"; -// Default salt for device ids. -const char kDefaultSalt[] = "Salt"; -// Zero UUID returned on buggy iOS devices. -NSString* const kZeroUUID = @"00000000-0000-0000-0000-000000000000"; - -NSString* GenerateClientId() { - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - - // Try to migrate from legacy client id. - NSString* client_id = [defaults stringForKey:kLegacyClientIdPreferenceKey]; - - // Some iOS6 devices return a buggy identifierForVendor: - // http://openradar.appspot.com/12377282. If this is the case, revert to - // generating a new one. - if (!client_id || [client_id isEqualToString:kZeroUUID]) { - client_id = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; - if ([client_id isEqualToString:kZeroUUID]) - client_id = base::SysUTF8ToNSString(ios::device_util::GetRandomId()); - } - return client_id; -} - -} // namespace - -namespace ios { -namespace device_util { - -std::string GetPlatform() { - std::string platform; - size_t size = 0; - sysctlbyname("hw.machine", NULL, &size, NULL, 0); - sysctlbyname("hw.machine", base::WriteInto(&platform, size), &size, NULL, 0); - return platform; -} - -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 >= ram_in_mb * 1024 * 1024; - } - return false; -} - -bool IsSingleCoreDevice() { - uint64_t cpu_number = 0; - size_t sizes = sizeof(cpu_number); - sysctlbyname("hw.physicalcpu", &cpu_number, &sizes, NULL, 0); - return cpu_number == 1; -} - -std::string GetMacAddress(const std::string& interface_name) { - std::string mac_string; - struct ifaddrs* addresses; - if (getifaddrs(&addresses) == 0) { - for (struct ifaddrs* address = addresses; address; - address = address->ifa_next) { - if ((address->ifa_addr->sa_family == AF_LINK) && - strcmp(interface_name.c_str(), address->ifa_name) == 0) { - const struct sockaddr_dl* found_address_struct = - reinterpret_cast(address->ifa_addr); - - // |found_address_struct->sdl_data| contains the interface name followed - // by the interface address. The address part can be accessed based on - // the length of the name, that is, |found_address_struct->sdl_nlen|. - const unsigned char* found_address = - reinterpret_cast( - &found_address_struct->sdl_data[ - found_address_struct->sdl_nlen]); - - int found_address_length = found_address_struct->sdl_alen; - for (int i = 0; i < found_address_length; ++i) { - if (i != 0) - mac_string.push_back(':'); - base::StringAppendF(&mac_string, "%02X", found_address[i]); - } - break; - } - } - freeifaddrs(addresses); - } - return mac_string; -} - -std::string GetRandomId() { - base::ScopedCFTypeRef uuid_object( - CFUUIDCreate(kCFAllocatorDefault)); - base::ScopedCFTypeRef uuid_string( - CFUUIDCreateString(kCFAllocatorDefault, uuid_object)); - return base::SysCFStringRefToUTF8(uuid_string); -} - -std::string GetDeviceIdentifier(const char* salt) { - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - - NSString* last_seen_hardware = - [defaults stringForKey:kHardwareTypePreferenceKey]; - NSString* current_hardware = base::SysUTF8ToNSString(GetPlatform()); - if (!last_seen_hardware) { - last_seen_hardware = current_hardware; - [defaults setObject:current_hardware forKey:kHardwareTypePreferenceKey]; - [defaults synchronize]; - } - - NSString* client_id = [defaults stringForKey:kClientIdPreferenceKey]; - - if (!client_id || ![last_seen_hardware isEqualToString:current_hardware]) { - client_id = GenerateClientId(); - [defaults setObject:client_id forKey:kClientIdPreferenceKey]; - [defaults setObject:current_hardware forKey:kHardwareTypePreferenceKey]; - [defaults synchronize]; - } - - 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); - CFUUIDBytes* uuid_bytes = reinterpret_cast(hash); - - base::ScopedCFTypeRef uuid_object( - CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, *uuid_bytes)); - base::ScopedCFTypeRef device_id( - CFUUIDCreateString(kCFAllocatorDefault, uuid_object)); - return base::SysCFStringRefToUTF8(device_id); -} - -} // namespace device_util -} // namespace ios diff --git a/ios/device_util_unittest.mm b/ios/device_util_unittest.mm deleted file mode 100644 index 82d421723..000000000 --- a/ios/device_util_unittest.mm +++ /dev/null @@ -1,143 +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 - -#include "base/ios/device_util.h" -#include "base/strings/sys_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/gtest_mac.h" -#include "testing/platform_test.h" - -namespace { -// The behavior of most of these utility functions depends on what they are run -// on, so there is not much to unittest them. The APIs are run to make sure they -// don't choke. Additional checks are added for particular APIs when needed. - -typedef PlatformTest DeviceUtilTest; - -void CleanNSUserDefaultsForDeviceId() { - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - [defaults removeObjectForKey:@"ChromeClientID"]; - [defaults removeObjectForKey:@"ChromiumClientID"]; - [defaults removeObjectForKey:@"ClientIDGenerationHardwareType"]; - [defaults synchronize]; -} - -TEST_F(DeviceUtilTest, GetPlatform) { - GTEST_ASSERT_GT(ios::device_util::GetPlatform().length(), 0U); -} - -TEST_F(DeviceUtilTest, IsSingleCoreDevice) { - ios::device_util::IsSingleCoreDevice(); -} - -TEST_F(DeviceUtilTest, GetMacAddress) { - GTEST_ASSERT_GT(ios::device_util::GetMacAddress("en0").length(), 0U); -} - -TEST_F(DeviceUtilTest, GetRandomId) { - GTEST_ASSERT_GT(ios::device_util::GetRandomId().length(), 0U); -} - -TEST_F(DeviceUtilTest, GetDeviceIdentifier) { - CleanNSUserDefaultsForDeviceId(); - - std::string default_id = ios::device_util::GetDeviceIdentifier(NULL); - std::string other_id = ios::device_util::GetDeviceIdentifier("ForTest"); - EXPECT_NE(default_id, other_id); - - CleanNSUserDefaultsForDeviceId(); - - std::string new_default_id = ios::device_util::GetDeviceIdentifier(NULL); - if (![[[[UIDevice currentDevice] identifierForVendor] UUIDString] - isEqualToString:@"00000000-0000-0000-0000-000000000000"]) { - EXPECT_EQ(default_id, new_default_id); - } else { - EXPECT_NE(default_id, new_default_id); - } - - CleanNSUserDefaultsForDeviceId(); -} - -TEST_F(DeviceUtilTest, CheckMigration) { - CleanNSUserDefaultsForDeviceId(); - - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - [defaults setObject:@"10000000-0000-0000-0000-000000000000" - forKey:@"ChromeClientID"]; - [defaults synchronize]; - std::string expected_id = ios::device_util::GetDeviceIdentifier(NULL); - [defaults removeObjectForKey:@"ChromeClientID"]; - [defaults setObject:@"10000000-0000-0000-0000-000000000000" - forKey:@"ChromiumClientID"]; - [defaults synchronize]; - std::string new_id = ios::device_util::GetDeviceIdentifier(NULL); - EXPECT_EQ(expected_id, new_id); - - CleanNSUserDefaultsForDeviceId(); -} - -TEST_F(DeviceUtilTest, CheckMigrationFromZero) { - CleanNSUserDefaultsForDeviceId(); - - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - [defaults setObject:@"00000000-0000-0000-0000-000000000000" - forKey:@"ChromeClientID"]; - [defaults synchronize]; - std::string zero_id = ios::device_util::GetDeviceIdentifier(NULL); - [defaults removeObjectForKey:@"ChromeClientID"]; - [defaults setObject:@"00000000-0000-0000-0000-000000000000" - forKey:@"ChromiumClientID"]; - [defaults synchronize]; - std::string new_id = ios::device_util::GetDeviceIdentifier(NULL); - EXPECT_NE(zero_id, new_id); - - 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(); - - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - [defaults setObject:@"10000000-0000-0000-0000-000000000000" - forKey:@"ChromeClientID"]; - [defaults synchronize]; - std::string base_id = ios::device_util::GetDeviceIdentifier(NULL); - [defaults setObject:@"Foo" forKey:@"ClientIDGenerationHardwareType"]; - [defaults synchronize]; - std::string new_id = ios::device_util::GetDeviceIdentifier(NULL); - EXPECT_NE(new_id, base_id); - - CleanNSUserDefaultsForDeviceId(); -} - -} // namespace diff --git a/ios/ios_util.h b/ios/ios_util.h deleted file mode 100644 index 91b045afb..000000000 --- a/ios/ios_util.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 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_IOS_IOS_UTIL_H_ -#define BASE_IOS_IOS_UTIL_H_ - -#include - -#include "base/base_export.h" -#include "base/files/file_path.h" - -namespace base { -namespace ios { - -// Returns whether the operating system is iOS 10 or later. -BASE_EXPORT bool IsRunningOnIOS10OrLater(); - -// Returns whether the operating system is iOS 11 or later. -BASE_EXPORT bool IsRunningOnIOS11OrLater(); - -// Returns whether the operating system is iOS 12 or later. -BASE_EXPORT bool IsRunningOnIOS12OrLater(); - -// Returns whether the operating system is at the given version or later. -BASE_EXPORT bool IsRunningOnOrLater(int32_t major, - int32_t minor, - int32_t bug_fix); - -// Returns whether iOS is signalling that an RTL text direction should be used -// regardless of the current locale. This should not return true if the current -// language is a "real" RTL language such as Arabic or Urdu; it should only -// return true in cases where the RTL text direction has been forced (for -// example by using the "RTL Psuedolanguage" option when launching from XCode). -BASE_EXPORT bool IsInForcedRTL(); - -// Stores the |path| of the ICU dat file in a global to be referenced later by -// FilePathOfICUFile(). This should only be called once. -BASE_EXPORT void OverridePathOfEmbeddedICU(const char* path); - -// Returns the overriden path set by OverridePathOfEmbeddedICU(), otherwise -// returns invalid FilePath. -BASE_EXPORT FilePath FilePathOfEmbeddedICU(); - -} // namespace ios -} // namespace base - -#endif // BASE_IOS_IOS_UTIL_H_ diff --git a/ios/ios_util.mm b/ios/ios_util.mm deleted file mode 100644 index eba1b7107..000000000 --- a/ios/ios_util.mm +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 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/ios/ios_util.h" - -#import -#include - -#include "base/macros.h" -#include "base/sys_info.h" - -namespace { - -// Return a 3 elements array containing the major, minor and bug fix version of -// the OS. -const int32_t* OSVersionAsArray() { - int32_t* digits = new int32_t[3]; - base::SysInfo::OperatingSystemVersionNumbers( - &digits[0], &digits[1], &digits[2]); - return digits; -} - -std::string* g_icudtl_path_override = nullptr; - -} // namespace - -namespace base { -namespace ios { - -bool IsRunningOnIOS10OrLater() { - static const bool is_running_on_or_later = IsRunningOnOrLater(10, 0, 0); - return is_running_on_or_later; -} - -bool IsRunningOnIOS11OrLater() { - static const bool is_running_on_or_later = IsRunningOnOrLater(11, 0, 0); - return is_running_on_or_later; -} - -bool IsRunningOnIOS12OrLater() { - static const bool is_running_on_or_later = IsRunningOnOrLater(12, 0, 0); - return is_running_on_or_later; -} - -bool IsRunningOnOrLater(int32_t major, int32_t minor, int32_t bug_fix) { - static const int32_t* current_version = OSVersionAsArray(); - int32_t version[] = {major, minor, bug_fix}; - for (size_t i = 0; i < arraysize(version); i++) { - if (current_version[i] != version[i]) - return current_version[i] > version[i]; - } - return true; -} - -bool IsInForcedRTL() { - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - return [defaults boolForKey:@"NSForceRightToLeftWritingDirection"]; -} - -void OverridePathOfEmbeddedICU(const char* path) { - DCHECK(!g_icudtl_path_override); - g_icudtl_path_override = new std::string(path); -} - -FilePath FilePathOfEmbeddedICU() { - if (g_icudtl_path_override) { - return FilePath(*g_icudtl_path_override); - } - return FilePath(); -} - -} // namespace ios -} // namespace base diff --git a/ios/ns_error_util.h b/ios/ns_error_util.h deleted file mode 100644 index 101229208..000000000 --- a/ios/ns_error_util.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_IOS_NS_ERROR_UTIL_H_ -#define BASE_IOS_NS_ERROR_UTIL_H_ - -@class NSError; - -namespace base { -namespace ios { - -// Iterates through |error|'s underlying errors and returns the first error for -// which there is no underlying error. -NSError* GetFinalUnderlyingErrorFromError(NSError* error); - -// Returns a copy of |original_error| with |underlying_error| appended to the -// end of its underlying error chain. -NSError* ErrorWithAppendedUnderlyingError(NSError* original_error, - NSError* underlying_error); - -} // namespace ios -} // namespace base - -#endif // BASE_IOS_NS_ERROR_UTIL_H_ diff --git a/ios/ns_error_util.mm b/ios/ns_error_util.mm deleted file mode 100644 index c44d9ee41..000000000 --- a/ios/ns_error_util.mm +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "base/ios/ns_error_util.h" - -#import - -#include "base/logging.h" -#include "base/mac/scoped_nsobject.h" - -namespace base { -namespace ios { - -namespace { -// Iterates through |error|'s underlying errors and returns them in an array. -NSArray* GetFullErrorChainForError(NSError* error) { - NSMutableArray* error_chain = [NSMutableArray array]; - NSError* current_error = error; - while (current_error) { - DCHECK([current_error isKindOfClass:[NSError class]]); - [error_chain addObject:current_error]; - current_error = current_error.userInfo[NSUnderlyingErrorKey]; - } - return error_chain; -} -} // namespace - -NSError* GetFinalUnderlyingErrorFromError(NSError* error) { - DCHECK(error); - return [GetFullErrorChainForError(error) lastObject]; -} - -NSError* ErrorWithAppendedUnderlyingError(NSError* original_error, - NSError* underlying_error) { - DCHECK(original_error); - DCHECK(underlying_error); - NSArray* error_chain = GetFullErrorChainForError(original_error); - NSError* current_error = underlying_error; - for (NSInteger idx = error_chain.count - 1; idx >= 0; --idx) { - NSError* error = error_chain[idx]; - scoped_nsobject user_info( - [error.userInfo mutableCopy]); - [user_info setObject:current_error forKey:NSUnderlyingErrorKey]; - current_error = [NSError errorWithDomain:error.domain - code:error.code - userInfo:user_info]; - } - return current_error; -} - -} // namespace ios -} // namespace base diff --git a/ios/scoped_critical_action.h b/ios/scoped_critical_action.h deleted file mode 100644 index 2f7d16c3f..000000000 --- a/ios/scoped_critical_action.h +++ /dev/null @@ -1,73 +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_IOS_SCOPED_CRITICAL_ACTION_H_ -#define BASE_IOS_SCOPED_CRITICAL_ACTION_H_ - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" - -namespace base { -namespace ios { - -// This class attempts to allow the application to continue to run for a period -// of time after it transitions to the background. The construction of an -// instance of this class marks the beginning of a task that needs background -// running time when the application is moved to the background and the -// destruction marks the end of such a task. -// -// Note there is no guarantee that the task will continue to finish when the -// application is moved to the background. -// -// This class should be used at times where leaving a task unfinished might be -// detrimental to user experience. For example, it should be used to ensure that -// the application has enough time to save important data or at least attempt to -// save such data. -class ScopedCriticalAction { - public: - ScopedCriticalAction(); - ~ScopedCriticalAction(); - - private: - // Core logic; ScopedCriticalAction should not be reference counted so - // that it follows the normal pattern of stack-allocating ScopedFoo objects, - // but the expiration handler needs to have a reference counted object to - // refer to. - class Core : public base::RefCountedThreadSafe { - public: - Core(); - - // Informs the OS that the background task has started. This is a - // static method to ensure that the instance has a non-zero refcount. - static void StartBackgroundTask(scoped_refptr core); - // Informs the OS that the background task has completed. This is a - // static method to ensure that the instance has a non-zero refcount. - static void EndBackgroundTask(scoped_refptr core); - - private: - friend base::RefCountedThreadSafe; - ~Core(); - - // |UIBackgroundTaskIdentifier| returned by - // |beginBackgroundTaskWithExpirationHandler:| when marking the beginning of - // a long-running background task. It is defined as an |unsigned int| - // instead of a |UIBackgroundTaskIdentifier| so this class can be used in - // .cc files. - unsigned int background_task_id_; - Lock background_task_id_lock_; - - DISALLOW_COPY_AND_ASSIGN(Core); - }; - - // The instance of the core that drives the background task. - scoped_refptr core_; - - DISALLOW_COPY_AND_ASSIGN(ScopedCriticalAction); -}; - -} // namespace ios -} // namespace base - -#endif // BASE_IOS_SCOPED_CRITICAL_ACTION_H_ diff --git a/ios/scoped_critical_action.mm b/ios/scoped_critical_action.mm deleted file mode 100644 index dbfbd4525..000000000 --- a/ios/scoped_critical_action.mm +++ /dev/null @@ -1,77 +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/ios/scoped_critical_action.h" - -#import - -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" - -namespace base { -namespace ios { - -ScopedCriticalAction::ScopedCriticalAction() - : core_(MakeRefCounted()) { - ScopedCriticalAction::Core::StartBackgroundTask(core_); -} - -ScopedCriticalAction::~ScopedCriticalAction() { - ScopedCriticalAction::Core::EndBackgroundTask(core_); -} - -ScopedCriticalAction::Core::Core() - : background_task_id_(UIBackgroundTaskInvalid) {} - -ScopedCriticalAction::Core::~Core() { - DCHECK_EQ(background_task_id_, UIBackgroundTaskInvalid); -} - -// This implementation calls |beginBackgroundTaskWithExpirationHandler:| when -// instantiated and |endBackgroundTask:| when destroyed, creating a scope whose -// execution will continue (temporarily) even after the app is backgrounded. -// static -void ScopedCriticalAction::Core::StartBackgroundTask(scoped_refptr core) { - UIApplication* application = [UIApplication sharedApplication]; - if (!application) { - return; - } - - core->background_task_id_ = - [application beginBackgroundTaskWithExpirationHandler:^{ - DLOG(WARNING) << "Background task with id " << core->background_task_id_ - << " expired."; - // Note if |endBackgroundTask:| is not called for each task before time - // expires, the system kills the application. - EndBackgroundTask(core); - }]; - - if (core->background_task_id_ == UIBackgroundTaskInvalid) { - DLOG(WARNING) - << "beginBackgroundTaskWithExpirationHandler: returned an invalid ID"; - } else { - VLOG(3) << "Beginning background task with id " - << core->background_task_id_; - } -} - -// static -void ScopedCriticalAction::Core::EndBackgroundTask(scoped_refptr core) { - UIBackgroundTaskIdentifier task_id; - { - AutoLock lock_scope(core->background_task_id_lock_); - if (core->background_task_id_ == UIBackgroundTaskInvalid) { - return; - } - task_id = core->background_task_id_; - core->background_task_id_ = UIBackgroundTaskInvalid; - } - - VLOG(3) << "Ending background task with id " << task_id; - [[UIApplication sharedApplication] endBackgroundTask:task_id]; -} - -} // namespace ios -} // namespace base diff --git a/ios/weak_nsobject.h b/ios/weak_nsobject.h deleted file mode 100644 index 498cdee28..000000000 --- a/ios/weak_nsobject.h +++ /dev/null @@ -1,187 +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_IOS_WEAK_NSOBJECT_H_ -#define BASE_IOS_WEAK_NSOBJECT_H_ - -#import -#import - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/threading/thread_checker.h" - -// WeakNSObject<> is patterned after scoped_nsobject<>, but instead of -// maintaining ownership of an NSObject subclass object, it will nil itself out -// when the object is deallocated. -// -// WeakNSProtocol<> has the same behavior as WeakNSObject, but can be used -// with protocols. -// -// Example usage (base::WeakNSObject): -// scoped_nsobject foo([[Foo alloc] init]); -// WeakNSObject weak_foo; // No pointer -// weak_foo.reset(foo) // Now a weak reference is kept. -// [weak_foo description]; // Returns [foo description]. -// foo.reset(); // The reference is released. -// [weak_foo description]; // Returns nil, as weak_foo is pointing to nil. -// -// -// Implementation wise a WeakNSObject keeps a reference to a refcounted -// WeakContainer. There is one unique instance of a WeakContainer per watched -// NSObject, this relationship is maintained via the ObjectiveC associated -// object API, indirectly via an ObjectiveC CRBWeakNSProtocolSentinel class. -// -// Threading restrictions: -// - Several WeakNSObject pointing to the same underlying object must all be -// created and dereferenced on the same thread; -// - thread safety is enforced by the implementation, except in two cases: -// (1) it is allowed to copy a WeakNSObject on a different thread. However, -// that copy must return to the original thread before being dereferenced, -// (2) it is allowed to destroy a WeakNSObject on any thread; -// - the implementation assumes that the tracked object will be released on the -// same thread that the WeakNSObject is created on. -namespace base { - -// WeakContainer keeps a weak pointer to an object and clears it when it -// receives nullify() from the object's sentinel. -class WeakContainer : public base::RefCountedThreadSafe { - public: - explicit WeakContainer(id object); - - id object() { - DCHECK(checker_.CalledOnValidThread()); - return object_; - } - - void nullify() { - DCHECK(checker_.CalledOnValidThread()); - object_ = nil; - } - - private: - friend base::RefCountedThreadSafe; - ~WeakContainer(); - base::ThreadChecker checker_; - __unsafe_unretained id object_; -}; - -} // namespace base - -// Sentinel for observing the object contained in the weak pointer. The object -// will be deleted when the weak object is deleted and will notify its -// container. -@interface CRBWeakNSProtocolSentinel : NSObject -// Return the only associated container for this object. There can be only one. -// Will return null if object is nil . -+ (scoped_refptr)containerForObject:(id)object; -@end - -namespace base { - -// Base class for all WeakNSObject derivatives. -template -class WeakNSProtocol { - public: - explicit WeakNSProtocol(NST object = nil) { - container_ = [CRBWeakNSProtocolSentinel containerForObject:object]; - } - - WeakNSProtocol(const WeakNSProtocol& that) { - // A WeakNSProtocol object can be copied on one thread and used on - // another. - checker_.DetachFromThread(); - container_ = that.container_; - } - - ~WeakNSProtocol() { - // A WeakNSProtocol object can be used on one thread and released on - // another. This is not the case for the contained object. - checker_.DetachFromThread(); - } - - void reset(NST object = nil) { - DCHECK(checker_.CalledOnValidThread()); - container_ = [CRBWeakNSProtocolSentinel containerForObject:object]; - } - - NST get() const { - DCHECK(checker_.CalledOnValidThread()); - if (!container_.get()) - return nil; - return container_->object(); - } - - WeakNSProtocol& operator=(const WeakNSProtocol& that) { - // A WeakNSProtocol object can be copied on one thread and used on - // another. - checker_.DetachFromThread(); - container_ = that.container_; - return *this; - } - - bool operator==(NST that) const { - DCHECK(checker_.CalledOnValidThread()); - return get() == that; - } - - bool operator!=(NST that) const { - DCHECK(checker_.CalledOnValidThread()); - return get() != that; - } - - operator NST() const { - DCHECK(checker_.CalledOnValidThread()); - return get(); - } - - private: - // Refecounted reference to the container tracking the ObjectiveC object this - // class encapsulates. - scoped_refptr container_; - base::ThreadChecker checker_; -}; - -// Free functions -template -bool operator==(NST p1, const WeakNSProtocol& p2) { - return p1 == p2.get(); -} - -template -bool operator!=(NST p1, const WeakNSProtocol& p2) { - return p1 != p2.get(); -} - -template -class WeakNSObject : public WeakNSProtocol { - public: - explicit WeakNSObject(NST* object = nil) : WeakNSProtocol(object) {} - - WeakNSObject(const WeakNSObject& that) : WeakNSProtocol(that) {} - - WeakNSObject& operator=(const WeakNSObject& that) { - WeakNSProtocol::operator=(that); - return *this; - } -}; - -// Specialization to make WeakNSObject work. -template <> -class WeakNSObject : public WeakNSProtocol { - public: - explicit WeakNSObject(id object = nil) : WeakNSProtocol(object) {} - - WeakNSObject(const WeakNSObject& that) : WeakNSProtocol(that) {} - - WeakNSObject& operator=(const WeakNSObject& that) { - WeakNSProtocol::operator=(that); - return *this; - } -}; - -} // namespace base - -#endif // BASE_IOS_WEAK_NSOBJECT_H_ diff --git a/ios/weak_nsobject.mm b/ios/weak_nsobject.mm deleted file mode 100644 index c017b1d18..000000000 --- a/ios/weak_nsobject.mm +++ /dev/null @@ -1,69 +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/ios/weak_nsobject.h" - -#include "base/mac/scoped_nsautorelease_pool.h" -#include "base/mac/scoped_nsobject.h" - -namespace { -// The key needed by objc_setAssociatedObject. -char sentinelObserverKey_; -} - -namespace base { - -WeakContainer::WeakContainer(id object) : object_(object) {} - -WeakContainer::~WeakContainer() {} - -} // namespace base - -@interface CRBWeakNSProtocolSentinel () -// Container to notify on dealloc. -@property(readonly, assign) scoped_refptr container; -// Designed initializer. -- (id)initWithContainer:(scoped_refptr)container; -@end - -@implementation CRBWeakNSProtocolSentinel - -@synthesize container = container_; - -+ (scoped_refptr)containerForObject:(id)object { - if (object == nil) - return nullptr; - // The autoreleasePool is needed here as the call to objc_getAssociatedObject - // returns an autoreleased object which is better released sooner than later. - base::mac::ScopedNSAutoreleasePool pool; - CRBWeakNSProtocolSentinel* sentinel = - objc_getAssociatedObject(object, &sentinelObserverKey_); - if (!sentinel) { - base::scoped_nsobject newSentinel( - [[CRBWeakNSProtocolSentinel alloc] - initWithContainer:new base::WeakContainer(object)]); - sentinel = newSentinel; - objc_setAssociatedObject(object, &sentinelObserverKey_, sentinel, - OBJC_ASSOCIATION_RETAIN); - // The retain count is 2. One retain is due to the alloc, the other to the - // association with the weak object. - DCHECK_EQ(2u, [sentinel retainCount]); - } - return [sentinel container]; -} - -- (id)initWithContainer:(scoped_refptr)container { - DCHECK(container.get()); - self = [super init]; - if (self) - container_ = container; - return self; -} - -- (void)dealloc { - self.container->nullify(); - [super dealloc]; -} - -@end diff --git a/ios/weak_nsobject_unittest.mm b/ios/weak_nsobject_unittest.mm deleted file mode 100644 index ba85217d2..000000000 --- a/ios/weak_nsobject_unittest.mm +++ /dev/null @@ -1,140 +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/bind.h" -#include "base/ios/weak_nsobject.h" -#include "base/mac/scoped_nsobject.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -TEST(WeakNSObjectTest, WeakNSObject) { - scoped_nsobject p1([[NSObject alloc] init]); - WeakNSObject w1(p1); - EXPECT_TRUE(w1); - p1.reset(); - EXPECT_FALSE(w1); -} - -TEST(WeakNSObjectTest, MultipleWeakNSObject) { - scoped_nsobject p1([[NSObject alloc] init]); - WeakNSObject w1(p1); - WeakNSObject w2(w1); - EXPECT_TRUE(w1); - EXPECT_TRUE(w2); - EXPECT_TRUE(w1.get() == w2.get()); - p1.reset(); - EXPECT_FALSE(w1); - EXPECT_FALSE(w2); -} - -TEST(WeakNSObjectTest, WeakNSObjectDies) { - scoped_nsobject p1([[NSObject alloc] init]); - { - WeakNSObject w1(p1); - EXPECT_TRUE(w1); - } -} - -TEST(WeakNSObjectTest, WeakNSObjectReset) { - scoped_nsobject p1([[NSObject alloc] init]); - WeakNSObject w1(p1); - EXPECT_TRUE(w1); - w1.reset(); - EXPECT_FALSE(w1); - EXPECT_TRUE(p1); - EXPECT_TRUE([p1 description]); -} - -TEST(WeakNSObjectTest, WeakNSObjectResetWithObject) { - scoped_nsobject p1([[NSObject alloc] init]); - scoped_nsobject p2([[NSObject alloc] init]); - WeakNSObject w1(p1); - EXPECT_TRUE(w1); - w1.reset(p2); - EXPECT_TRUE(w1); - EXPECT_TRUE([p1 description]); - EXPECT_TRUE([p2 description]); -} - -TEST(WeakNSObjectTest, WeakNSObjectEmpty) { - scoped_nsobject p1([[NSObject alloc] init]); - WeakNSObject w1; - EXPECT_FALSE(w1); - w1.reset(p1); - EXPECT_TRUE(w1); - p1.reset(); - EXPECT_FALSE(w1); -} - -TEST(WeakNSObjectTest, WeakNSObjectCopy) { - scoped_nsobject p1([[NSObject alloc] init]); - WeakNSObject w1(p1); - WeakNSObject w2(w1); - EXPECT_TRUE(w1); - EXPECT_TRUE(w2); - p1.reset(); - EXPECT_FALSE(w1); - EXPECT_FALSE(w2); -} - -TEST(WeakNSObjectTest, WeakNSObjectAssignment) { - scoped_nsobject p1([[NSObject alloc] init]); - WeakNSObject w1(p1); - WeakNSObject w2; - EXPECT_FALSE(w2); - w2 = w1; - EXPECT_TRUE(w1); - EXPECT_TRUE(w2); - p1.reset(); - EXPECT_FALSE(w1); - EXPECT_FALSE(w2); -} - -// Touches |weak_data| by increasing its length by 1. Used to check that the -// weak object can be dereferenced. -void TouchWeakData(const WeakNSObject& weak_data) { - if (!weak_data) - return; - [weak_data increaseLengthBy:1]; -} - -// Makes a copy of |weak_object| on the current thread and posts a task to touch -// the weak object on its original thread. -void CopyWeakNSObjectAndPost(const WeakNSObject& weak_object, - scoped_refptr runner) { - // Copy using constructor. - WeakNSObject weak_copy1(weak_object); - runner->PostTask(FROM_HERE, Bind(&TouchWeakData, weak_copy1)); - // Copy using assignment operator. - WeakNSObject weak_copy2 = weak_object; - runner->PostTask(FROM_HERE, Bind(&TouchWeakData, weak_copy2)); -} - -// Tests that the weak object can be copied on a different thread. -TEST(WeakNSObjectTest, WeakNSObjectCopyOnOtherThread) { - MessageLoop loop; - Thread other_thread("WeakNSObjectCopyOnOtherThread"); - other_thread.Start(); - - scoped_nsobject data([[NSMutableData alloc] init]); - WeakNSObject weak(data); - - scoped_refptr runner = loop.task_runner(); - other_thread.task_runner()->PostTask( - FROM_HERE, Bind(&CopyWeakNSObjectAndPost, weak, runner)); - other_thread.Stop(); - RunLoop().RunUntilIdle(); - - // Check that TouchWeakData was called and the object touched twice. - EXPECT_EQ(2u, [data length]); -} - -} // namespace -} // namespace base diff --git a/json/OWNERS b/json/OWNERS deleted file mode 100644 index 14fce2ae6..000000000 --- a/json/OWNERS +++ /dev/null @@ -1 +0,0 @@ -rsesek@chromium.org diff --git a/json/json_correctness_fuzzer.cc b/json/json_correctness_fuzzer.cc deleted file mode 100644 index 1f32d8c23..000000000 --- a/json/json_correctness_fuzzer.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2016 The Chromium 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 fuzzer that checks correctness of json parser/writer. -// The fuzzer input is passed through parsing twice, -// so that presumably valid json is parsed/written again. - -#include -#include - -#include - -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/json/string_escape.h" -#include "base/logging.h" -#include "base/values.h" - -// Entry point for libFuzzer. -// We will use the last byte of data as parsing options. -// The rest will be used as text input to the parser. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < 2) - return 0; - - int error_code, error_line, error_column; - std::string error_message; - - // Create a copy of input buffer, as otherwise we don't catch - // overflow that touches the last byte (which is used in options). - std::unique_ptr input(new char[size - 1]); - memcpy(input.get(), data, size - 1); - - base::StringPiece input_string(input.get(), size - 1); - - const int options = data[size - 1]; - auto parsed_value = base::JSONReader::ReadAndReturnError( - input_string, options, &error_code, &error_message, &error_line, - &error_column); - if (!parsed_value) - return 0; - - std::string parsed_output; - bool b = base::JSONWriter::Write(*parsed_value, &parsed_output); - LOG_ASSERT(b); - - auto double_parsed_value = base::JSONReader::ReadAndReturnError( - parsed_output, options, &error_code, &error_message, &error_line, - &error_column); - LOG_ASSERT(double_parsed_value); - std::string double_parsed_output; - bool b2 = - base::JSONWriter::Write(*double_parsed_value, &double_parsed_output); - LOG_ASSERT(b2); - - LOG_ASSERT(parsed_output == double_parsed_output) - << "Parser/Writer mismatch." - << "\nInput=" << base::GetQuotedJSONString(parsed_output) - << "\nOutput=" << base::GetQuotedJSONString(double_parsed_output); - - return 0; -} diff --git a/json/json_file_value_serializer.cc b/json/json_file_value_serializer.cc deleted file mode 100644 index a7c68c59d..000000000 --- a/json/json_file_value_serializer.cc +++ /dev/null @@ -1,115 +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/json/json_file_value_serializer.h" - -#include "base/files/file_util.h" -#include "base/json/json_string_value_serializer.h" -#include "base/logging.h" -#include "build/build_config.h" - -using base::FilePath; - -const char JSONFileValueDeserializer::kAccessDenied[] = "Access denied."; -const char JSONFileValueDeserializer::kCannotReadFile[] = "Can't read file."; -const char JSONFileValueDeserializer::kFileLocked[] = "File locked."; -const char JSONFileValueDeserializer::kNoSuchFile[] = "File doesn't exist."; - -JSONFileValueSerializer::JSONFileValueSerializer( - const base::FilePath& json_file_path) - : json_file_path_(json_file_path) { -} - -JSONFileValueSerializer::~JSONFileValueSerializer() = default; - -bool JSONFileValueSerializer::Serialize(const base::Value& root) { - return SerializeInternal(root, false); -} - -bool JSONFileValueSerializer::SerializeAndOmitBinaryValues( - const base::Value& root) { - return SerializeInternal(root, true); -} - -bool JSONFileValueSerializer::SerializeInternal(const base::Value& root, - bool omit_binary_values) { - std::string json_string; - JSONStringValueSerializer serializer(&json_string); - serializer.set_pretty_print(true); - bool result = omit_binary_values ? - serializer.SerializeAndOmitBinaryValues(root) : - serializer.Serialize(root); - if (!result) - return false; - - int data_size = static_cast(json_string.size()); - if (base::WriteFile(json_file_path_, json_string.data(), data_size) != - data_size) - return false; - - return true; -} - -JSONFileValueDeserializer::JSONFileValueDeserializer( - const base::FilePath& json_file_path, - int options) - : json_file_path_(json_file_path), options_(options), last_read_size_(0U) {} - -JSONFileValueDeserializer::~JSONFileValueDeserializer() = default; - -int JSONFileValueDeserializer::ReadFileToString(std::string* json_string) { - DCHECK(json_string); - if (!base::ReadFileToString(json_file_path_, json_string)) { -#if defined(OS_WIN) - int error = ::GetLastError(); - if (error == ERROR_SHARING_VIOLATION || error == ERROR_LOCK_VIOLATION) { - return JSON_FILE_LOCKED; - } else if (error == ERROR_ACCESS_DENIED) { - return JSON_ACCESS_DENIED; - } -#endif - if (!base::PathExists(json_file_path_)) - return JSON_NO_SUCH_FILE; - else - return JSON_CANNOT_READ_FILE; - } - - last_read_size_ = json_string->size(); - return JSON_NO_ERROR; -} - -const char* JSONFileValueDeserializer::GetErrorMessageForCode(int error_code) { - switch (error_code) { - case JSON_NO_ERROR: - return ""; - case JSON_ACCESS_DENIED: - return kAccessDenied; - case JSON_CANNOT_READ_FILE: - return kCannotReadFile; - case JSON_FILE_LOCKED: - return kFileLocked; - case JSON_NO_SUCH_FILE: - return kNoSuchFile; - default: - NOTREACHED(); - return ""; - } -} - -std::unique_ptr JSONFileValueDeserializer::Deserialize( - int* error_code, - std::string* error_str) { - std::string json_string; - int error = ReadFileToString(&json_string); - if (error != JSON_NO_ERROR) { - if (error_code) - *error_code = error; - if (error_str) - *error_str = GetErrorMessageForCode(error); - return nullptr; - } - - JSONStringValueDeserializer deserializer(json_string, options_); - return deserializer.Deserialize(error_code, error_str); -} diff --git a/json/json_file_value_serializer.h b/json/json_file_value_serializer.h deleted file mode 100644 index a93950a60..000000000 --- a/json/json_file_value_serializer.h +++ /dev/null @@ -1,103 +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_JSON_JSON_FILE_VALUE_SERIALIZER_H_ -#define BASE_JSON_JSON_FILE_VALUE_SERIALIZER_H_ - -#include - -#include - -#include "base/base_export.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/values.h" - -class BASE_EXPORT JSONFileValueSerializer : public base::ValueSerializer { - public: - // |json_file_path_| is the path of a file that will be destination of the - // serialization. The serializer will attempt to create the file at the - // specified location. - explicit JSONFileValueSerializer(const base::FilePath& json_file_path); - - ~JSONFileValueSerializer() override; - - // DO NOT USE except in unit tests to verify the file was written properly. - // We should never serialize directly to a file since this will block the - // thread. Instead, serialize to a string and write to the file you want on - // the file thread. - // - // Attempt to serialize the data structure represented by Value into - // JSON. If the return value is true, the result will have been written - // into the file whose name was passed into the constructor. - bool Serialize(const base::Value& root) override; - - // Equivalent to Serialize(root) except binary values are omitted from the - // output. - bool SerializeAndOmitBinaryValues(const base::Value& root); - - private: - bool SerializeInternal(const base::Value& root, bool omit_binary_values); - - const base::FilePath json_file_path_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueSerializer); -}; - -class BASE_EXPORT JSONFileValueDeserializer : public base::ValueDeserializer { - public: - // |json_file_path_| is the path of a file that will be source of the - // deserialization. |options| is a bitmask of JSONParserOptions. - explicit JSONFileValueDeserializer(const base::FilePath& json_file_path, - int options = 0); - - ~JSONFileValueDeserializer() override; - - // Attempt to deserialize the data structure encoded in the file passed - // in to the constructor into a structure of Value objects. If the return - // value is NULL, and if |error_code| is non-null, |error_code| will - // contain an integer error code (either JsonFileError or JsonParseError). - // If |error_message| is non-null, it will be filled in with a formatted - // error message including the location of the error if appropriate. - // The caller takes ownership of the returned value. - std::unique_ptr Deserialize(int* error_code, - std::string* error_message) override; - - // This enum is designed to safely overlap with JSONReader::JsonParseError. - enum JsonFileError { - JSON_NO_ERROR = 0, - JSON_ACCESS_DENIED = 1000, - JSON_CANNOT_READ_FILE, - JSON_FILE_LOCKED, - JSON_NO_SUCH_FILE - }; - - // File-specific error messages that can be returned. - static const char kAccessDenied[]; - static const char kCannotReadFile[]; - static const char kFileLocked[]; - static const char kNoSuchFile[]; - - // Convert an error code into an error message. |error_code| is assumed to - // be a JsonFileError. - static const char* GetErrorMessageForCode(int error_code); - - // Returns the size (in bytes) of JSON string read from disk in the last - // successful |Deserialize()| call. - size_t get_last_read_size() const { return last_read_size_; } - - private: - // A wrapper for ReadFileToString which returns a non-zero JsonFileError if - // there were file errors. - int ReadFileToString(std::string* json_string); - - const base::FilePath json_file_path_; - const int options_; - size_t last_read_size_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueDeserializer); -}; - -#endif // BASE_JSON_JSON_FILE_VALUE_SERIALIZER_H_ - diff --git a/json/json_parser.cc b/json/json_parser.cc deleted file mode 100644 index dfe246c4f..000000000 --- a/json/json_parser.cc +++ /dev/null @@ -1,755 +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/json/json_parser.h" - -#include -#include -#include - -#include "base/logging.h" -#include "base/macros.h" -#include "base/numerics/safe_conversions.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversion_utils.h" -#include "base/strings/utf_string_conversions.h" -#include "base/third_party/icu/icu_utf.h" -#include "base/values.h" - -namespace base { -namespace internal { - -namespace { - -const int32_t kExtendedASCIIStart = 0x80; - -// Simple class that checks for maximum recursion/"stack overflow." -class StackMarker { - public: - StackMarker(int max_depth, int* depth) - : max_depth_(max_depth), depth_(depth) { - ++(*depth_); - DCHECK_LE(*depth_, max_depth_); - } - ~StackMarker() { - --(*depth_); - } - - bool IsTooDeep() const { return *depth_ >= max_depth_; } - - private: - const int max_depth_; - int* const depth_; - - DISALLOW_COPY_AND_ASSIGN(StackMarker); -}; - -constexpr uint32_t kUnicodeReplacementPoint = 0xFFFD; - -} // namespace - -// This is U+FFFD. -const char kUnicodeReplacementString[] = "\xEF\xBF\xBD"; - -JSONParser::JSONParser(int options, int max_depth) - : options_(options), - max_depth_(max_depth), - index_(0), - stack_depth_(0), - line_number_(0), - index_last_line_(0), - error_code_(JSONReader::JSON_NO_ERROR), - error_line_(0), - error_column_(0) { - CHECK_LE(max_depth, JSONReader::kStackMaxDepth); -} - -JSONParser::~JSONParser() = default; - -Optional JSONParser::Parse(StringPiece input) { - input_ = input; - index_ = 0; - line_number_ = 1; - index_last_line_ = 0; - - error_code_ = JSONReader::JSON_NO_ERROR; - error_line_ = 0; - error_column_ = 0; - - // ICU and ReadUnicodeCharacter() use int32_t for lengths, so ensure - // that the index_ will not overflow when parsing. - if (!base::IsValueInRangeForNumericType(input.length())) { - ReportError(JSONReader::JSON_TOO_LARGE, 0); - return nullopt; - } - - // When the input JSON string starts with a UTF-8 Byte-Order-Mark, - // advance the start position to avoid the ParseNextToken function mis- - // treating a Unicode BOM as an invalid character and returning NULL. - ConsumeIfMatch("\xEF\xBB\xBF"); - - // Parse the first and any nested tokens. - Optional root(ParseNextToken()); - if (!root) - return nullopt; - - // Make sure the input stream is at an end. - if (GetNextToken() != T_END_OF_INPUT) { - ReportError(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, 1); - return nullopt; - } - - return root; -} - -JSONReader::JsonParseError JSONParser::error_code() const { - return error_code_; -} - -std::string JSONParser::GetErrorMessage() const { - return FormatErrorMessage(error_line_, error_column_, - JSONReader::ErrorCodeToString(error_code_)); -} - -int JSONParser::error_line() const { - return error_line_; -} - -int JSONParser::error_column() const { - return error_column_; -} - -// StringBuilder /////////////////////////////////////////////////////////////// - -JSONParser::StringBuilder::StringBuilder() : StringBuilder(nullptr) {} - -JSONParser::StringBuilder::StringBuilder(const char* pos) - : pos_(pos), length_(0) {} - -JSONParser::StringBuilder::~StringBuilder() = default; - -JSONParser::StringBuilder& JSONParser::StringBuilder::operator=( - StringBuilder&& other) = default; - -void JSONParser::StringBuilder::Append(uint32_t point) { - DCHECK(IsValidCharacter(point)); - - if (point < kExtendedASCIIStart && !string_) { - DCHECK_EQ(static_cast(point), pos_[length_]); - ++length_; - } else { - Convert(); - if (UNLIKELY(point == kUnicodeReplacementPoint)) { - string_->append(kUnicodeReplacementString); - } else { - WriteUnicodeCharacter(point, &*string_); - } - } -} - -void JSONParser::StringBuilder::Convert() { - if (string_) - return; - string_.emplace(pos_, length_); -} - -std::string JSONParser::StringBuilder::DestructiveAsString() { - if (string_) - return std::move(*string_); - return std::string(pos_, length_); -} - -// JSONParser private ////////////////////////////////////////////////////////// - -Optional JSONParser::PeekChars(int count) { - if (static_cast(index_) + count > input_.length()) - return nullopt; - // Using StringPiece::substr() is significantly slower (according to - // base_perftests) than constructing a substring manually. - return StringPiece(input_.data() + index_, count); -} - -Optional JSONParser::PeekChar() { - Optional chars = PeekChars(1); - if (chars) - return (*chars)[0]; - return nullopt; -} - -Optional JSONParser::ConsumeChars(int count) { - Optional chars = PeekChars(count); - if (chars) - index_ += count; - return chars; -} - -Optional JSONParser::ConsumeChar() { - Optional chars = ConsumeChars(1); - if (chars) - return (*chars)[0]; - return nullopt; -} - -const char* JSONParser::pos() { - CHECK_LE(static_cast(index_), input_.length()); - return input_.data() + index_; -} - -JSONParser::Token JSONParser::GetNextToken() { - EatWhitespaceAndComments(); - - Optional c = PeekChar(); - if (!c) - return T_END_OF_INPUT; - - switch (*c) { - case '{': - return T_OBJECT_BEGIN; - case '}': - return T_OBJECT_END; - case '[': - return T_ARRAY_BEGIN; - case ']': - return T_ARRAY_END; - case '"': - return T_STRING; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - return T_NUMBER; - case 't': - return T_BOOL_TRUE; - case 'f': - return T_BOOL_FALSE; - case 'n': - return T_NULL; - case ',': - return T_LIST_SEPARATOR; - case ':': - return T_OBJECT_PAIR_SEPARATOR; - default: - return T_INVALID_TOKEN; - } -} - -void JSONParser::EatWhitespaceAndComments() { - while (Optional c = PeekChar()) { - switch (*c) { - case '\r': - case '\n': - index_last_line_ = index_; - // Don't increment line_number_ twice for "\r\n". - if (!(c == '\n' && index_ > 0 && input_[index_ - 1] == '\r')) { - ++line_number_; - } - FALLTHROUGH; - case ' ': - case '\t': - ConsumeChar(); - break; - case '/': - if (!EatComment()) - return; - break; - default: - return; - } - } -} - -bool JSONParser::EatComment() { - Optional comment_start = ConsumeChars(2); - if (!comment_start) - return false; - - if (comment_start == "//") { - // Single line comment, read to newline. - while (Optional c = PeekChar()) { - if (c == '\n' || c == '\r') - return true; - ConsumeChar(); - } - } else if (comment_start == "/*") { - char previous_char = '\0'; - // Block comment, read until end marker. - while (Optional c = PeekChar()) { - if (previous_char == '*' && c == '/') { - // EatWhitespaceAndComments will inspect pos(), which will still be on - // the last / of the comment, so advance once more (which may also be - // end of input). - ConsumeChar(); - return true; - } - previous_char = *ConsumeChar(); - } - - // If the comment is unterminated, GetNextToken will report T_END_OF_INPUT. - } - - return false; -} - -Optional JSONParser::ParseNextToken() { - return ParseToken(GetNextToken()); -} - -Optional JSONParser::ParseToken(Token token) { - switch (token) { - case T_OBJECT_BEGIN: - return ConsumeDictionary(); - case T_ARRAY_BEGIN: - return ConsumeList(); - case T_STRING: - return ConsumeString(); - case T_NUMBER: - return ConsumeNumber(); - case T_BOOL_TRUE: - case T_BOOL_FALSE: - case T_NULL: - return ConsumeLiteral(); - default: - ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); - return nullopt; - } -} - -Optional JSONParser::ConsumeDictionary() { - if (ConsumeChar() != '{') { - ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); - return nullopt; - } - - StackMarker depth_check(max_depth_, &stack_depth_); - if (depth_check.IsTooDeep()) { - ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 0); - return nullopt; - } - - std::vector dict_storage; - - Token token = GetNextToken(); - while (token != T_OBJECT_END) { - if (token != T_STRING) { - ReportError(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, 1); - return nullopt; - } - - // First consume the key. - StringBuilder key; - if (!ConsumeStringRaw(&key)) { - return nullopt; - } - - // Read the separator. - token = GetNextToken(); - if (token != T_OBJECT_PAIR_SEPARATOR) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; - } - - // The next token is the value. Ownership transfers to |dict|. - ConsumeChar(); - Optional value = ParseNextToken(); - if (!value) { - // ReportError from deeper level. - return nullopt; - } - - dict_storage.emplace_back(key.DestructiveAsString(), - std::make_unique(std::move(*value))); - - token = GetNextToken(); - if (token == T_LIST_SEPARATOR) { - ConsumeChar(); - token = GetNextToken(); - if (token == T_OBJECT_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) { - ReportError(JSONReader::JSON_TRAILING_COMMA, 1); - return nullopt; - } - } else if (token != T_OBJECT_END) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 0); - return nullopt; - } - } - - ConsumeChar(); // Closing '}'. - - return Value(Value::DictStorage(std::move(dict_storage), KEEP_LAST_OF_DUPES)); -} - -Optional JSONParser::ConsumeList() { - if (ConsumeChar() != '[') { - ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); - return nullopt; - } - - StackMarker depth_check(max_depth_, &stack_depth_); - if (depth_check.IsTooDeep()) { - ReportError(JSONReader::JSON_TOO_MUCH_NESTING, 0); - return nullopt; - } - - Value::ListStorage list_storage; - - Token token = GetNextToken(); - while (token != T_ARRAY_END) { - Optional item = ParseToken(token); - if (!item) { - // ReportError from deeper level. - return nullopt; - } - - list_storage.push_back(std::move(*item)); - - token = GetNextToken(); - if (token == T_LIST_SEPARATOR) { - ConsumeChar(); - token = GetNextToken(); - if (token == T_ARRAY_END && !(options_ & JSON_ALLOW_TRAILING_COMMAS)) { - ReportError(JSONReader::JSON_TRAILING_COMMA, 1); - return nullopt; - } - } else if (token != T_ARRAY_END) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; - } - } - - ConsumeChar(); // Closing ']'. - - return Value(std::move(list_storage)); -} - -Optional JSONParser::ConsumeString() { - StringBuilder string; - if (!ConsumeStringRaw(&string)) - return nullopt; - - return Value(string.DestructiveAsString()); -} - -bool JSONParser::ConsumeStringRaw(StringBuilder* out) { - if (ConsumeChar() != '"') { - ReportError(JSONReader::JSON_UNEXPECTED_TOKEN, 1); - return false; - } - - // StringBuilder will internally build a StringPiece unless a UTF-16 - // conversion occurs, at which point it will perform a copy into a - // std::string. - StringBuilder string(pos()); - - while (PeekChar()) { - uint32_t next_char = 0; - if (!ReadUnicodeCharacter(input_.data(), - static_cast(input_.length()), - &index_, - &next_char) || - !IsValidCharacter(next_char)) { - if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) { - ReportError(JSONReader::JSON_UNSUPPORTED_ENCODING, 1); - return false; - } - ConsumeChar(); - string.Append(kUnicodeReplacementPoint); - continue; - } - - if (next_char == '"') { - ConsumeChar(); - *out = std::move(string); - return true; - } else if (next_char != '\\') { - // If this character is not an escape sequence... - ConsumeChar(); - string.Append(next_char); - } else { - // And if it is an escape sequence, the input string will be adjusted - // (either by combining the two characters of an encoded escape sequence, - // or with a UTF conversion), so using StringPiece isn't possible -- force - // a conversion. - string.Convert(); - - // Read past the escape '\' and ensure there's a character following. - Optional escape_sequence = ConsumeChars(2); - if (!escape_sequence) { - ReportError(JSONReader::JSON_INVALID_ESCAPE, 0); - return false; - } - - switch ((*escape_sequence)[1]) { - // Allowed esape sequences: - case 'x': { // UTF-8 sequence. - // UTF-8 \x escape sequences are not allowed in the spec, but they - // are supported here for backwards-compatiblity with the old parser. - escape_sequence = ConsumeChars(2); - if (!escape_sequence) { - ReportError(JSONReader::JSON_INVALID_ESCAPE, -2); - return false; - } - - int hex_digit = 0; - if (!HexStringToInt(*escape_sequence, &hex_digit) || - !IsValidCharacter(hex_digit)) { - ReportError(JSONReader::JSON_INVALID_ESCAPE, -2); - return false; - } - - string.Append(hex_digit); - break; - } - case 'u': { // UTF-16 sequence. - // UTF units are of the form \uXXXX. - uint32_t code_point; - if (!DecodeUTF16(&code_point)) { - ReportError(JSONReader::JSON_INVALID_ESCAPE, 0); - return false; - } - string.Append(code_point); - break; - } - case '"': - string.Append('"'); - break; - case '\\': - string.Append('\\'); - break; - case '/': - string.Append('/'); - break; - case 'b': - string.Append('\b'); - break; - case 'f': - string.Append('\f'); - break; - case 'n': - string.Append('\n'); - break; - case 'r': - string.Append('\r'); - break; - case 't': - string.Append('\t'); - break; - case 'v': // Not listed as valid escape sequence in the RFC. - string.Append('\v'); - break; - // All other escape squences are illegal. - default: - ReportError(JSONReader::JSON_INVALID_ESCAPE, 0); - return false; - } - } - } - - ReportError(JSONReader::JSON_SYNTAX_ERROR, 0); - return false; -} - -// Entry is at the first X in \uXXXX. -bool JSONParser::DecodeUTF16(uint32_t* out_code_point) { - Optional escape_sequence = ConsumeChars(4); - if (!escape_sequence) - return false; - - // Consume the UTF-16 code unit, which may be a high surrogate. - int code_unit16_high = 0; - if (!HexStringToInt(*escape_sequence, &code_unit16_high)) - return false; - - // If this is a high surrogate, consume the next code unit to get the - // low surrogate. - if (CBU16_IS_SURROGATE(code_unit16_high)) { - // Make sure this is the high surrogate. If not, it's an encoding - // error. - if (!CBU16_IS_SURROGATE_LEAD(code_unit16_high)) - return false; - - // Make sure that the token has more characters to consume the - // lower surrogate. - if (!ConsumeIfMatch("\\u")) - return false; - - escape_sequence = ConsumeChars(4); - if (!escape_sequence) - return false; - - int code_unit16_low = 0; - if (!HexStringToInt(*escape_sequence, &code_unit16_low)) - return false; - - if (!CBU16_IS_TRAIL(code_unit16_low)) - return false; - - uint32_t code_point = - CBU16_GET_SUPPLEMENTARY(code_unit16_high, code_unit16_low); - if (!IsValidCharacter(code_point)) - return false; - - *out_code_point = code_point; - } else { - // Not a surrogate. - DCHECK(CBU16_IS_SINGLE(code_unit16_high)); - if (!IsValidCharacter(code_unit16_high)) { - if ((options_ & JSON_REPLACE_INVALID_CHARACTERS) == 0) { - return false; - } - *out_code_point = kUnicodeReplacementPoint; - return true; - } - - *out_code_point = code_unit16_high; - } - - return true; -} - -Optional JSONParser::ConsumeNumber() { - const char* num_start = pos(); - const int start_index = index_; - int end_index = start_index; - - if (PeekChar() == '-') - ConsumeChar(); - - if (!ReadInt(false)) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; - } - end_index = index_; - - // The optional fraction part. - if (PeekChar() == '.') { - ConsumeChar(); - if (!ReadInt(true)) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; - } - end_index = index_; - } - - // Optional exponent part. - Optional c = PeekChar(); - if (c == 'e' || c == 'E') { - ConsumeChar(); - if (PeekChar() == '-' || PeekChar() == '+') { - ConsumeChar(); - } - if (!ReadInt(true)) { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; - } - end_index = index_; - } - - // ReadInt is greedy because numbers have no easily detectable sentinel, - // so save off where the parser should be on exit (see Consume invariant at - // the top of the header), then make sure the next token is one which is - // valid. - int exit_index = index_; - - switch (GetNextToken()) { - case T_OBJECT_END: - case T_ARRAY_END: - case T_LIST_SEPARATOR: - case T_END_OF_INPUT: - break; - default: - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; - } - - index_ = exit_index; - - StringPiece num_string(num_start, end_index - start_index); - - int num_int; - if (StringToInt(num_string, &num_int)) - return Value(num_int); - - double num_double; - if (StringToDouble(num_string.as_string(), &num_double) && - std::isfinite(num_double)) { - return Value(num_double); - } - - return nullopt; -} - -bool JSONParser::ReadInt(bool allow_leading_zeros) { - size_t len = 0; - char first = 0; - - while (Optional c = PeekChar()) { - if (!IsAsciiDigit(c)) - break; - - if (len == 0) - first = *c; - - ++len; - ConsumeChar(); - } - - if (len == 0) - return false; - - if (!allow_leading_zeros && len > 1 && first == '0') - return false; - - return true; -} - -Optional JSONParser::ConsumeLiteral() { - if (ConsumeIfMatch("true")) { - return Value(true); - } else if (ConsumeIfMatch("false")) { - return Value(false); - } else if (ConsumeIfMatch("null")) { - return Value(Value::Type::NONE); - } else { - ReportError(JSONReader::JSON_SYNTAX_ERROR, 1); - return nullopt; - } -} - -bool JSONParser::ConsumeIfMatch(StringPiece match) { - if (match == PeekChars(match.size())) { - ConsumeChars(match.size()); - return true; - } - return false; -} - -void JSONParser::ReportError(JSONReader::JsonParseError code, - int column_adjust) { - error_code_ = code; - error_line_ = line_number_; - error_column_ = index_ - index_last_line_ + column_adjust; -} - -// static -std::string JSONParser::FormatErrorMessage(int line, int column, - const std::string& description) { - if (line || column) { - return StringPrintf("Line: %i, column: %i, %s", - line, column, description.c_str()); - } - return description; -} - -} // namespace internal -} // namespace base diff --git a/json/json_parser.h b/json/json_parser.h deleted file mode 100644 index a4dd2ba36..000000000 --- a/json/json_parser.h +++ /dev/null @@ -1,260 +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_JSON_JSON_PARSER_H_ -#define BASE_JSON_JSON_PARSER_H_ - -#include -#include - -#include -#include - -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "base/gtest_prod_util.h" -#include "base/json/json_reader.h" -#include "base/macros.h" -#include "base/optional.h" -#include "base/strings/string_piece.h" - -namespace base { - -class Value; - -namespace internal { - -class JSONParserTest; - -// The implementation behind the JSONReader interface. This class is not meant -// to be used directly; it encapsulates logic that need not be exposed publicly. -// -// This parser guarantees O(n) time through the input string. Iteration happens -// on the byte level, with the functions ConsumeChars() and ConsumeChar(). The -// conversion from byte to JSON token happens without advancing the parser in -// GetNextToken/ParseToken, that is tokenization operates on the current parser -// position without advancing. -// -// Built on top of these are a family of Consume functions that iterate -// internally. Invariant: on entry of a Consume function, the parser is wound -// to the first byte of a valid JSON token. On exit, it is on the first byte -// after the token that was just consumed, which would likely be the first byte -// of the next token. -class BASE_EXPORT JSONParser { - public: - JSONParser(int options, int max_depth = JSONReader::kStackMaxDepth); - ~JSONParser(); - - // Parses the input string according to the set options and returns the - // result as a Value. - // Wrap this in base::FooValue::From() to check the Value is of type Foo and - // convert to a FooValue at the same time. - Optional Parse(StringPiece input); - - // Returns the error code. - JSONReader::JsonParseError error_code() const; - - // Returns the human-friendly error message. - std::string GetErrorMessage() const; - - // Returns the error line number if parse error happened. Otherwise always - // returns 0. - int error_line() const; - - // Returns the error column number if parse error happened. Otherwise always - // returns 0. - int error_column() const; - - private: - enum Token { - T_OBJECT_BEGIN, // { - T_OBJECT_END, // } - T_ARRAY_BEGIN, // [ - T_ARRAY_END, // ] - T_STRING, - T_NUMBER, - T_BOOL_TRUE, // true - T_BOOL_FALSE, // false - T_NULL, // null - T_LIST_SEPARATOR, // , - T_OBJECT_PAIR_SEPARATOR, // : - T_END_OF_INPUT, - T_INVALID_TOKEN, - }; - - // A helper class used for parsing strings. One optimization performed is to - // create base::Value with a StringPiece to avoid unnecessary std::string - // copies. This is not possible if the input string needs to be decoded from - // UTF-16 to UTF-8, or if an escape sequence causes characters to be skipped. - // This class centralizes that logic. - class StringBuilder { - public: - // Empty constructor. Used for creating a builder with which to assign to. - StringBuilder(); - - // |pos| is the beginning of an input string, excluding the |"|. - explicit StringBuilder(const char* pos); - - ~StringBuilder(); - - StringBuilder& operator=(StringBuilder&& other); - - // Appends the Unicode code point |point| to the string, either by - // increasing the |length_| of the string if the string has not been - // converted, or by appending the UTF8 bytes for the code point. - void Append(uint32_t point); - - // Converts the builder from its default StringPiece to a full std::string, - // performing a copy. Once a builder is converted, it cannot be made a - // StringPiece again. - void Convert(); - - // Returns the builder as a string, invalidating all state. This allows - // the internal string buffer representation to be destructively moved - // in cases where the builder will not be needed any more. - std::string DestructiveAsString(); - - private: - // The beginning of the input string. - const char* pos_; - - // Number of bytes in |pos_| that make up the string being built. - size_t length_; - - // The copied string representation. Will be unset until Convert() is - // called. - base::Optional string_; - }; - - // Returns the next |count| bytes of the input stream, or nullopt if fewer - // than |count| bytes remain. - Optional PeekChars(int count); - - // Calls PeekChars() with a |count| of 1. - Optional PeekChar(); - - // Returns the next |count| bytes of the input stream, or nullopt if fewer - // than |count| bytes remain, and advances the parser position by |count|. - Optional ConsumeChars(int count); - - // Calls ConsumeChars() with a |count| of 1. - Optional ConsumeChar(); - - // Returns a pointer to the current character position. - const char* pos(); - - // Skips over whitespace and comments to find the next token in the stream. - // This does not advance the parser for non-whitespace or comment chars. - Token GetNextToken(); - - // Consumes whitespace characters and comments until the next non-that is - // encountered. - void EatWhitespaceAndComments(); - // Helper function that consumes a comment, assuming that the parser is - // currently wound to a '/'. - bool EatComment(); - - // Calls GetNextToken() and then ParseToken(). - Optional ParseNextToken(); - - // Takes a token that represents the start of a Value ("a structural token" - // in RFC terms) and consumes it, returning the result as a Value. - Optional ParseToken(Token token); - - // Assuming that the parser is currently wound to '{', this parses a JSON - // object into a Value. - Optional ConsumeDictionary(); - - // Assuming that the parser is wound to '[', this parses a JSON list into a - // Value. - Optional ConsumeList(); - - // Calls through ConsumeStringRaw and wraps it in a value. - Optional ConsumeString(); - - // Assuming that the parser is wound to a double quote, this parses a string, - // decoding any escape sequences and converts UTF-16 to UTF-8. Returns true on - // success and places result into |out|. Returns false on failure with - // error information set. - bool ConsumeStringRaw(StringBuilder* out); - // Helper function for ConsumeStringRaw() that consumes the next four or 10 - // bytes (parser is wound to the first character of a HEX sequence, with the - // potential for consuming another \uXXXX for a surrogate). Returns true on - // success and places the code point |out_code_point|, and false on failure. - bool DecodeUTF16(uint32_t* out_code_point); - - // Assuming that the parser is wound to the start of a valid JSON number, - // this parses and converts it to either an int or double value. - Optional ConsumeNumber(); - // Helper that reads characters that are ints. Returns true if a number was - // read and false on error. - bool ReadInt(bool allow_leading_zeros); - - // Consumes the literal values of |true|, |false|, and |null|, assuming the - // parser is wound to the first character of any of those. - Optional ConsumeLiteral(); - - // Helper function that returns true if the byte squence |match| can be - // consumed at the current parser position. Returns false if there are fewer - // than |match|-length bytes or if the sequence does not match, and the - // parser state is unchanged. - bool ConsumeIfMatch(StringPiece match); - - // Sets the error information to |code| at the current column, based on - // |index_| and |index_last_line_|, with an optional positive/negative - // adjustment by |column_adjust|. - void ReportError(JSONReader::JsonParseError code, int column_adjust); - - // Given the line and column number of an error, formats one of the error - // message contants from json_reader.h for human display. - static std::string FormatErrorMessage(int line, int column, - const std::string& description); - - // base::JSONParserOptions that control parsing. - const int options_; - - // Maximum depth to parse. - const int max_depth_; - - // The input stream being parsed. Note: Not guaranteed to NUL-terminated. - StringPiece input_; - - // The index in the input stream to which the parser is wound. - int index_; - - // The number of times the parser has recursed (current stack depth). - int stack_depth_; - - // The line number that the parser is at currently. - int line_number_; - - // The last value of |index_| on the previous line. - int index_last_line_; - - // Error information. - JSONReader::JsonParseError error_code_; - int error_line_; - int error_column_; - - friend class JSONParserTest; - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, NextChar); - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeDictionary); - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeList); - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeString); - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeLiterals); - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ConsumeNumbers); - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ErrorMessages); - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidCharacters); - FRIEND_TEST_ALL_PREFIXES(JSONParserTest, ReplaceInvalidUTF16EscapeSequence); - - DISALLOW_COPY_AND_ASSIGN(JSONParser); -}; - -// Used when decoding and an invalid utf-8 sequence is encountered. -BASE_EXPORT extern const char kUnicodeReplacementString[]; - -} // namespace internal -} // namespace base - -#endif // BASE_JSON_JSON_PARSER_H_ diff --git a/json/json_parser_unittest.cc b/json/json_parser_unittest.cc deleted file mode 100644 index 0da3db849..000000000 --- a/json/json_parser_unittest.cc +++ /dev/null @@ -1,462 +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/json/json_parser.h" - -#include - -#include - -#include "base/json/json_reader.h" -#include "base/memory/ptr_util.h" -#include "base/optional.h" -#include "base/strings/stringprintf.h" -#include "base/values.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace internal { - -class JSONParserTest : public testing::Test { - public: - JSONParser* NewTestParser(const std::string& input, - int options = JSON_PARSE_RFC) { - JSONParser* parser = new JSONParser(options); - parser->input_ = input; - parser->index_ = 0; - return parser; - } - - // MSan will do a better job detecting over-read errors if the input is - // not nul-terminated on the heap. This will copy |input| to a new buffer - // owned by |owner|, returning a StringPiece to |owner|. - StringPiece MakeNotNullTerminatedInput(const char* input, - std::unique_ptr* owner) { - size_t str_len = strlen(input); - owner->reset(new char[str_len]); - memcpy(owner->get(), input, str_len); - return StringPiece(owner->get(), str_len); - } - - void TestLastThree(JSONParser* parser) { - EXPECT_EQ(',', *parser->PeekChar()); - parser->ConsumeChar(); - EXPECT_EQ('|', *parser->PeekChar()); - parser->ConsumeChar(); - EXPECT_EQ('\0', *parser->pos()); - EXPECT_EQ(static_cast(parser->index_), parser->input_.length()); - } -}; - -TEST_F(JSONParserTest, NextChar) { - std::string input("Hello world"); - std::unique_ptr parser(NewTestParser(input)); - - EXPECT_EQ('H', *parser->pos()); - for (size_t i = 1; i < input.length(); ++i) { - parser->ConsumeChar(); - EXPECT_EQ(input[i], *parser->PeekChar()); - } - parser->ConsumeChar(); - EXPECT_EQ('\0', *parser->pos()); - EXPECT_EQ(static_cast(parser->index_), parser->input_.length()); -} - -TEST_F(JSONParserTest, ConsumeString) { - std::string input("\"test\",|"); - std::unique_ptr parser(NewTestParser(input)); - Optional value(parser->ConsumeString()); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - std::string str; - EXPECT_TRUE(value->GetAsString(&str)); - EXPECT_EQ("test", str); -} - -TEST_F(JSONParserTest, ConsumeList) { - std::string input("[true, false],|"); - std::unique_ptr parser(NewTestParser(input)); - Optional value(parser->ConsumeList()); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - base::ListValue* list; - EXPECT_TRUE(value->GetAsList(&list)); - EXPECT_EQ(2u, list->GetSize()); -} - -TEST_F(JSONParserTest, ConsumeDictionary) { - std::string input("{\"abc\":\"def\"},|"); - std::unique_ptr parser(NewTestParser(input)); - Optional value(parser->ConsumeDictionary()); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - base::DictionaryValue* dict; - EXPECT_TRUE(value->GetAsDictionary(&dict)); - std::string str; - EXPECT_TRUE(dict->GetString("abc", &str)); - EXPECT_EQ("def", str); -} - -TEST_F(JSONParserTest, ConsumeLiterals) { - // Literal |true|. - std::string input("true,|"); - std::unique_ptr parser(NewTestParser(input)); - Optional value(parser->ConsumeLiteral()); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - bool bool_value = false; - EXPECT_TRUE(value->GetAsBoolean(&bool_value)); - EXPECT_TRUE(bool_value); - - // Literal |false|. - input = "false,|"; - parser.reset(NewTestParser(input)); - value = parser->ConsumeLiteral(); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - EXPECT_TRUE(value->GetAsBoolean(&bool_value)); - EXPECT_FALSE(bool_value); - - // Literal |null|. - input = "null,|"; - parser.reset(NewTestParser(input)); - value = parser->ConsumeLiteral(); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - EXPECT_TRUE(value->is_none()); -} - -TEST_F(JSONParserTest, ConsumeNumbers) { - // Integer. - std::string input("1234,|"); - std::unique_ptr parser(NewTestParser(input)); - Optional value(parser->ConsumeNumber()); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - int number_i; - EXPECT_TRUE(value->GetAsInteger(&number_i)); - EXPECT_EQ(1234, number_i); - - // Negative integer. - input = "-1234,|"; - parser.reset(NewTestParser(input)); - value = parser->ConsumeNumber(); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - EXPECT_TRUE(value->GetAsInteger(&number_i)); - EXPECT_EQ(-1234, number_i); - - // Double. - input = "12.34,|"; - parser.reset(NewTestParser(input)); - value = parser->ConsumeNumber(); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - double number_d; - EXPECT_TRUE(value->GetAsDouble(&number_d)); - EXPECT_EQ(12.34, number_d); - - // Scientific. - input = "42e3,|"; - parser.reset(NewTestParser(input)); - value = parser->ConsumeNumber(); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - EXPECT_TRUE(value->GetAsDouble(&number_d)); - EXPECT_EQ(42000, number_d); - - // Negative scientific. - input = "314159e-5,|"; - parser.reset(NewTestParser(input)); - value = parser->ConsumeNumber(); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - EXPECT_TRUE(value->GetAsDouble(&number_d)); - EXPECT_EQ(3.14159, number_d); - - // Positive scientific. - input = "0.42e+3,|"; - parser.reset(NewTestParser(input)); - value = parser->ConsumeNumber(); - EXPECT_EQ(',', *parser->pos()); - - TestLastThree(parser.get()); - - ASSERT_TRUE(value); - EXPECT_TRUE(value->GetAsDouble(&number_d)); - EXPECT_EQ(420, number_d); -} - -TEST_F(JSONParserTest, ErrorMessages) { - // Error strings should not be modified in case of success. - std::string error_message; - int error_code = 0; - std::unique_ptr root = JSONReader::ReadAndReturnError( - "[42]", JSON_PARSE_RFC, &error_code, &error_message); - EXPECT_TRUE(error_message.empty()); - EXPECT_EQ(0, error_code); - - // Test line and column counting - const char big_json[] = "[\n0,\n1,\n2,\n3,4,5,6 7,\n8,\n9\n]"; - // error here ----------------------------------^ - root = JSONReader::ReadAndReturnError(big_json, JSON_PARSE_RFC, &error_code, - &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(5, 10, JSONReader::kSyntaxError), - error_message); - EXPECT_EQ(JSONReader::JSON_SYNTAX_ERROR, error_code); - - error_code = 0; - error_message = ""; - // Test line and column counting with "\r\n" line ending - const char big_json_crlf[] = - "[\r\n0,\r\n1,\r\n2,\r\n3,4,5,6 7,\r\n8,\r\n9\r\n]"; - // error here ----------------------^ - root = JSONReader::ReadAndReturnError(big_json_crlf, JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(5, 10, JSONReader::kSyntaxError), - error_message); - EXPECT_EQ(JSONReader::JSON_SYNTAX_ERROR, error_code); - - // Test each of the error conditions - root = JSONReader::ReadAndReturnError("{},{}", JSON_PARSE_RFC, &error_code, - &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 3, - JSONReader::kUnexpectedDataAfterRoot), error_message); - EXPECT_EQ(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, error_code); - - std::string nested_json; - for (int i = 0; i < 201; ++i) { - nested_json.insert(nested_json.begin(), '['); - nested_json.append(1, ']'); - } - root = JSONReader::ReadAndReturnError(nested_json, JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 200, JSONReader::kTooMuchNesting), - error_message); - EXPECT_EQ(JSONReader::JSON_TOO_MUCH_NESTING, error_code); - - root = JSONReader::ReadAndReturnError("[1,]", JSON_PARSE_RFC, &error_code, - &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 4, JSONReader::kTrailingComma), - error_message); - EXPECT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code); - - root = JSONReader::ReadAndReturnError("{foo:\"bar\"}", JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 2, - JSONReader::kUnquotedDictionaryKey), error_message); - EXPECT_EQ(JSONReader::JSON_UNQUOTED_DICTIONARY_KEY, error_code); - - root = JSONReader::ReadAndReturnError("{\"foo\":\"bar\",}", JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 14, JSONReader::kTrailingComma), - error_message); - - root = JSONReader::ReadAndReturnError("[nu]", JSON_PARSE_RFC, &error_code, - &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 2, JSONReader::kSyntaxError), - error_message); - EXPECT_EQ(JSONReader::JSON_SYNTAX_ERROR, error_code); - - root = JSONReader::ReadAndReturnError("[\"xxx\\xq\"]", JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape), - error_message); - EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code); - - root = JSONReader::ReadAndReturnError("[\"xxx\\uq\"]", JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape), - error_message); - EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code); - - root = JSONReader::ReadAndReturnError("[\"xxx\\q\"]", JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_FALSE(root.get()); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape), - error_message); - EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code); - - root = JSONReader::ReadAndReturnError(("[\"\\ufffe\"]"), JSON_PARSE_RFC, - &error_code, &error_message); - EXPECT_EQ(JSONParser::FormatErrorMessage(1, 8, JSONReader::kInvalidEscape), - error_message); - EXPECT_EQ(JSONReader::JSON_INVALID_ESCAPE, error_code); -} - -TEST_F(JSONParserTest, Decode4ByteUtf8Char) { - // This test strings contains a 4 byte unicode character (a smiley!) that the - // reader should be able to handle (the character is \xf0\x9f\x98\x87). - const char kUtf8Data[] = - "[\"😇\",[],[],[],{\"google:suggesttype\":[]}]"; - std::string error_message; - int error_code = 0; - std::unique_ptr root = JSONReader::ReadAndReturnError( - kUtf8Data, JSON_PARSE_RFC, &error_code, &error_message); - EXPECT_TRUE(root.get()) << error_message; -} - -TEST_F(JSONParserTest, DecodeUnicodeNonCharacter) { - // Tests Unicode code points (encoded as escaped UTF-16) that are not valid - // characters. - EXPECT_FALSE(JSONReader::Read("[\"\\ufdd0\"]")); - EXPECT_FALSE(JSONReader::Read("[\"\\ufffe\"]")); - EXPECT_FALSE(JSONReader::Read("[\"\\ud83f\\udffe\"]")); - - EXPECT_TRUE( - JSONReader::Read("[\"\\ufdd0\"]", JSON_REPLACE_INVALID_CHARACTERS)); - EXPECT_TRUE( - JSONReader::Read("[\"\\ufffe\"]", JSON_REPLACE_INVALID_CHARACTERS)); -} - -TEST_F(JSONParserTest, DecodeNegativeEscapeSequence) { - EXPECT_FALSE(JSONReader::Read("[\"\\x-A\"]")); - EXPECT_FALSE(JSONReader::Read("[\"\\u-00A\"]")); -} - -// Verifies invalid utf-8 characters are replaced. -TEST_F(JSONParserTest, ReplaceInvalidCharacters) { - const std::string bogus_char = "󿿿"; - const std::string quoted_bogus_char = "\"" + bogus_char + "\""; - std::unique_ptr parser( - NewTestParser(quoted_bogus_char, JSON_REPLACE_INVALID_CHARACTERS)); - Optional value(parser->ConsumeString()); - ASSERT_TRUE(value); - std::string str; - EXPECT_TRUE(value->GetAsString(&str)); - EXPECT_EQ(kUnicodeReplacementString, str); -} - -TEST_F(JSONParserTest, ReplaceInvalidUTF16EscapeSequence) { - const std::string invalid = "\"\\ufffe\""; - std::unique_ptr parser( - NewTestParser(invalid, JSON_REPLACE_INVALID_CHARACTERS)); - Optional value(parser->ConsumeString()); - ASSERT_TRUE(value); - std::string str; - EXPECT_TRUE(value->GetAsString(&str)); - EXPECT_EQ(kUnicodeReplacementString, str); -} - -TEST_F(JSONParserTest, ParseNumberErrors) { - const struct { - const char* input; - bool parse_success; - double value; - } kCases[] = { - // clang-format off - {"1", true, 1}, - {"2.", false, 0}, - {"42", true, 42}, - {"6e", false, 0}, - {"43e2", true, 4300}, - {"43e-", false, 0}, - {"9e-3", true, 0.009}, - {"2e+", false, 0}, - {"2e+2", true, 200}, - // clang-format on - }; - - for (unsigned int i = 0; i < arraysize(kCases); ++i) { - auto test_case = kCases[i]; - SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case.input)); - - std::unique_ptr input_owner; - StringPiece input = - MakeNotNullTerminatedInput(test_case.input, &input_owner); - - std::unique_ptr result = JSONReader::Read(input); - if (test_case.parse_success) { - EXPECT_TRUE(result); - } else { - EXPECT_FALSE(result); - } - - if (!result) - continue; - - double double_value = 0; - EXPECT_TRUE(result->GetAsDouble(&double_value)); - EXPECT_EQ(test_case.value, double_value); - } -} - -TEST_F(JSONParserTest, UnterminatedInputs) { - const char* kCases[] = { - // clang-format off - "/", - "//", - "/*", - "\"xxxxxx", - "\"", - "{ ", - "[\t", - "tru", - "fals", - "nul", - "\"\\x", - "\"\\x2", - "\"\\u123", - "\"\\uD803\\u", - "\"\\", - "\"\\/", - // clang-format on - }; - - for (unsigned int i = 0; i < arraysize(kCases); ++i) { - auto* test_case = kCases[i]; - SCOPED_TRACE(StringPrintf("case %u: \"%s\"", i, test_case)); - - std::unique_ptr input_owner; - StringPiece input = MakeNotNullTerminatedInput(test_case, &input_owner); - - EXPECT_FALSE(JSONReader::Read(input)); - } -} - -} // namespace internal -} // namespace base diff --git a/json/json_perftest.cc b/json/json_perftest.cc deleted file mode 100644 index fc05bdca6..000000000 --- a/json/json_perftest.cc +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/memory/ptr_util.h" -#include "base/time/time.h" -#include "base/values.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_test.h" - -namespace base { - -namespace { -// Generates a simple dictionary value with simple data types, a string and a -// list. -std::unique_ptr GenerateDict() { - auto root = std::make_unique(); - root->SetDouble("Double", 3.141); - root->SetBoolean("Bool", true); - root->SetInteger("Int", 42); - root->SetString("String", "Foo"); - - auto list = std::make_unique(); - list->Set(0, std::make_unique(2.718)); - list->Set(1, std::make_unique(false)); - list->Set(2, std::make_unique(123)); - list->Set(3, std::make_unique("Bar")); - root->Set("List", std::move(list)); - - return root; -} - -// Generates a tree-like dictionary value with a size of O(breadth ** depth). -std::unique_ptr GenerateLayeredDict(int breadth, int depth) { - if (depth == 1) - return GenerateDict(); - - auto root = GenerateDict(); - auto next = GenerateLayeredDict(breadth, depth - 1); - - for (int i = 0; i < breadth; ++i) { - root->Set("Dict" + std::to_string(i), next->CreateDeepCopy()); - } - - return root; -} - -} // namespace - -class JSONPerfTest : public testing::Test { - public: - void TestWriteAndRead(int breadth, int depth) { - std::string description = "Breadth: " + std::to_string(breadth) + - ", Depth: " + std::to_string(depth); - auto dict = GenerateLayeredDict(breadth, depth); - std::string json; - - TimeTicks start_write = TimeTicks::Now(); - JSONWriter::Write(*dict, &json); - TimeTicks end_write = TimeTicks::Now(); - perf_test::PrintResult("Write", "", description, - (end_write - start_write).InMillisecondsF(), "ms", - true); - - TimeTicks start_read = TimeTicks::Now(); - JSONReader::Read(json); - TimeTicks end_read = TimeTicks::Now(); - perf_test::PrintResult("Read", "", description, - (end_read - start_read).InMillisecondsF(), "ms", - true); - } -}; - -TEST_F(JSONPerfTest, StressTest) { - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 12; ++j) { - TestWriteAndRead(i + 1, j + 1); - } - } -} - -} // namespace base diff --git a/json/json_reader.cc b/json/json_reader.cc deleted file mode 100644 index bf2a18a5e..000000000 --- a/json/json_reader.cc +++ /dev/null @@ -1,126 +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/json/json_reader.h" - -#include -#include - -#include "base/json/json_parser.h" -#include "base/logging.h" -#include "base/optional.h" -#include "base/values.h" - -namespace base { - -// Chosen to support 99.9% of documents found in the wild late 2016. -// http://crbug.com/673263 -const int JSONReader::kStackMaxDepth = 200; - -// Values 1000 and above are used by JSONFileValueSerializer::JsonFileError. -static_assert(JSONReader::JSON_PARSE_ERROR_COUNT < 1000, - "JSONReader error out of bounds"); - -const char JSONReader::kInvalidEscape[] = - "Invalid escape sequence."; -const char JSONReader::kSyntaxError[] = - "Syntax error."; -const char JSONReader::kUnexpectedToken[] = - "Unexpected token."; -const char JSONReader::kTrailingComma[] = - "Trailing comma not allowed."; -const char JSONReader::kTooMuchNesting[] = - "Too much nesting."; -const char JSONReader::kUnexpectedDataAfterRoot[] = - "Unexpected data after root element."; -const char JSONReader::kUnsupportedEncoding[] = - "Unsupported encoding. JSON must be UTF-8."; -const char JSONReader::kUnquotedDictionaryKey[] = - "Dictionary keys must be quoted."; -const char JSONReader::kInputTooLarge[] = - "Input string is too large (>2GB)."; - -JSONReader::JSONReader(int options, int max_depth) - : parser_(new internal::JSONParser(options, max_depth)) {} - -JSONReader::~JSONReader() = default; - -// static -std::unique_ptr JSONReader::Read(StringPiece json, - int options, - int max_depth) { - internal::JSONParser parser(options, max_depth); - Optional root = parser.Parse(json); - return root ? std::make_unique(std::move(*root)) : nullptr; -} - - -// static -std::unique_ptr JSONReader::ReadAndReturnError( - StringPiece json, - int options, - int* error_code_out, - std::string* error_msg_out, - int* error_line_out, - int* error_column_out) { - internal::JSONParser parser(options); - Optional root = parser.Parse(json); - if (!root) { - if (error_code_out) - *error_code_out = parser.error_code(); - if (error_msg_out) - *error_msg_out = parser.GetErrorMessage(); - if (error_line_out) - *error_line_out = parser.error_line(); - if (error_column_out) - *error_column_out = parser.error_column(); - } - - return root ? std::make_unique(std::move(*root)) : nullptr; -} - -// static -std::string JSONReader::ErrorCodeToString(JsonParseError error_code) { - switch (error_code) { - case JSON_NO_ERROR: - return std::string(); - case JSON_INVALID_ESCAPE: - return kInvalidEscape; - case JSON_SYNTAX_ERROR: - return kSyntaxError; - case JSON_UNEXPECTED_TOKEN: - return kUnexpectedToken; - case JSON_TRAILING_COMMA: - return kTrailingComma; - case JSON_TOO_MUCH_NESTING: - return kTooMuchNesting; - case JSON_UNEXPECTED_DATA_AFTER_ROOT: - return kUnexpectedDataAfterRoot; - case JSON_UNSUPPORTED_ENCODING: - return kUnsupportedEncoding; - case JSON_UNQUOTED_DICTIONARY_KEY: - return kUnquotedDictionaryKey; - case JSON_TOO_LARGE: - return kInputTooLarge; - case JSON_PARSE_ERROR_COUNT: - break; - } - NOTREACHED(); - return std::string(); -} - -std::unique_ptr JSONReader::ReadToValue(StringPiece json) { - Optional value = parser_->Parse(json); - return value ? std::make_unique(std::move(*value)) : nullptr; -} - -JSONReader::JsonParseError JSONReader::error_code() const { - return parser_->error_code(); -} - -std::string JSONReader::GetErrorMessage() const { - return parser_->GetErrorMessage(); -} - -} // namespace base diff --git a/json/json_reader.h b/json/json_reader.h deleted file mode 100644 index 2c6bd3e47..000000000 --- a/json/json_reader.h +++ /dev/null @@ -1,135 +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. -// -// A JSON parser. Converts strings of JSON into a Value object (see -// base/values.h). -// http://www.ietf.org/rfc/rfc4627.txt?number=4627 -// -// Known limitations/deviations from the RFC: -// - Only knows how to parse ints within the range of a signed 32 bit int and -// decimal numbers within a double. -// - Assumes input is encoded as UTF8. The spec says we should allow UTF-16 -// (BE or LE) and UTF-32 (BE or LE) as well. -// - We limit nesting to 100 levels to prevent stack overflow (this is allowed -// by the RFC). -// - A Unicode FAQ ("http://unicode.org/faq/utf_bom.html") writes a data -// stream may start with a Unicode Byte-Order-Mark (U+FEFF), i.e. the input -// UTF-8 string for the JSONReader::JsonToValue() function may start with a -// UTF-8 BOM (0xEF, 0xBB, 0xBF). -// To avoid the function from mis-treating a UTF-8 BOM as an invalid -// character, the function skips a Unicode BOM at the beginning of the -// Unicode string (converted from the input UTF-8 string) before parsing it. -// -// TODO(tc): Add a parsing option to to relax object keys being wrapped in -// double quotes -// TODO(tc): Add an option to disable comment stripping - -#ifndef BASE_JSON_JSON_READER_H_ -#define BASE_JSON_JSON_READER_H_ - -#include -#include - -#include "base/base_export.h" -#include "base/strings/string_piece.h" - -namespace base { - -class Value; - -namespace internal { -class JSONParser; -} - -enum JSONParserOptions { - // Parses the input strictly according to RFC 4627, except for where noted - // above. - JSON_PARSE_RFC = 0, - - // Allows commas to exist after the last element in structures. - JSON_ALLOW_TRAILING_COMMAS = 1 << 0, - - // If set the parser replaces invalid characters with the Unicode replacement - // character (U+FFFD). If not set, invalid characters trigger a hard error and - // parsing fails. - JSON_REPLACE_INVALID_CHARACTERS = 1 << 1, -}; - -class BASE_EXPORT JSONReader { - public: - static const int kStackMaxDepth; - - // Error codes during parsing. - enum JsonParseError { - JSON_NO_ERROR = 0, - JSON_INVALID_ESCAPE, - JSON_SYNTAX_ERROR, - JSON_UNEXPECTED_TOKEN, - JSON_TRAILING_COMMA, - JSON_TOO_MUCH_NESTING, - JSON_UNEXPECTED_DATA_AFTER_ROOT, - JSON_UNSUPPORTED_ENCODING, - JSON_UNQUOTED_DICTIONARY_KEY, - JSON_TOO_LARGE, - JSON_PARSE_ERROR_COUNT - }; - - // String versions of parse error codes. - static const char kInvalidEscape[]; - static const char kSyntaxError[]; - static const char kUnexpectedToken[]; - static const char kTrailingComma[]; - static const char kTooMuchNesting[]; - static const char kUnexpectedDataAfterRoot[]; - static const char kUnsupportedEncoding[]; - static const char kUnquotedDictionaryKey[]; - static const char kInputTooLarge[]; - - // Constructs a reader. - JSONReader(int options = JSON_PARSE_RFC, int max_depth = kStackMaxDepth); - - ~JSONReader(); - - // Reads and parses |json|, returning a Value. - // If |json| is not a properly formed JSON string, returns nullptr. - // Wrap this in base::FooValue::From() to check the Value is of type Foo and - // convert to a FooValue at the same time. - static std::unique_ptr Read(StringPiece json, - int options = JSON_PARSE_RFC, - int max_depth = kStackMaxDepth); - - // Reads and parses |json| like Read(). |error_code_out| and |error_msg_out| - // are optional. If specified and nullptr is returned, they will be populated - // an error code and a formatted error message (including error location if - // appropriate). Otherwise, they will be unmodified. - static std::unique_ptr ReadAndReturnError( - StringPiece json, - int options, // JSONParserOptions - int* error_code_out, - std::string* error_msg_out, - int* error_line_out = nullptr, - int* error_column_out = nullptr); - - // Converts a JSON parse error code into a human readable message. - // Returns an empty string if error_code is JSON_NO_ERROR. - static std::string ErrorCodeToString(JsonParseError error_code); - - // Non-static version of Read() above. - std::unique_ptr ReadToValue(StringPiece json); - - // Returns the error code if the last call to ReadToValue() failed. - // Returns JSON_NO_ERROR otherwise. - JsonParseError error_code() const; - - // Converts error_code_ to a human-readable string, including line and column - // numbers if appropriate. - std::string GetErrorMessage() const; - - private: - std::unique_ptr parser_; -}; - -} // namespace base - -#endif // BASE_JSON_JSON_READER_H_ diff --git a/json/json_reader_fuzzer.cc b/json/json_reader_fuzzer.cc deleted file mode 100644 index 5e69940e6..000000000 --- a/json/json_reader_fuzzer.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/json/json_reader.h" -#include "base/values.h" - -// Entry point for LibFuzzer. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < 2) - return 0; - - // Create a copy of input buffer, as otherwise we don't catch - // overflow that touches the last byte (which is used in options). - std::unique_ptr input(new char[size - 1]); - memcpy(input.get(), data, size - 1); - - base::StringPiece input_string(input.get(), size - 1); - - const int options = data[size - 1]; - - int error_code, error_line, error_column; - std::string error_message; - base::JSONReader::ReadAndReturnError(input_string, options, &error_code, - &error_message, &error_line, - &error_column); - - return 0; -} diff --git a/json/json_reader_unittest.cc b/json/json_reader_unittest.cc deleted file mode 100644 index faaf43ebd..000000000 --- a/json/json_reader_unittest.cc +++ /dev/null @@ -1,665 +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/json/json_reader.h" - -#include - -#include - -#include "base/base_paths.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/path_service.h" -#include "base/strings/string_piece.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(JSONReaderTest, Whitespace) { - std::unique_ptr root = JSONReader().ReadToValue(" null "); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_none()); -} - -TEST(JSONReaderTest, InvalidString) { - EXPECT_FALSE(JSONReader().ReadToValue("nu")); -} - -TEST(JSONReaderTest, SimpleBool) { - std::unique_ptr root = JSONReader().ReadToValue("true "); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_bool()); -} - -TEST(JSONReaderTest, EmbeddedComments) { - std::unique_ptr root = JSONReader().ReadToValue("/* comment */null"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_none()); - root = JSONReader().ReadToValue("40 /* comment */"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_int()); - root = JSONReader().ReadToValue("true // comment"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_bool()); - root = JSONReader().ReadToValue("/* comment */\"sample string\""); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string value; - EXPECT_TRUE(root->GetAsString(&value)); - EXPECT_EQ("sample string", value); - std::unique_ptr list = - ListValue::From(JSONReader().ReadToValue("[1, /* comment, 2 ] */ \n 3]")); - ASSERT_TRUE(list); - EXPECT_EQ(2u, list->GetSize()); - int int_val = 0; - EXPECT_TRUE(list->GetInteger(0, &int_val)); - EXPECT_EQ(1, int_val); - EXPECT_TRUE(list->GetInteger(1, &int_val)); - EXPECT_EQ(3, int_val); - list = ListValue::From(JSONReader().ReadToValue("[1, /*a*/2, 3]")); - ASSERT_TRUE(list); - EXPECT_EQ(3u, list->GetSize()); - root = JSONReader().ReadToValue("/* comment **/42"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_int()); - EXPECT_TRUE(root->GetAsInteger(&int_val)); - EXPECT_EQ(42, int_val); - root = JSONReader().ReadToValue( - "/* comment **/\n" - "// */ 43\n" - "44"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_int()); - EXPECT_TRUE(root->GetAsInteger(&int_val)); - EXPECT_EQ(44, int_val); -} - -TEST(JSONReaderTest, Ints) { - std::unique_ptr root = JSONReader().ReadToValue("43"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_int()); - int int_val = 0; - EXPECT_TRUE(root->GetAsInteger(&int_val)); - EXPECT_EQ(43, int_val); -} - -TEST(JSONReaderTest, NonDecimalNumbers) { - // According to RFC4627, oct, hex, and leading zeros are invalid JSON. - EXPECT_FALSE(JSONReader().ReadToValue("043")); - EXPECT_FALSE(JSONReader().ReadToValue("0x43")); - EXPECT_FALSE(JSONReader().ReadToValue("00")); -} - -TEST(JSONReaderTest, NumberZero) { - // Test 0 (which needs to be special cased because of the leading zero - // clause). - std::unique_ptr root = JSONReader().ReadToValue("0"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_int()); - int int_val = 1; - EXPECT_TRUE(root->GetAsInteger(&int_val)); - EXPECT_EQ(0, int_val); -} - -TEST(JSONReaderTest, LargeIntPromotion) { - // Numbers that overflow ints should succeed, being internally promoted to - // storage as doubles - std::unique_ptr root = JSONReader().ReadToValue("2147483648"); - ASSERT_TRUE(root); - double double_val; - EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(2147483648.0, double_val); - root = JSONReader().ReadToValue("-2147483649"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(-2147483649.0, double_val); -} - -TEST(JSONReaderTest, Doubles) { - std::unique_ptr root = JSONReader().ReadToValue("43.1"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_double()); - double double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(43.1, double_val); - - root = JSONReader().ReadToValue("4.3e-1"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(.43, double_val); - - root = JSONReader().ReadToValue("2.1e0"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(2.1, double_val); - - root = JSONReader().ReadToValue("2.1e+0001"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(21.0, double_val); - - root = JSONReader().ReadToValue("0.01"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(0.01, double_val); - - root = JSONReader().ReadToValue("1.00"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_double()); - double_val = 0.0; - EXPECT_TRUE(root->GetAsDouble(&double_val)); - EXPECT_DOUBLE_EQ(1.0, double_val); -} - -TEST(JSONReaderTest, FractionalNumbers) { - // Fractional parts must have a digit before and after the decimal point. - EXPECT_FALSE(JSONReader().ReadToValue("1.")); - EXPECT_FALSE(JSONReader().ReadToValue(".1")); - EXPECT_FALSE(JSONReader().ReadToValue("1.e10")); -} - -TEST(JSONReaderTest, ExponentialNumbers) { - // Exponent must have a digit following the 'e'. - EXPECT_FALSE(JSONReader().ReadToValue("1e")); - EXPECT_FALSE(JSONReader().ReadToValue("1E")); - EXPECT_FALSE(JSONReader().ReadToValue("1e1.")); - EXPECT_FALSE(JSONReader().ReadToValue("1e1.0")); -} - -TEST(JSONReaderTest, InvalidNAN) { - EXPECT_FALSE(JSONReader().ReadToValue("1e1000")); - EXPECT_FALSE(JSONReader().ReadToValue("-1e1000")); - EXPECT_FALSE(JSONReader().ReadToValue("NaN")); - EXPECT_FALSE(JSONReader().ReadToValue("nan")); - EXPECT_FALSE(JSONReader().ReadToValue("inf")); -} - -TEST(JSONReaderTest, InvalidNumbers) { - EXPECT_FALSE(JSONReader().ReadToValue("4.3.1")); - EXPECT_FALSE(JSONReader().ReadToValue("4e3.1")); - EXPECT_FALSE(JSONReader().ReadToValue("4.a")); -} - -TEST(JSONReader, SimpleString) { - std::unique_ptr root = JSONReader().ReadToValue("\"hello world\""); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ("hello world", str_val); -} - -TEST(JSONReaderTest, EmptyString) { - std::unique_ptr root = JSONReader().ReadToValue("\"\""); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ("", str_val); -} - -TEST(JSONReaderTest, BasicStringEscapes) { - std::unique_ptr root = - JSONReader().ReadToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\""); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ(" \"\\/\b\f\n\r\t\v", str_val); -} - -TEST(JSONReaderTest, UnicodeEscapes) { - // Test hex and unicode escapes including the null character. - std::unique_ptr root = - JSONReader().ReadToValue("\"\\x41\\x00\\u1234\\u0000\""); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ(std::wstring(L"A\0\x1234\0", 4), UTF8ToWide(str_val)); -} - -TEST(JSONReaderTest, InvalidStrings) { - EXPECT_FALSE(JSONReader().ReadToValue("\"no closing quote")); - EXPECT_FALSE(JSONReader().ReadToValue("\"\\z invalid escape char\"")); - EXPECT_FALSE(JSONReader().ReadToValue("\"\\xAQ invalid hex code\"")); - EXPECT_FALSE(JSONReader().ReadToValue("not enough hex chars\\x1\"")); - EXPECT_FALSE(JSONReader().ReadToValue("\"not enough escape chars\\u123\"")); - EXPECT_FALSE( - JSONReader().ReadToValue("\"extra backslash at end of input\\\"")); -} - -TEST(JSONReaderTest, BasicArray) { - std::unique_ptr list = - ListValue::From(JSONReader::Read("[true, false, null]")); - ASSERT_TRUE(list); - EXPECT_EQ(3U, list->GetSize()); - - // Test with trailing comma. Should be parsed the same as above. - std::unique_ptr root2 = - JSONReader::Read("[true, false, null, ]", JSON_ALLOW_TRAILING_COMMAS); - EXPECT_TRUE(list->Equals(root2.get())); -} - -TEST(JSONReaderTest, EmptyArray) { - std::unique_ptr list = ListValue::From(JSONReader::Read("[]")); - ASSERT_TRUE(list); - EXPECT_EQ(0U, list->GetSize()); -} - -TEST(JSONReaderTest, NestedArrays) { - std::unique_ptr list = ListValue::From( - JSONReader::Read("[[true], [], [false, [], [null]], null]")); - ASSERT_TRUE(list); - EXPECT_EQ(4U, list->GetSize()); - - // Lots of trailing commas. - std::unique_ptr root2 = - JSONReader::Read("[[true], [], [false, [], [null, ] , ], null,]", - JSON_ALLOW_TRAILING_COMMAS); - EXPECT_TRUE(list->Equals(root2.get())); -} - -TEST(JSONReaderTest, InvalidArrays) { - // Missing close brace. - EXPECT_FALSE(JSONReader::Read("[[true], [], [false, [], [null]], null")); - - // Too many commas. - EXPECT_FALSE(JSONReader::Read("[true,, null]")); - EXPECT_FALSE(JSONReader::Read("[true,, null]", JSON_ALLOW_TRAILING_COMMAS)); - - // No commas. - EXPECT_FALSE(JSONReader::Read("[true null]")); - - // Trailing comma. - EXPECT_FALSE(JSONReader::Read("[true,]")); -} - -TEST(JSONReaderTest, ArrayTrailingComma) { - // Valid if we set |allow_trailing_comma| to true. - std::unique_ptr list = - ListValue::From(JSONReader::Read("[true,]", JSON_ALLOW_TRAILING_COMMAS)); - ASSERT_TRUE(list); - EXPECT_EQ(1U, list->GetSize()); - Value* tmp_value = nullptr; - ASSERT_TRUE(list->Get(0, &tmp_value)); - EXPECT_TRUE(tmp_value->is_bool()); - bool bool_value = false; - EXPECT_TRUE(tmp_value->GetAsBoolean(&bool_value)); - EXPECT_TRUE(bool_value); -} - -TEST(JSONReaderTest, ArrayTrailingCommaNoEmptyElements) { - // Don't allow empty elements, even if |allow_trailing_comma| is - // true. - EXPECT_FALSE(JSONReader::Read("[,]", JSON_ALLOW_TRAILING_COMMAS)); - EXPECT_FALSE(JSONReader::Read("[true,,]", JSON_ALLOW_TRAILING_COMMAS)); - EXPECT_FALSE(JSONReader::Read("[,true,]", JSON_ALLOW_TRAILING_COMMAS)); - EXPECT_FALSE(JSONReader::Read("[true,,false]", JSON_ALLOW_TRAILING_COMMAS)); -} - -TEST(JSONReaderTest, EmptyDictionary) { - std::unique_ptr dict_val = - DictionaryValue::From(JSONReader::Read("{}")); - ASSERT_TRUE(dict_val); -} - -TEST(JSONReaderTest, CompleteDictionary) { - auto dict_val = DictionaryValue::From(JSONReader::Read( - "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\" }")); - ASSERT_TRUE(dict_val); - double double_val = 0.0; - EXPECT_TRUE(dict_val->GetDouble("number", &double_val)); - EXPECT_DOUBLE_EQ(9.87654321, double_val); - Value* null_val = nullptr; - ASSERT_TRUE(dict_val->Get("null", &null_val)); - EXPECT_TRUE(null_val->is_none()); - std::string str_val; - EXPECT_TRUE(dict_val->GetString("S", &str_val)); - EXPECT_EQ("str", str_val); - - std::unique_ptr root2 = JSONReader::Read( - "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", }", - JSON_ALLOW_TRAILING_COMMAS); - ASSERT_TRUE(root2); - EXPECT_TRUE(dict_val->Equals(root2.get())); - - // Test newline equivalence. - root2 = JSONReader::Read( - "{\n" - " \"number\":9.87654321,\n" - " \"null\":null,\n" - " \"\\x53\":\"str\",\n" - "}\n", - JSON_ALLOW_TRAILING_COMMAS); - ASSERT_TRUE(root2); - EXPECT_TRUE(dict_val->Equals(root2.get())); - - root2 = JSONReader::Read( - "{\r\n" - " \"number\":9.87654321,\r\n" - " \"null\":null,\r\n" - " \"\\x53\":\"str\",\r\n" - "}\r\n", - JSON_ALLOW_TRAILING_COMMAS); - ASSERT_TRUE(root2); - EXPECT_TRUE(dict_val->Equals(root2.get())); -} - -TEST(JSONReaderTest, NestedDictionaries) { - std::unique_ptr dict_val = - DictionaryValue::From(JSONReader::Read( - "{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}")); - ASSERT_TRUE(dict_val); - DictionaryValue* inner_dict = nullptr; - ASSERT_TRUE(dict_val->GetDictionary("inner", &inner_dict)); - ListValue* inner_array = nullptr; - ASSERT_TRUE(inner_dict->GetList("array", &inner_array)); - EXPECT_EQ(1U, inner_array->GetSize()); - bool bool_value = true; - EXPECT_TRUE(dict_val->GetBoolean("false", &bool_value)); - EXPECT_FALSE(bool_value); - inner_dict = nullptr; - EXPECT_TRUE(dict_val->GetDictionary("d", &inner_dict)); - - std::unique_ptr root2 = JSONReader::Read( - "{\"inner\": {\"array\":[true] , },\"false\":false,\"d\":{},}", - JSON_ALLOW_TRAILING_COMMAS); - EXPECT_TRUE(dict_val->Equals(root2.get())); -} - -TEST(JSONReaderTest, DictionaryKeysWithPeriods) { - std::unique_ptr dict_val = DictionaryValue::From( - JSONReader::Read("{\"a.b\":3,\"c\":2,\"d.e.f\":{\"g.h.i.j\":1}}")); - ASSERT_TRUE(dict_val); - int integer_value = 0; - EXPECT_TRUE(dict_val->GetIntegerWithoutPathExpansion("a.b", &integer_value)); - EXPECT_EQ(3, integer_value); - EXPECT_TRUE(dict_val->GetIntegerWithoutPathExpansion("c", &integer_value)); - EXPECT_EQ(2, integer_value); - DictionaryValue* inner_dict = nullptr; - ASSERT_TRUE( - dict_val->GetDictionaryWithoutPathExpansion("d.e.f", &inner_dict)); - EXPECT_EQ(1U, inner_dict->size()); - EXPECT_TRUE( - inner_dict->GetIntegerWithoutPathExpansion("g.h.i.j", &integer_value)); - EXPECT_EQ(1, integer_value); - - dict_val = - DictionaryValue::From(JSONReader::Read("{\"a\":{\"b\":2},\"a.b\":1}")); - ASSERT_TRUE(dict_val); - EXPECT_TRUE(dict_val->GetInteger("a.b", &integer_value)); - EXPECT_EQ(2, integer_value); - EXPECT_TRUE(dict_val->GetIntegerWithoutPathExpansion("a.b", &integer_value)); - EXPECT_EQ(1, integer_value); -} - -TEST(JSONReaderTest, InvalidDictionaries) { - // No closing brace. - EXPECT_FALSE(JSONReader::Read("{\"a\": true")); - - // Keys must be quoted strings. - EXPECT_FALSE(JSONReader::Read("{foo:true}")); - EXPECT_FALSE(JSONReader::Read("{1234: false}")); - EXPECT_FALSE(JSONReader::Read("{:false}")); - - // Trailing comma. - EXPECT_FALSE(JSONReader::Read("{\"a\":true,}")); - - // Too many commas. - EXPECT_FALSE(JSONReader::Read("{\"a\":true,,\"b\":false}")); - EXPECT_FALSE(JSONReader::Read("{\"a\":true,,\"b\":false}", - JSON_ALLOW_TRAILING_COMMAS)); - - // No separator. - EXPECT_FALSE(JSONReader::Read("{\"a\" \"b\"}")); - - // Lone comma. - EXPECT_FALSE(JSONReader::Read("{,}")); - EXPECT_FALSE(JSONReader::Read("{,}", JSON_ALLOW_TRAILING_COMMAS)); - EXPECT_FALSE(JSONReader::Read("{\"a\":true,,}", JSON_ALLOW_TRAILING_COMMAS)); - EXPECT_FALSE(JSONReader::Read("{,\"a\":true}", JSON_ALLOW_TRAILING_COMMAS)); - EXPECT_FALSE(JSONReader::Read("{\"a\":true,,\"b\":false}", - JSON_ALLOW_TRAILING_COMMAS)); -} - -TEST(JSONReaderTest, StackOverflow) { - std::string evil(1000000, '['); - evil.append(std::string(1000000, ']')); - EXPECT_FALSE(JSONReader::Read(evil)); - - // A few thousand adjacent lists is fine. - std::string not_evil("["); - not_evil.reserve(15010); - for (int i = 0; i < 5000; ++i) - not_evil.append("[],"); - not_evil.append("[]]"); - std::unique_ptr list = ListValue::From(JSONReader::Read(not_evil)); - ASSERT_TRUE(list); - EXPECT_EQ(5001U, list->GetSize()); -} - -TEST(JSONReaderTest, UTF8Input) { - std::unique_ptr root = - JSONReader().ReadToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\""); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ(L"\x7f51\x9875", UTF8ToWide(str_val)); - - std::unique_ptr dict_val = - DictionaryValue::From(JSONReader().ReadToValue( - "{\"path\": \"/tmp/\xc3\xa0\xc3\xa8\xc3\xb2.png\"}")); - ASSERT_TRUE(dict_val); - EXPECT_TRUE(dict_val->GetString("path", &str_val)); - EXPECT_EQ("/tmp/\xC3\xA0\xC3\xA8\xC3\xB2.png", str_val); -} - -TEST(JSONReaderTest, InvalidUTF8Input) { - EXPECT_FALSE(JSONReader().ReadToValue("\"345\xb0\xa1\xb0\xa2\"")); - EXPECT_FALSE(JSONReader().ReadToValue("\"123\xc0\x81\"")); - EXPECT_FALSE(JSONReader().ReadToValue("\"abc\xc0\xae\"")); -} - -TEST(JSONReaderTest, UTF16Escapes) { - std::unique_ptr root = JSONReader().ReadToValue("\"\\u20ac3,14\""); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ( - "\xe2\x82\xac" - "3,14", - str_val); - - root = JSONReader().ReadToValue("\"\\ud83d\\udca9\\ud83d\\udc6c\""); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_string()); - str_val.clear(); - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ("\xf0\x9f\x92\xa9\xf0\x9f\x91\xac", str_val); -} - -TEST(JSONReaderTest, InvalidUTF16Escapes) { - const char* const cases[] = { - "\"\\u123\"", // Invalid scalar. - "\"\\ud83d\"", // Invalid scalar. - "\"\\u$%@!\"", // Invalid scalar. - "\"\\uzz89\"", // Invalid scalar. - "\"\\ud83d\\udca\"", // Invalid lower surrogate. - "\"\\ud83d\\ud83d\"", // Invalid lower surrogate. - "\"\\ud83d\\uaaaZ\"" // Invalid lower surrogate. - "\"\\ud83foo\"", // No lower surrogate. - "\"\\ud83d\\foo\"" // No lower surrogate. - "\"\\ud83\\foo\"" // Invalid upper surrogate. - "\"\\ud83d\\u1\"" // No lower surrogate. - "\"\\ud83\\u1\"" // Invalid upper surrogate. - }; - std::unique_ptr root; - for (size_t i = 0; i < arraysize(cases); ++i) { - root = JSONReader().ReadToValue(cases[i]); - EXPECT_FALSE(root) << cases[i]; - } -} - -TEST(JSONReaderTest, LiteralRoots) { - std::unique_ptr root = JSONReader::Read("null"); - ASSERT_TRUE(root); - EXPECT_TRUE(root->is_none()); - - root = JSONReader::Read("true"); - ASSERT_TRUE(root); - bool bool_value; - EXPECT_TRUE(root->GetAsBoolean(&bool_value)); - EXPECT_TRUE(bool_value); - - root = JSONReader::Read("10"); - ASSERT_TRUE(root); - int integer_value; - EXPECT_TRUE(root->GetAsInteger(&integer_value)); - EXPECT_EQ(10, integer_value); - - root = JSONReader::Read("\"root\""); - ASSERT_TRUE(root); - std::string str_val; - EXPECT_TRUE(root->GetAsString(&str_val)); - EXPECT_EQ("root", str_val); -} - -TEST(JSONReaderTest, ReadFromFile) { - FilePath path; - ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &path)); - path = path.AppendASCII("json"); - ASSERT_TRUE(base::PathExists(path)); - - std::string input; - ASSERT_TRUE(ReadFileToString(path.AppendASCII("bom_feff.json"), &input)); - - JSONReader reader; - std::unique_ptr root(reader.ReadToValue(input)); - ASSERT_TRUE(root) << reader.GetErrorMessage(); - EXPECT_TRUE(root->is_dict()); -} - -// Tests that the root of a JSON object can be deleted safely while its -// children outlive it. -TEST(JSONReaderTest, StringOptimizations) { - std::unique_ptr dict_literal_0; - std::unique_ptr dict_literal_1; - std::unique_ptr dict_string_0; - std::unique_ptr dict_string_1; - std::unique_ptr list_value_0; - std::unique_ptr list_value_1; - - { - std::unique_ptr root = JSONReader::Read( - "{" - " \"test\": {" - " \"foo\": true," - " \"bar\": 3.14," - " \"baz\": \"bat\"," - " \"moo\": \"cow\"" - " }," - " \"list\": [" - " \"a\"," - " \"b\"" - " ]" - "}", - JSON_PARSE_RFC); - ASSERT_TRUE(root); - - DictionaryValue* root_dict = nullptr; - ASSERT_TRUE(root->GetAsDictionary(&root_dict)); - - DictionaryValue* dict = nullptr; - ListValue* list = nullptr; - - ASSERT_TRUE(root_dict->GetDictionary("test", &dict)); - ASSERT_TRUE(root_dict->GetList("list", &list)); - - ASSERT_TRUE(dict->Remove("foo", &dict_literal_0)); - ASSERT_TRUE(dict->Remove("bar", &dict_literal_1)); - ASSERT_TRUE(dict->Remove("baz", &dict_string_0)); - ASSERT_TRUE(dict->Remove("moo", &dict_string_1)); - - ASSERT_EQ(2u, list->GetSize()); - ASSERT_TRUE(list->Remove(0, &list_value_0)); - ASSERT_TRUE(list->Remove(0, &list_value_1)); - } - - bool b = false; - double d = 0; - std::string s; - - EXPECT_TRUE(dict_literal_0->GetAsBoolean(&b)); - EXPECT_TRUE(b); - - EXPECT_TRUE(dict_literal_1->GetAsDouble(&d)); - EXPECT_EQ(3.14, d); - - EXPECT_TRUE(dict_string_0->GetAsString(&s)); - EXPECT_EQ("bat", s); - - EXPECT_TRUE(dict_string_1->GetAsString(&s)); - EXPECT_EQ("cow", s); - - EXPECT_TRUE(list_value_0->GetAsString(&s)); - EXPECT_EQ("a", s); - EXPECT_TRUE(list_value_1->GetAsString(&s)); - EXPECT_EQ("b", s); -} - -// A smattering of invalid JSON designed to test specific portions of the -// parser implementation against buffer overflow. Best run with DCHECKs so -// that the one in NextChar fires. -TEST(JSONReaderTest, InvalidSanity) { - const char* const kInvalidJson[] = { - "/* test *", "{\"foo\"", "{\"foo\":", " [", "\"\\u123g\"", "{\n\"eh:\n}", - }; - - for (size_t i = 0; i < arraysize(kInvalidJson); ++i) { - JSONReader reader; - LOG(INFO) << "Sanity test " << i << ": <" << kInvalidJson[i] << ">"; - EXPECT_FALSE(reader.ReadToValue(kInvalidJson[i])); - EXPECT_NE(JSONReader::JSON_NO_ERROR, reader.error_code()); - EXPECT_NE("", reader.GetErrorMessage()); - } -} - -TEST(JSONReaderTest, IllegalTrailingNull) { - const char json[] = { '"', 'n', 'u', 'l', 'l', '"', '\0' }; - std::string json_string(json, sizeof(json)); - JSONReader reader; - EXPECT_FALSE(reader.ReadToValue(json_string)); - EXPECT_EQ(JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT, reader.error_code()); -} - -TEST(JSONReaderTest, MaxNesting) { - std::string json(R"({"outer": { "inner": {"foo": true}}})"); - std::unique_ptr root; - root = JSONReader::Read(json, JSON_PARSE_RFC, 3); - ASSERT_FALSE(root); - root = JSONReader::Read(json, JSON_PARSE_RFC, 4); - ASSERT_TRUE(root); -} - -} // namespace base diff --git a/json/json_string_value_serializer.cc b/json/json_string_value_serializer.cc deleted file mode 100644 index f9c45a40d..000000000 --- a/json/json_string_value_serializer.cc +++ /dev/null @@ -1,55 +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/json/json_string_value_serializer.h" - -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/logging.h" - -using base::Value; - -JSONStringValueSerializer::JSONStringValueSerializer(std::string* json_string) - : json_string_(json_string), - pretty_print_(false) { -} - -JSONStringValueSerializer::~JSONStringValueSerializer() = default; - -bool JSONStringValueSerializer::Serialize(const Value& root) { - return SerializeInternal(root, false); -} - -bool JSONStringValueSerializer::SerializeAndOmitBinaryValues( - const Value& root) { - return SerializeInternal(root, true); -} - -bool JSONStringValueSerializer::SerializeInternal(const Value& root, - bool omit_binary_values) { - if (!json_string_) - return false; - - int options = 0; - if (omit_binary_values) - options |= base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES; - if (pretty_print_) - options |= base::JSONWriter::OPTIONS_PRETTY_PRINT; - - return base::JSONWriter::WriteWithOptions(root, options, json_string_); -} - -JSONStringValueDeserializer::JSONStringValueDeserializer( - const base::StringPiece& json_string, - int options) - : json_string_(json_string), options_(options) {} - -JSONStringValueDeserializer::~JSONStringValueDeserializer() = default; - -std::unique_ptr JSONStringValueDeserializer::Deserialize( - int* error_code, - std::string* error_str) { - return base::JSONReader::ReadAndReturnError(json_string_, options_, - error_code, error_str); -} diff --git a/json/json_string_value_serializer.h b/json/json_string_value_serializer.h deleted file mode 100644 index 55a53e207..000000000 --- a/json/json_string_value_serializer.h +++ /dev/null @@ -1,75 +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_JSON_JSON_STRING_VALUE_SERIALIZER_H_ -#define BASE_JSON_JSON_STRING_VALUE_SERIALIZER_H_ - -#include - -#include "base/base_export.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" -#include "base/values.h" - -class BASE_EXPORT JSONStringValueSerializer : public base::ValueSerializer { - public: - // |json_string| is the string that will be the destination of the - // serialization. The caller of the constructor retains ownership of the - // string. |json_string| must not be null. - explicit JSONStringValueSerializer(std::string* json_string); - - ~JSONStringValueSerializer() override; - - // Attempt to serialize the data structure represented by Value into - // JSON. If the return value is true, the result will have been written - // into the string passed into the constructor. - bool Serialize(const base::Value& root) override; - - // Equivalent to Serialize(root) except binary values are omitted from the - // output. - bool SerializeAndOmitBinaryValues(const base::Value& root); - - void set_pretty_print(bool new_value) { pretty_print_ = new_value; } - bool pretty_print() { return pretty_print_; } - - private: - bool SerializeInternal(const base::Value& root, bool omit_binary_values); - - // Owned by the caller of the constructor. - std::string* json_string_; - bool pretty_print_; // If true, serialization will span multiple lines. - - DISALLOW_COPY_AND_ASSIGN(JSONStringValueSerializer); -}; - -class BASE_EXPORT JSONStringValueDeserializer : public base::ValueDeserializer { - public: - // This retains a reference to the contents of |json_string|, so the data - // must outlive the JSONStringValueDeserializer. |options| is a bitmask of - // JSONParserOptions. - explicit JSONStringValueDeserializer(const base::StringPiece& json_string, - int options = 0); - - ~JSONStringValueDeserializer() override; - - // Attempt to deserialize the data structure encoded in the string passed - // in to the constructor into a structure of Value objects. If the return - // value is null, and if |error_code| is non-null, |error_code| will - // contain an integer error code (a JsonParseError in this case). - // If |error_message| is non-null, it will be filled in with a formatted - // error message including the location of the error if appropriate. - // The caller takes ownership of the returned value. - std::unique_ptr Deserialize(int* error_code, - std::string* error_message) override; - - private: - // Data is owned by the caller of the constructor. - base::StringPiece json_string_; - const int options_; - - DISALLOW_COPY_AND_ASSIGN(JSONStringValueDeserializer); -}; - -#endif // BASE_JSON_JSON_STRING_VALUE_SERIALIZER_H_ diff --git a/json/json_value_converter.cc b/json/json_value_converter.cc deleted file mode 100644 index 6f772f366..000000000 --- a/json/json_value_converter.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/json/json_value_converter.h" - -namespace base { -namespace internal { - -bool BasicValueConverter::Convert( - const base::Value& value, int* field) const { - return value.GetAsInteger(field); -} - -bool BasicValueConverter::Convert( - const base::Value& value, std::string* field) const { - return value.GetAsString(field); -} - -bool BasicValueConverter::Convert( - const base::Value& value, string16* field) const { - return value.GetAsString(field); -} - -bool BasicValueConverter::Convert( - const base::Value& value, double* field) const { - return value.GetAsDouble(field); -} - -bool BasicValueConverter::Convert( - const base::Value& value, bool* field) const { - return value.GetAsBoolean(field); -} - -} // namespace internal -} // namespace base - diff --git a/json/json_value_converter.h b/json/json_value_converter.h deleted file mode 100644 index ef0811501..000000000 --- a/json/json_value_converter.h +++ /dev/null @@ -1,524 +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_JSON_JSON_VALUE_CONVERTER_H_ -#define BASE_JSON_JSON_VALUE_CONVERTER_H_ - -#include - -#include -#include -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/strings/string16.h" -#include "base/strings/string_piece.h" -#include "base/values.h" - -// JSONValueConverter converts a JSON value into a C++ struct in a -// lightweight way. -// -// Usage: -// For real examples, you may want to refer to _unittest.cc file. -// -// Assume that you have a struct like this: -// struct Message { -// int foo; -// std::string bar; -// static void RegisterJSONConverter( -// JSONValueConverter* converter); -// }; -// -// And you want to parse a json data into this struct. First, you -// need to declare RegisterJSONConverter() method in your struct. -// // static -// void Message::RegisterJSONConverter( -// JSONValueConverter* converter) { -// converter->RegisterIntField("foo", &Message::foo); -// converter->RegisterStringField("bar", &Message::bar); -// } -// -// Then, you just instantiate your JSONValueConverter of your type and call -// Convert() method. -// Message message; -// JSONValueConverter converter; -// converter.Convert(json, &message); -// -// Convert() returns false when it fails. Here "fail" means that the value is -// structurally different from expected, such like a string value appears -// for an int field. Do not report failures for missing fields. -// Also note that Convert() will modify the passed |message| even when it -// fails for performance reason. -// -// For nested field, the internal message also has to implement the registration -// method. Then, just use RegisterNestedField() from the containing struct's -// RegisterJSONConverter method. -// struct Nested { -// Message foo; -// static void RegisterJSONConverter(...) { -// ... -// converter->RegisterNestedField("foo", &Nested::foo); -// } -// }; -// -// For repeated field, we just assume std::vector> -// for its container and you can put RegisterRepeatedInt or some other types. -// Use RegisterRepeatedMessage for nested repeated fields. -// -// Sometimes JSON format uses string representations for other types such -// like enum, timestamp, or URL. You can use RegisterCustomField method -// and specify a function to convert a StringPiece to your type. -// bool ConvertFunc(StringPiece s, YourEnum* result) { -// // do something and return true if succeed... -// } -// struct Message { -// YourEnum ye; -// ... -// static void RegisterJSONConverter(...) { -// ... -// converter->RegsiterCustomField( -// "your_enum", &Message::ye, &ConvertFunc); -// } -// }; - -namespace base { - -template -class JSONValueConverter; - -namespace internal { - -template -class FieldConverterBase { - public: - explicit FieldConverterBase(const std::string& path) : field_path_(path) {} - virtual ~FieldConverterBase() = default; - virtual bool ConvertField(const base::Value& value, StructType* obj) - const = 0; - const std::string& field_path() const { return field_path_; } - - private: - std::string field_path_; - DISALLOW_COPY_AND_ASSIGN(FieldConverterBase); -}; - -template -class ValueConverter { - public: - virtual ~ValueConverter() = default; - virtual bool Convert(const base::Value& value, FieldType* field) const = 0; -}; - -template -class FieldConverter : public FieldConverterBase { - public: - explicit FieldConverter(const std::string& path, - FieldType StructType::* field, - ValueConverter* converter) - : FieldConverterBase(path), - field_pointer_(field), - value_converter_(converter) { - } - - bool ConvertField(const base::Value& value, StructType* dst) const override { - return value_converter_->Convert(value, &(dst->*field_pointer_)); - } - - private: - FieldType StructType::* field_pointer_; - std::unique_ptr> value_converter_; - DISALLOW_COPY_AND_ASSIGN(FieldConverter); -}; - -template -class BasicValueConverter; - -template <> -class BASE_EXPORT BasicValueConverter : public ValueConverter { - public: - BasicValueConverter() = default; - - bool Convert(const base::Value& value, int* field) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); -}; - -template <> -class BASE_EXPORT BasicValueConverter - : public ValueConverter { - public: - BasicValueConverter() = default; - - bool Convert(const base::Value& value, std::string* field) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); -}; - -template <> -class BASE_EXPORT BasicValueConverter - : public ValueConverter { - public: - BasicValueConverter() = default; - - bool Convert(const base::Value& value, string16* field) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); -}; - -template <> -class BASE_EXPORT BasicValueConverter : public ValueConverter { - public: - BasicValueConverter() = default; - - bool Convert(const base::Value& value, double* field) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); -}; - -template <> -class BASE_EXPORT BasicValueConverter : public ValueConverter { - public: - BasicValueConverter() = default; - - bool Convert(const base::Value& value, bool* field) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(BasicValueConverter); -}; - -template -class ValueFieldConverter : public ValueConverter { - public: - typedef bool(*ConvertFunc)(const base::Value* value, FieldType* field); - - explicit ValueFieldConverter(ConvertFunc convert_func) - : convert_func_(convert_func) {} - - bool Convert(const base::Value& value, FieldType* field) const override { - return convert_func_(&value, field); - } - - private: - ConvertFunc convert_func_; - - DISALLOW_COPY_AND_ASSIGN(ValueFieldConverter); -}; - -template -class CustomFieldConverter : public ValueConverter { - public: - typedef bool (*ConvertFunc)(StringPiece value, FieldType* field); - - explicit CustomFieldConverter(ConvertFunc convert_func) - : convert_func_(convert_func) {} - - bool Convert(const base::Value& value, FieldType* field) const override { - std::string string_value; - return value.GetAsString(&string_value) && - convert_func_(string_value, field); - } - - private: - ConvertFunc convert_func_; - - DISALLOW_COPY_AND_ASSIGN(CustomFieldConverter); -}; - -template -class NestedValueConverter : public ValueConverter { - public: - NestedValueConverter() = default; - - bool Convert(const base::Value& value, NestedType* field) const override { - return converter_.Convert(value, field); - } - - private: - JSONValueConverter converter_; - DISALLOW_COPY_AND_ASSIGN(NestedValueConverter); -}; - -template -class RepeatedValueConverter - : public ValueConverter>> { - public: - RepeatedValueConverter() = default; - - bool Convert(const base::Value& value, - std::vector>* field) const override { - const base::ListValue* list = NULL; - if (!value.GetAsList(&list)) { - // The field is not a list. - return false; - } - - field->reserve(list->GetSize()); - for (size_t i = 0; i < list->GetSize(); ++i) { - const base::Value* element = NULL; - if (!list->Get(i, &element)) - continue; - - std::unique_ptr e(new Element); - if (basic_converter_.Convert(*element, e.get())) { - field->push_back(std::move(e)); - } else { - DVLOG(1) << "failure at " << i << "-th element"; - return false; - } - } - return true; - } - - private: - BasicValueConverter basic_converter_; - DISALLOW_COPY_AND_ASSIGN(RepeatedValueConverter); -}; - -template -class RepeatedMessageConverter - : public ValueConverter>> { - public: - RepeatedMessageConverter() = default; - - bool Convert(const base::Value& value, - std::vector>* field) const override { - const base::ListValue* list = NULL; - if (!value.GetAsList(&list)) - return false; - - field->reserve(list->GetSize()); - for (size_t i = 0; i < list->GetSize(); ++i) { - const base::Value* element = NULL; - if (!list->Get(i, &element)) - continue; - - std::unique_ptr nested(new NestedType); - if (converter_.Convert(*element, nested.get())) { - field->push_back(std::move(nested)); - } else { - DVLOG(1) << "failure at " << i << "-th element"; - return false; - } - } - return true; - } - - private: - JSONValueConverter converter_; - DISALLOW_COPY_AND_ASSIGN(RepeatedMessageConverter); -}; - -template -class RepeatedCustomValueConverter - : public ValueConverter>> { - public: - typedef bool(*ConvertFunc)(const base::Value* value, NestedType* field); - - explicit RepeatedCustomValueConverter(ConvertFunc convert_func) - : convert_func_(convert_func) {} - - bool Convert(const base::Value& value, - std::vector>* field) const override { - const base::ListValue* list = NULL; - if (!value.GetAsList(&list)) - return false; - - field->reserve(list->GetSize()); - for (size_t i = 0; i < list->GetSize(); ++i) { - const base::Value* element = NULL; - if (!list->Get(i, &element)) - continue; - - std::unique_ptr nested(new NestedType); - if ((*convert_func_)(element, nested.get())) { - field->push_back(std::move(nested)); - } else { - DVLOG(1) << "failure at " << i << "-th element"; - return false; - } - } - return true; - } - - private: - ConvertFunc convert_func_; - DISALLOW_COPY_AND_ASSIGN(RepeatedCustomValueConverter); -}; - - -} // namespace internal - -template -class JSONValueConverter { - public: - JSONValueConverter() { - StructType::RegisterJSONConverter(this); - } - - void RegisterIntField(const std::string& field_name, - int StructType::* field) { - fields_.push_back( - std::make_unique>( - field_name, field, new internal::BasicValueConverter)); - } - - void RegisterStringField(const std::string& field_name, - std::string StructType::* field) { - fields_.push_back( - std::make_unique>( - field_name, field, new internal::BasicValueConverter)); - } - - void RegisterStringField(const std::string& field_name, - string16 StructType::* field) { - fields_.push_back( - std::make_unique>( - field_name, field, new internal::BasicValueConverter)); - } - - void RegisterBoolField(const std::string& field_name, - bool StructType::* field) { - fields_.push_back( - std::make_unique>( - field_name, field, new internal::BasicValueConverter)); - } - - void RegisterDoubleField(const std::string& field_name, - double StructType::* field) { - fields_.push_back( - std::make_unique>( - field_name, field, new internal::BasicValueConverter)); - } - - template - void RegisterNestedField( - const std::string& field_name, NestedType StructType::* field) { - fields_.push_back( - std::make_unique>( - field_name, field, new internal::NestedValueConverter)); - } - - template - void RegisterCustomField(const std::string& field_name, - FieldType StructType::*field, - bool (*convert_func)(StringPiece, FieldType*)) { - fields_.push_back( - std::make_unique>( - field_name, field, - new internal::CustomFieldConverter(convert_func))); - } - - template - void RegisterCustomValueField( - const std::string& field_name, - FieldType StructType::* field, - bool (*convert_func)(const base::Value*, FieldType*)) { - fields_.push_back( - std::make_unique>( - field_name, field, - new internal::ValueFieldConverter(convert_func))); - } - - void RegisterRepeatedInt( - const std::string& field_name, - std::vector> StructType::*field) { - fields_.push_back(std::make_unique>>>( - field_name, field, new internal::RepeatedValueConverter)); - } - - void RegisterRepeatedString( - const std::string& field_name, - std::vector> StructType::*field) { - fields_.push_back( - std::make_unique>>>( - field_name, field, - new internal::RepeatedValueConverter)); - } - - void RegisterRepeatedString( - const std::string& field_name, - std::vector> StructType::*field) { - fields_.push_back(std::make_unique>>>( - field_name, field, new internal::RepeatedValueConverter)); - } - - void RegisterRepeatedDouble( - const std::string& field_name, - std::vector> StructType::*field) { - fields_.push_back(std::make_unique>>>( - field_name, field, new internal::RepeatedValueConverter)); - } - - void RegisterRepeatedBool( - const std::string& field_name, - std::vector> StructType::*field) { - fields_.push_back(std::make_unique>>>( - field_name, field, new internal::RepeatedValueConverter)); - } - - template - void RegisterRepeatedCustomValue( - const std::string& field_name, - std::vector> StructType::*field, - bool (*convert_func)(const base::Value*, NestedType*)) { - fields_.push_back( - std::make_unique>>>( - field_name, field, - new internal::RepeatedCustomValueConverter( - convert_func))); - } - - template - void RegisterRepeatedMessage( - const std::string& field_name, - std::vector> StructType::*field) { - fields_.push_back( - std::make_unique>>>( - field_name, field, - new internal::RepeatedMessageConverter)); - } - - bool Convert(const base::Value& value, StructType* output) const { - const DictionaryValue* dictionary_value = NULL; - if (!value.GetAsDictionary(&dictionary_value)) - return false; - - for (size_t i = 0; i < fields_.size(); ++i) { - const internal::FieldConverterBase* field_converter = - fields_[i].get(); - const base::Value* field = NULL; - if (dictionary_value->Get(field_converter->field_path(), &field)) { - if (!field_converter->ConvertField(*field, output)) { - DVLOG(1) << "failure at field " << field_converter->field_path(); - return false; - } - } - } - return true; - } - - private: - std::vector>> - fields_; - - DISALLOW_COPY_AND_ASSIGN(JSONValueConverter); -}; - -} // namespace base - -#endif // BASE_JSON_JSON_VALUE_CONVERTER_H_ diff --git a/json/json_value_converter_unittest.cc b/json/json_value_converter_unittest.cc deleted file mode 100644 index 322f5f0a3..000000000 --- a/json/json_value_converter_unittest.cc +++ /dev/null @@ -1,255 +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/json/json_value_converter.h" - -#include -#include -#include - -#include "base/json/json_reader.h" -#include "base/strings/string_piece.h" -#include "base/values.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -// Very simple messages. -struct SimpleMessage { - enum SimpleEnum { - FOO, BAR, - }; - int foo; - std::string bar; - bool baz; - bool bstruct; - SimpleEnum simple_enum; - std::vector> ints; - std::vector> string_values; - SimpleMessage() : foo(0), baz(false), bstruct(false), simple_enum(FOO) {} - - static bool ParseSimpleEnum(StringPiece value, SimpleEnum* field) { - if (value == "foo") { - *field = FOO; - return true; - } else if (value == "bar") { - *field = BAR; - return true; - } - return false; - } - - static bool HasFieldPresent(const base::Value* value, bool* result) { - *result = value != nullptr; - return true; - } - - static bool GetValueString(const base::Value* value, std::string* result) { - const base::DictionaryValue* dict = nullptr; - if (!value->GetAsDictionary(&dict)) - return false; - - if (!dict->GetString("val", result)) - return false; - - return true; - } - - static void RegisterJSONConverter( - base::JSONValueConverter* converter) { - converter->RegisterIntField("foo", &SimpleMessage::foo); - converter->RegisterStringField("bar", &SimpleMessage::bar); - converter->RegisterBoolField("baz", &SimpleMessage::baz); - converter->RegisterCustomField( - "simple_enum", &SimpleMessage::simple_enum, &ParseSimpleEnum); - converter->RegisterRepeatedInt("ints", &SimpleMessage::ints); - converter->RegisterCustomValueField("bstruct", - &SimpleMessage::bstruct, - &HasFieldPresent); - converter->RegisterRepeatedCustomValue( - "string_values", - &SimpleMessage::string_values, - &GetValueString); - } -}; - -// For nested messages. -struct NestedMessage { - double foo; - SimpleMessage child; - std::vector> children; - - NestedMessage() : foo(0) {} - - static void RegisterJSONConverter( - base::JSONValueConverter* converter) { - converter->RegisterDoubleField("foo", &NestedMessage::foo); - converter->RegisterNestedField("child", &NestedMessage::child); - converter->RegisterRepeatedMessage("children", &NestedMessage::children); - } -}; - -} // namespace - -TEST(JSONValueConverterTest, ParseSimpleMessage) { - const char normal_data[] = - "{\n" - " \"foo\": 1,\n" - " \"bar\": \"bar\",\n" - " \"baz\": true,\n" - " \"bstruct\": {},\n" - " \"string_values\": [{\"val\": \"value_1\"}, {\"val\": \"value_2\"}]," - " \"simple_enum\": \"foo\"," - " \"ints\": [1, 2]" - "}\n"; - - std::unique_ptr value = base::JSONReader::Read(normal_data); - SimpleMessage message; - base::JSONValueConverter converter; - EXPECT_TRUE(converter.Convert(*value.get(), &message)); - - EXPECT_EQ(1, message.foo); - EXPECT_EQ("bar", message.bar); - EXPECT_TRUE(message.baz); - EXPECT_EQ(SimpleMessage::FOO, message.simple_enum); - EXPECT_EQ(2, static_cast(message.ints.size())); - ASSERT_EQ(2U, message.string_values.size()); - EXPECT_EQ("value_1", *message.string_values[0]); - EXPECT_EQ("value_2", *message.string_values[1]); - EXPECT_EQ(1, *(message.ints[0])); - EXPECT_EQ(2, *(message.ints[1])); -} - -TEST(JSONValueConverterTest, ParseNestedMessage) { - const char normal_data[] = - "{\n" - " \"foo\": 1.0,\n" - " \"child\": {\n" - " \"foo\": 1,\n" - " \"bar\": \"bar\",\n" - " \"bstruct\": {},\n" - " \"string_values\": [{\"val\": \"value_1\"}, {\"val\": \"value_2\"}]," - " \"baz\": true\n" - " },\n" - " \"children\": [{\n" - " \"foo\": 2,\n" - " \"bar\": \"foobar\",\n" - " \"bstruct\": \"\",\n" - " \"string_values\": [{\"val\": \"value_1\"}]," - " \"baz\": true\n" - " },\n" - " {\n" - " \"foo\": 3,\n" - " \"bar\": \"barbaz\",\n" - " \"baz\": false\n" - " }]\n" - "}\n"; - - std::unique_ptr value = base::JSONReader::Read(normal_data); - NestedMessage message; - base::JSONValueConverter converter; - EXPECT_TRUE(converter.Convert(*value.get(), &message)); - - EXPECT_EQ(1.0, message.foo); - EXPECT_EQ(1, message.child.foo); - EXPECT_EQ("bar", message.child.bar); - EXPECT_TRUE(message.child.baz); - EXPECT_TRUE(message.child.bstruct); - ASSERT_EQ(2U, message.child.string_values.size()); - EXPECT_EQ("value_1", *message.child.string_values[0]); - EXPECT_EQ("value_2", *message.child.string_values[1]); - - EXPECT_EQ(2, static_cast(message.children.size())); - const SimpleMessage* first_child = message.children[0].get(); - ASSERT_TRUE(first_child); - EXPECT_EQ(2, first_child->foo); - EXPECT_EQ("foobar", first_child->bar); - EXPECT_TRUE(first_child->baz); - EXPECT_TRUE(first_child->bstruct); - ASSERT_EQ(1U, first_child->string_values.size()); - EXPECT_EQ("value_1", *first_child->string_values[0]); - - const SimpleMessage* second_child = message.children[1].get(); - ASSERT_TRUE(second_child); - EXPECT_EQ(3, second_child->foo); - EXPECT_EQ("barbaz", second_child->bar); - EXPECT_FALSE(second_child->baz); - EXPECT_FALSE(second_child->bstruct); - EXPECT_EQ(0U, second_child->string_values.size()); -} - -TEST(JSONValueConverterTest, ParseFailures) { - const char normal_data[] = - "{\n" - " \"foo\": 1,\n" - " \"bar\": 2,\n" // "bar" is an integer here. - " \"baz\": true,\n" - " \"ints\": [1, 2]" - "}\n"; - - std::unique_ptr value = base::JSONReader::Read(normal_data); - SimpleMessage message; - base::JSONValueConverter converter; - EXPECT_FALSE(converter.Convert(*value.get(), &message)); - // Do not check the values below. |message| may be modified during - // Convert() even it fails. -} - -TEST(JSONValueConverterTest, ParseWithMissingFields) { - const char normal_data[] = - "{\n" - " \"foo\": 1,\n" - " \"baz\": true,\n" - " \"ints\": [1, 2]" - "}\n"; - - std::unique_ptr value = base::JSONReader::Read(normal_data); - SimpleMessage message; - base::JSONValueConverter converter; - // Convert() still succeeds even if the input doesn't have "bar" field. - EXPECT_TRUE(converter.Convert(*value.get(), &message)); - - EXPECT_EQ(1, message.foo); - EXPECT_TRUE(message.baz); - EXPECT_EQ(2, static_cast(message.ints.size())); - EXPECT_EQ(1, *(message.ints[0])); - EXPECT_EQ(2, *(message.ints[1])); -} - -TEST(JSONValueConverterTest, EnumParserFails) { - const char normal_data[] = - "{\n" - " \"foo\": 1,\n" - " \"bar\": \"bar\",\n" - " \"baz\": true,\n" - " \"simple_enum\": \"baz\"," - " \"ints\": [1, 2]" - "}\n"; - - std::unique_ptr value = base::JSONReader::Read(normal_data); - SimpleMessage message; - base::JSONValueConverter converter; - EXPECT_FALSE(converter.Convert(*value.get(), &message)); - // No check the values as mentioned above. -} - -TEST(JSONValueConverterTest, RepeatedValueErrorInTheMiddle) { - const char normal_data[] = - "{\n" - " \"foo\": 1,\n" - " \"bar\": \"bar\",\n" - " \"baz\": true,\n" - " \"simple_enum\": \"baz\"," - " \"ints\": [1, false]" - "}\n"; - - std::unique_ptr value = base::JSONReader::Read(normal_data); - SimpleMessage message; - base::JSONValueConverter converter; - EXPECT_FALSE(converter.Convert(*value.get(), &message)); - // No check the values as mentioned above. -} - -} // namespace base diff --git a/json/json_value_serializer_unittest.cc b/json/json_value_serializer_unittest.cc deleted file mode 100644 index d25f95047..000000000 --- a/json/json_value_serializer_unittest.cc +++ /dev/null @@ -1,487 +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 -#include - -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/json/json_file_value_serializer.h" -#include "base/json/json_reader.h" -#include "base/json/json_string_value_serializer.h" -#include "base/json/json_writer.h" -#include "base/path_service.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -// Some proper JSON to test with: -const char kProperJSON[] = - "{\n" - " \"compound\": {\n" - " \"a\": 1,\n" - " \"b\": 2\n" - " },\n" - " \"some_String\": \"1337\",\n" - " \"some_int\": 42,\n" - " \"the_list\": [ \"val1\", \"val2\" ]\n" - "}\n"; - -// Some proper JSON with trailing commas: -const char kProperJSONWithCommas[] = - "{\n" - "\t\"some_int\": 42,\n" - "\t\"some_String\": \"1337\",\n" - "\t\"the_list\": [\"val1\", \"val2\", ],\n" - "\t\"compound\": { \"a\": 1, \"b\": 2, },\n" - "}\n"; - -// kProperJSON with a few misc characters at the begin and end. -const char kProperJSONPadded[] = - ")]}'\n" - "{\n" - " \"compound\": {\n" - " \"a\": 1,\n" - " \"b\": 2\n" - " },\n" - " \"some_String\": \"1337\",\n" - " \"some_int\": 42,\n" - " \"the_list\": [ \"val1\", \"val2\" ]\n" - "}\n" - "?!ab\n"; - -const char kWinLineEnds[] = "\r\n"; -const char kLinuxLineEnds[] = "\n"; - -// Verifies the generated JSON against the expected output. -void CheckJSONIsStillTheSame(const Value& value) { - // Serialize back the output. - std::string serialized_json; - JSONStringValueSerializer str_serializer(&serialized_json); - str_serializer.set_pretty_print(true); - ASSERT_TRUE(str_serializer.Serialize(value)); - // Unify line endings between platforms. - ReplaceSubstringsAfterOffset(&serialized_json, 0, - kWinLineEnds, kLinuxLineEnds); - // Now compare the input with the output. - ASSERT_EQ(kProperJSON, serialized_json); -} - -void ValidateJsonList(const std::string& json) { - std::unique_ptr list = ListValue::From(JSONReader::Read(json)); - ASSERT_TRUE(list); - ASSERT_EQ(1U, list->GetSize()); - Value* elt = nullptr; - ASSERT_TRUE(list->Get(0, &elt)); - int value = 0; - ASSERT_TRUE(elt && elt->GetAsInteger(&value)); - ASSERT_EQ(1, value); -} - -// Test proper JSON deserialization from string is working. -TEST(JSONValueDeserializerTest, ReadProperJSONFromString) { - // Try to deserialize it through the serializer. - JSONStringValueDeserializer str_deserializer(kProperJSON); - - int error_code = 0; - std::string error_message; - std::unique_ptr value = - str_deserializer.Deserialize(&error_code, &error_message); - ASSERT_TRUE(value); - ASSERT_EQ(0, error_code); - ASSERT_TRUE(error_message.empty()); - // Verify if the same JSON is still there. - CheckJSONIsStillTheSame(*value); -} - -// Test proper JSON deserialization from a StringPiece substring. -TEST(JSONValueDeserializerTest, ReadProperJSONFromStringPiece) { - // Create a StringPiece for the substring of kProperJSONPadded that matches - // kProperJSON. - StringPiece proper_json(kProperJSONPadded); - proper_json = proper_json.substr(5, proper_json.length() - 10); - JSONStringValueDeserializer str_deserializer(proper_json); - - int error_code = 0; - std::string error_message; - std::unique_ptr value = - str_deserializer.Deserialize(&error_code, &error_message); - ASSERT_TRUE(value); - ASSERT_EQ(0, error_code); - ASSERT_TRUE(error_message.empty()); - // Verify if the same JSON is still there. - CheckJSONIsStillTheSame(*value); -} - -// Test that trialing commas are only properly deserialized from string when -// the proper flag for that is set. -TEST(JSONValueDeserializerTest, ReadJSONWithTrailingCommasFromString) { - // Try to deserialize it through the serializer. - JSONStringValueDeserializer str_deserializer(kProperJSONWithCommas); - - int error_code = 0; - std::string error_message; - std::unique_ptr value = - str_deserializer.Deserialize(&error_code, &error_message); - ASSERT_FALSE(value); - ASSERT_NE(0, error_code); - ASSERT_FALSE(error_message.empty()); - // Repeat with commas allowed. - JSONStringValueDeserializer str_deserializer2(kProperJSONWithCommas, - JSON_ALLOW_TRAILING_COMMAS); - value = str_deserializer2.Deserialize(&error_code, &error_message); - ASSERT_TRUE(value); - ASSERT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code); - // Verify if the same JSON is still there. - CheckJSONIsStillTheSame(*value); -} - -// Test proper JSON deserialization from file is working. -TEST(JSONValueDeserializerTest, ReadProperJSONFromFile) { - ScopedTempDir tempdir; - ASSERT_TRUE(tempdir.CreateUniqueTempDir()); - // Write it down in the file. - FilePath temp_file(tempdir.GetPath().AppendASCII("test.json")); - ASSERT_EQ(static_cast(strlen(kProperJSON)), - WriteFile(temp_file, kProperJSON, strlen(kProperJSON))); - - // Try to deserialize it through the serializer. - JSONFileValueDeserializer file_deserializer(temp_file); - - int error_code = 0; - std::string error_message; - std::unique_ptr value = - file_deserializer.Deserialize(&error_code, &error_message); - ASSERT_TRUE(value); - ASSERT_EQ(0, error_code); - ASSERT_TRUE(error_message.empty()); - // Verify if the same JSON is still there. - CheckJSONIsStillTheSame(*value); -} - -// Test that trialing commas are only properly deserialized from file when -// the proper flag for that is set. -TEST(JSONValueDeserializerTest, ReadJSONWithCommasFromFile) { - ScopedTempDir tempdir; - ASSERT_TRUE(tempdir.CreateUniqueTempDir()); - // Write it down in the file. - FilePath temp_file(tempdir.GetPath().AppendASCII("test.json")); - ASSERT_EQ(static_cast(strlen(kProperJSONWithCommas)), - WriteFile(temp_file, kProperJSONWithCommas, - strlen(kProperJSONWithCommas))); - - // Try to deserialize it through the serializer. - JSONFileValueDeserializer file_deserializer(temp_file); - // This must fail without the proper flag. - int error_code = 0; - std::string error_message; - std::unique_ptr value = - file_deserializer.Deserialize(&error_code, &error_message); - ASSERT_FALSE(value); - ASSERT_NE(0, error_code); - ASSERT_FALSE(error_message.empty()); - // Repeat with commas allowed. - JSONFileValueDeserializer file_deserializer2(temp_file, - JSON_ALLOW_TRAILING_COMMAS); - value = file_deserializer2.Deserialize(&error_code, &error_message); - ASSERT_TRUE(value); - ASSERT_EQ(JSONReader::JSON_TRAILING_COMMA, error_code); - // Verify if the same JSON is still there. - CheckJSONIsStillTheSame(*value); -} - -TEST(JSONValueDeserializerTest, AllowTrailingComma) { - static const char kTestWithCommas[] = "{\"key\": [true,],}"; - static const char kTestNoCommas[] = "{\"key\": [true]}"; - - JSONStringValueDeserializer deserializer(kTestWithCommas, - JSON_ALLOW_TRAILING_COMMAS); - JSONStringValueDeserializer deserializer_expected(kTestNoCommas); - std::unique_ptr root = deserializer.Deserialize(nullptr, nullptr); - ASSERT_TRUE(root); - std::unique_ptr root_expected; - root_expected = deserializer_expected.Deserialize(nullptr, nullptr); - ASSERT_TRUE(root_expected); - ASSERT_TRUE(root->Equals(root_expected.get())); -} - -TEST(JSONValueSerializerTest, Roundtrip) { - static const char kOriginalSerialization[] = - "{\"bool\":true,\"double\":3.14,\"int\":42,\"list\":[1,2],\"null\":null}"; - JSONStringValueDeserializer deserializer(kOriginalSerialization); - std::unique_ptr root_dict = - DictionaryValue::From(deserializer.Deserialize(nullptr, nullptr)); - ASSERT_TRUE(root_dict); - - Value* null_value = nullptr; - ASSERT_TRUE(root_dict->Get("null", &null_value)); - ASSERT_TRUE(null_value); - ASSERT_TRUE(null_value->is_none()); - - bool bool_value = false; - ASSERT_TRUE(root_dict->GetBoolean("bool", &bool_value)); - ASSERT_TRUE(bool_value); - - int int_value = 0; - ASSERT_TRUE(root_dict->GetInteger("int", &int_value)); - ASSERT_EQ(42, int_value); - - double double_value = 0.0; - ASSERT_TRUE(root_dict->GetDouble("double", &double_value)); - ASSERT_DOUBLE_EQ(3.14, double_value); - - std::string test_serialization; - JSONStringValueSerializer mutable_serializer(&test_serialization); - ASSERT_TRUE(mutable_serializer.Serialize(*root_dict)); - ASSERT_EQ(kOriginalSerialization, test_serialization); - - mutable_serializer.set_pretty_print(true); - ASSERT_TRUE(mutable_serializer.Serialize(*root_dict)); - // JSON output uses a different newline style on Windows than on other - // platforms. -#if defined(OS_WIN) -#define JSON_NEWLINE "\r\n" -#else -#define JSON_NEWLINE "\n" -#endif - const std::string pretty_serialization = - "{" JSON_NEWLINE - " \"bool\": true," JSON_NEWLINE - " \"double\": 3.14," JSON_NEWLINE - " \"int\": 42," JSON_NEWLINE - " \"list\": [ 1, 2 ]," JSON_NEWLINE - " \"null\": null" JSON_NEWLINE - "}" JSON_NEWLINE; -#undef JSON_NEWLINE - ASSERT_EQ(pretty_serialization, test_serialization); -} - -TEST(JSONValueSerializerTest, StringEscape) { - string16 all_chars; - for (int i = 1; i < 256; ++i) { - all_chars += static_cast(i); - } - // Generated in in Firefox using the following js (with an extra backslash for - // double quote): - // var s = ''; - // for (var i = 1; i < 256; ++i) { s += String.fromCharCode(i); } - // uneval(s).replace(/\\/g, "\\\\"); - std::string all_chars_expected = - "\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000B\\f\\r" - "\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" - "\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F !\\\"#$%&'()*+," - "-./0123456789:;\\u003C=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcde" - "fghijklmnopqrstuvwxyz{|}~\x7F\xC2\x80\xC2\x81\xC2\x82\xC2\x83\xC2\x84" - "\xC2\x85\xC2\x86\xC2\x87\xC2\x88\xC2\x89\xC2\x8A\xC2\x8B\xC2\x8C\xC2\x8D" - "\xC2\x8E\xC2\x8F\xC2\x90\xC2\x91\xC2\x92\xC2\x93\xC2\x94\xC2\x95\xC2\x96" - "\xC2\x97\xC2\x98\xC2\x99\xC2\x9A\xC2\x9B\xC2\x9C\xC2\x9D\xC2\x9E\xC2\x9F" - "\xC2\xA0\xC2\xA1\xC2\xA2\xC2\xA3\xC2\xA4\xC2\xA5\xC2\xA6\xC2\xA7\xC2\xA8" - "\xC2\xA9\xC2\xAA\xC2\xAB\xC2\xAC\xC2\xAD\xC2\xAE\xC2\xAF\xC2\xB0\xC2\xB1" - "\xC2\xB2\xC2\xB3\xC2\xB4\xC2\xB5\xC2\xB6\xC2\xB7\xC2\xB8\xC2\xB9\xC2\xBA" - "\xC2\xBB\xC2\xBC\xC2\xBD\xC2\xBE\xC2\xBF\xC3\x80\xC3\x81\xC3\x82\xC3\x83" - "\xC3\x84\xC3\x85\xC3\x86\xC3\x87\xC3\x88\xC3\x89\xC3\x8A\xC3\x8B\xC3\x8C" - "\xC3\x8D\xC3\x8E\xC3\x8F\xC3\x90\xC3\x91\xC3\x92\xC3\x93\xC3\x94\xC3\x95" - "\xC3\x96\xC3\x97\xC3\x98\xC3\x99\xC3\x9A\xC3\x9B\xC3\x9C\xC3\x9D\xC3\x9E" - "\xC3\x9F\xC3\xA0\xC3\xA1\xC3\xA2\xC3\xA3\xC3\xA4\xC3\xA5\xC3\xA6\xC3\xA7" - "\xC3\xA8\xC3\xA9\xC3\xAA\xC3\xAB\xC3\xAC\xC3\xAD\xC3\xAE\xC3\xAF\xC3\xB0" - "\xC3\xB1\xC3\xB2\xC3\xB3\xC3\xB4\xC3\xB5\xC3\xB6\xC3\xB7\xC3\xB8\xC3\xB9" - "\xC3\xBA\xC3\xBB\xC3\xBC\xC3\xBD\xC3\xBE\xC3\xBF"; - - std::string expected_output = "{\"all_chars\":\"" + all_chars_expected + - "\"}"; - // Test JSONWriter interface - std::string output_js; - DictionaryValue valueRoot; - valueRoot.SetString("all_chars", all_chars); - JSONWriter::Write(valueRoot, &output_js); - ASSERT_EQ(expected_output, output_js); - - // Test JSONValueSerializer interface (uses JSONWriter). - JSONStringValueSerializer serializer(&output_js); - ASSERT_TRUE(serializer.Serialize(valueRoot)); - ASSERT_EQ(expected_output, output_js); -} - -TEST(JSONValueSerializerTest, UnicodeStrings) { - // unicode string json -> escaped ascii text - DictionaryValue root; - string16 test(WideToUTF16(L"\x7F51\x9875")); - root.SetString("web", test); - - static const char kExpected[] = "{\"web\":\"\xE7\xBD\x91\xE9\xA1\xB5\"}"; - - std::string actual; - JSONStringValueSerializer serializer(&actual); - ASSERT_TRUE(serializer.Serialize(root)); - ASSERT_EQ(kExpected, actual); - - // escaped ascii text -> json - JSONStringValueDeserializer deserializer(kExpected); - std::unique_ptr deserial_root = - deserializer.Deserialize(nullptr, nullptr); - ASSERT_TRUE(deserial_root); - DictionaryValue* dict_root = - static_cast(deserial_root.get()); - string16 web_value; - ASSERT_TRUE(dict_root->GetString("web", &web_value)); - ASSERT_EQ(test, web_value); -} - -TEST(JSONValueSerializerTest, HexStrings) { - // hex string json -> escaped ascii text - DictionaryValue root; - string16 test(WideToUTF16(L"\x01\x02")); - root.SetString("test", test); - - static const char kExpected[] = "{\"test\":\"\\u0001\\u0002\"}"; - - std::string actual; - JSONStringValueSerializer serializer(&actual); - ASSERT_TRUE(serializer.Serialize(root)); - ASSERT_EQ(kExpected, actual); - - // escaped ascii text -> json - JSONStringValueDeserializer deserializer(kExpected); - std::unique_ptr deserial_root = - deserializer.Deserialize(nullptr, nullptr); - ASSERT_TRUE(deserial_root); - DictionaryValue* dict_root = - static_cast(deserial_root.get()); - string16 test_value; - ASSERT_TRUE(dict_root->GetString("test", &test_value)); - ASSERT_EQ(test, test_value); - - // Test converting escaped regular chars - static const char kEscapedChars[] = "{\"test\":\"\\u0067\\u006f\"}"; - JSONStringValueDeserializer deserializer2(kEscapedChars); - deserial_root = deserializer2.Deserialize(nullptr, nullptr); - ASSERT_TRUE(deserial_root); - dict_root = static_cast(deserial_root.get()); - ASSERT_TRUE(dict_root->GetString("test", &test_value)); - ASSERT_EQ(ASCIIToUTF16("go"), test_value); -} - -TEST(JSONValueSerializerTest, JSONReaderComments) { - ValidateJsonList("[ // 2, 3, ignore me ] \n1 ]"); - ValidateJsonList("[ /* 2, \n3, ignore me ]*/ \n1 ]"); - ValidateJsonList("//header\n[ // 2, \n// 3, \n1 ]// footer"); - ValidateJsonList("/*\n[ // 2, \n// 3, \n1 ]*/[1]"); - ValidateJsonList("[ 1 /* one */ ] /* end */"); - ValidateJsonList("[ 1 //// ,2\r\n ]"); - - // It's ok to have a comment in a string. - std::unique_ptr list = - ListValue::From(JSONReader::Read("[\"// ok\\n /* foo */ \"]")); - ASSERT_TRUE(list); - ASSERT_EQ(1U, list->GetSize()); - Value* elt = nullptr; - ASSERT_TRUE(list->Get(0, &elt)); - std::string value; - ASSERT_TRUE(elt && elt->GetAsString(&value)); - ASSERT_EQ("// ok\n /* foo */ ", value); - - // You can't nest comments. - ASSERT_FALSE(JSONReader::Read("/* /* inner */ outer */ [ 1 ]")); - - // Not a open comment token. - ASSERT_FALSE(JSONReader::Read("/ * * / [1]")); -} - -class JSONFileValueSerializerTest : public testing::Test { - protected: - void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } - - ScopedTempDir temp_dir_; -}; - -TEST_F(JSONFileValueSerializerTest, Roundtrip) { - FilePath original_file_path; - ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &original_file_path)); - original_file_path = original_file_path.AppendASCII("serializer_test.json"); - - ASSERT_TRUE(PathExists(original_file_path)); - - JSONFileValueDeserializer deserializer(original_file_path); - std::unique_ptr root_dict = - DictionaryValue::From(deserializer.Deserialize(nullptr, nullptr)); - ASSERT_TRUE(root_dict); - - Value* null_value = nullptr; - ASSERT_TRUE(root_dict->Get("null", &null_value)); - ASSERT_TRUE(null_value); - ASSERT_TRUE(null_value->is_none()); - - bool bool_value = false; - ASSERT_TRUE(root_dict->GetBoolean("bool", &bool_value)); - ASSERT_TRUE(bool_value); - - int int_value = 0; - ASSERT_TRUE(root_dict->GetInteger("int", &int_value)); - ASSERT_EQ(42, int_value); - - std::string string_value; - ASSERT_TRUE(root_dict->GetString("string", &string_value)); - ASSERT_EQ("hello", string_value); - - // Now try writing. - const FilePath written_file_path = - temp_dir_.GetPath().AppendASCII("test_output.js"); - - ASSERT_FALSE(PathExists(written_file_path)); - JSONFileValueSerializer serializer(written_file_path); - ASSERT_TRUE(serializer.Serialize(*root_dict)); - ASSERT_TRUE(PathExists(written_file_path)); - - // Now compare file contents. - EXPECT_TRUE(TextContentsEqual(original_file_path, written_file_path)); - EXPECT_TRUE(DeleteFile(written_file_path, false)); -} - -TEST_F(JSONFileValueSerializerTest, RoundtripNested) { - FilePath original_file_path; - ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &original_file_path)); - original_file_path = - original_file_path.AppendASCII("serializer_nested_test.json"); - - ASSERT_TRUE(PathExists(original_file_path)); - - JSONFileValueDeserializer deserializer(original_file_path); - std::unique_ptr root = deserializer.Deserialize(nullptr, nullptr); - ASSERT_TRUE(root); - - // Now try writing. - FilePath written_file_path = - temp_dir_.GetPath().AppendASCII("test_output.json"); - - ASSERT_FALSE(PathExists(written_file_path)); - JSONFileValueSerializer serializer(written_file_path); - ASSERT_TRUE(serializer.Serialize(*root)); - ASSERT_TRUE(PathExists(written_file_path)); - - // Now compare file contents. - EXPECT_TRUE(TextContentsEqual(original_file_path, written_file_path)); - EXPECT_TRUE(DeleteFile(written_file_path, false)); -} - -TEST_F(JSONFileValueSerializerTest, NoWhitespace) { - FilePath source_file_path; - ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &source_file_path)); - source_file_path = - source_file_path.AppendASCII("serializer_test_nowhitespace.json"); - ASSERT_TRUE(PathExists(source_file_path)); - JSONFileValueDeserializer deserializer(source_file_path); - std::unique_ptr root = deserializer.Deserialize(nullptr, nullptr); - ASSERT_TRUE(root); -} - -} // namespace - -} // namespace base diff --git a/json/json_writer.cc b/json/json_writer.cc deleted file mode 100644 index e4f1e3cf9..000000000 --- a/json/json_writer.cc +++ /dev/null @@ -1,209 +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/json/json_writer.h" - -#include - -#include -#include - -#include "base/json/string_escape.h" -#include "base/logging.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "base/values.h" -#include "build/build_config.h" - -namespace base { - -#if defined(OS_WIN) -const char kPrettyPrintLineEnding[] = "\r\n"; -#else -const char kPrettyPrintLineEnding[] = "\n"; -#endif - -// static -bool JSONWriter::Write(const Value& node, std::string* json) { - return WriteWithOptions(node, 0, json); -} - -// static -bool JSONWriter::WriteWithOptions(const Value& node, - int options, - std::string* json) { - json->clear(); - // Is there a better way to estimate the size of the output? - json->reserve(1024); - - JSONWriter writer(options, json); - bool result = writer.BuildJSONString(node, 0U); - - if (options & OPTIONS_PRETTY_PRINT) - json->append(kPrettyPrintLineEnding); - - return result; -} - -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); -} - -bool JSONWriter::BuildJSONString(const Value& node, size_t depth) { - switch (node.type()) { - case Value::Type::NONE: { - json_string_->append("null"); - 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 <= std::numeric_limits::max() && - value >= std::numeric_limits::min() && - std::floor(value) == value) { - json_string_->append(Int64ToString(static_cast(value))); - return result; - } - std::string real = NumberToString(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(static_cast(0), static_cast(1), '0'); - } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { - // "-.1" bad "-0.1" good - real.insert(static_cast(1), static_cast(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 = nullptr; - bool first_value_has_been_output = false; - bool result = node.GetAsList(&list); - DCHECK(result); - for (const auto& value : *list) { - if (omit_binary_values_ && value.type() == Value::Type::BINARY) - continue; - - if (first_value_has_been_output) { - json_string_->push_back(','); - if (pretty_print_) - json_string_->push_back(' '); - } - - if (!BuildJSONString(value, depth)) - result = false; - - first_value_has_been_output = true; - } - - 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 = nullptr; - 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().type() == Value::Type::BINARY) { - continue; - } - - if (first_value_has_been_output) { - json_string_->push_back(','); - if (pretty_print_) - json_string_->append(kPrettyPrintLineEnding); - } - - if (pretty_print_) - IndentLine(depth + 1U); - - EscapeJSONString(itr.key(), true, json_string_); - json_string_->push_back(':'); - if (pretty_print_) - json_string_->push_back(' '); - - if (!BuildJSONString(itr.value(), depth + 1U)) - result = false; - - first_value_has_been_output = true; - } - - if (pretty_print_) { - json_string_->append(kPrettyPrintLineEnding); - IndentLine(depth); - } - - 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(size_t depth) { - json_string_->append(depth * 3U, ' '); -} - -} // namespace base diff --git a/json/json_writer.h b/json/json_writer.h deleted file mode 100644 index 57cb8c16a..000000000 --- a/json/json_writer.h +++ /dev/null @@ -1,75 +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_JSON_JSON_WRITER_H_ -#define BASE_JSON_JSON_WRITER_H_ - -#include - -#include - -#include "base/base_export.h" -#include "base/macros.h" - -namespace base { - -class Value; - -class BASE_EXPORT JSONWriter { - public: - enum Options { - // 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 - // part as a normal integer (i.e., without using exponential notation - // or appending a '.0') as long as the value is within the range of a - // 64-bit int. - OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION = 1 << 1, - - // Return a slightly nicer formatted json string (pads with whitespace to - // help with readability). - OPTIONS_PRETTY_PRINT = 1 << 2, - }; - - // Given a root node, generates a JSON string and puts it into |json|. - // The output string is overwritten and not appended. - // - // 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)? Return true on success and false on failure. - static bool Write(const Value& node, std::string* json); - - // Same as above but with |options| which is a bunch of JSONWriter::Options - // bitwise ORed together. Return true on success and false on failure. - static bool WriteWithOptions(const Value& node, - int options, - std::string* json); - - private: - JSONWriter(int options, std::string* json); - - // Called recursively to build the JSON string. When completed, - // |json_string_| will contain the JSON. - bool BuildJSONString(const Value& node, size_t depth); - - // Adds space to json_string_ for the indent level. - void IndentLine(size_t depth); - - bool omit_binary_values_; - bool omit_double_type_preservation_; - bool pretty_print_; - - // Where we write JSON data as we generate it. - std::string* json_string_; - - DISALLOW_COPY_AND_ASSIGN(JSONWriter); -}; - -} // namespace base - -#endif // BASE_JSON_JSON_WRITER_H_ diff --git a/json/json_writer_unittest.cc b/json/json_writer_unittest.cc deleted file mode 100644 index 2d81af39c..000000000 --- a/json/json_writer_unittest.cc +++ /dev/null @@ -1,154 +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/json/json_writer.h" - -#include "base/memory/ptr_util.h" -#include "base/values.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(JSONWriterTest, BasicTypes) { - std::string output_js; - - // Test null. - EXPECT_TRUE(JSONWriter::Write(Value(), &output_js)); - EXPECT_EQ("null", output_js); - - // Test empty dict. - EXPECT_TRUE(JSONWriter::Write(DictionaryValue(), &output_js)); - EXPECT_EQ("{}", output_js); - - // Test empty list. - EXPECT_TRUE(JSONWriter::Write(ListValue(), &output_js)); - EXPECT_EQ("[]", output_js); - - // Test integer values. - EXPECT_TRUE(JSONWriter::Write(Value(42), &output_js)); - EXPECT_EQ("42", output_js); - - // Test boolean values. - EXPECT_TRUE(JSONWriter::Write(Value(true), &output_js)); - EXPECT_EQ("true", output_js); - - // Test Real values should always have a decimal or an 'e'. - EXPECT_TRUE(JSONWriter::Write(Value(1.0), &output_js)); - EXPECT_EQ("1.0", output_js); - - // Test Real values in the the range (-1, 1) must have leading zeros - EXPECT_TRUE(JSONWriter::Write(Value(0.2), &output_js)); - EXPECT_EQ("0.2", output_js); - - // Test Real values in the the range (-1, 1) must have leading zeros - EXPECT_TRUE(JSONWriter::Write(Value(-0.8), &output_js)); - EXPECT_EQ("-0.8", output_js); - - // Test String values. - EXPECT_TRUE(JSONWriter::Write(Value("foo"), &output_js)); - EXPECT_EQ("\"foo\"", output_js); -} - -TEST(JSONWriterTest, NestedTypes) { - std::string output_js; - - // Writer unittests like empty list/dict nesting, - // list list nesting, etc. - DictionaryValue root_dict; - std::unique_ptr list(new ListValue()); - std::unique_ptr inner_dict(new DictionaryValue()); - inner_dict->SetInteger("inner int", 10); - list->Append(std::move(inner_dict)); - list->Append(std::make_unique()); - list->AppendBoolean(true); - root_dict.Set("list", std::move(list)); - - // Test the pretty-printer. - 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) -#define JSON_NEWLINE "\r\n" -#else -#define JSON_NEWLINE "\n" -#endif - 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; - - DictionaryValue period_dict; - period_dict.SetKey("a.b", base::Value(3)); - period_dict.SetKey("c", base::Value(2)); - std::unique_ptr period_dict2(new DictionaryValue()); - period_dict2->SetKey("g.h.i.j", base::Value(1)); - period_dict.SetWithoutPathExpansion("d.e.f", std::move(period_dict2)); - 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.SetInteger("a.b", 2); - period_dict3.SetKey("a.b", base::Value(1)); - 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. - std::unique_ptr root(Value::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()); - - ListValue binary_list; - binary_list.Append(Value::CreateWithCopiedBuffer("asdf", 4)); - binary_list.Append(std::make_unique(5)); - binary_list.Append(Value::CreateWithCopiedBuffer("asdf", 4)); - binary_list.Append(std::make_unique(2)); - binary_list.Append(Value::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", Value::CreateWithCopiedBuffer("asdf", 4)); - binary_dict.SetInteger("b", 5); - binary_dict.Set("c", Value::CreateWithCopiedBuffer("asdf", 4)); - binary_dict.SetInteger("d", 2); - binary_dict.Set("e", Value::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. - Value double_value(1e10); - EXPECT_TRUE(JSONWriter::WriteWithOptions( - double_value, JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION, - &output_js)); - EXPECT_EQ("10000000000", output_js); -} - -} // namespace base diff --git a/json/string_escape.cc b/json/string_escape.cc deleted file mode 100644 index 471a9d30c..000000000 --- a/json/string_escape.cc +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/json/string_escape.h" - -#include -#include - -#include -#include - -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversion_utils.h" -#include "base/strings/utf_string_conversions.h" -#include "base/third_party/icu/icu_utf.h" - -namespace base { - -namespace { - -// Format string for printing a \uXXXX escape sequence. -const char kU16EscapeFormat[] = "\\u%04X"; - -// The code point to output for an invalid input code unit. -const uint32_t kReplacementCodePoint = 0xFFFD; - -// Used below in EscapeSpecialCodePoint(). -static_assert('<' == 0x3C, "less than sign must be 0x3c"); - -// Try to escape the |code_point| if it is a known special character. If -// successful, returns true and appends the escape sequence to |dest|. This -// isn't required by the spec, but it's more readable by humans. -bool EscapeSpecialCodePoint(uint32_t code_point, std::string* dest) { - // WARNING: if you add a new case here, you need to update the reader as well. - // Note: \v is in the reader, but not here since the JSON spec doesn't - // allow it. - switch (code_point) { - case '\b': - dest->append("\\b"); - break; - case '\f': - dest->append("\\f"); - break; - case '\n': - dest->append("\\n"); - break; - case '\r': - dest->append("\\r"); - break; - case '\t': - dest->append("\\t"); - break; - case '\\': - dest->append("\\\\"); - break; - case '"': - dest->append("\\\""); - break; - // Escape < to prevent script execution; escaping > is not necessary and - // not doing so save a few bytes. - case '<': - dest->append("\\u003C"); - break; - // Escape the "Line Separator" and "Paragraph Separator" characters, since - // they should be treated like a new line \r or \n. - case 0x2028: - dest->append("\\u2028"); - break; - case 0x2029: - dest->append("\\u2029"); - break; - default: - return false; - } - return true; -} - -template -bool EscapeJSONStringImpl(const S& str, bool put_in_quotes, std::string* dest) { - bool did_replacement = false; - - if (put_in_quotes) - dest->push_back('"'); - - // Casting is necessary because ICU uses int32_t. Try and do so safely. - CHECK_LE(str.length(), - static_cast(std::numeric_limits::max())); - const int32_t length = static_cast(str.length()); - - for (int32_t i = 0; i < length; ++i) { - uint32_t code_point; - if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point) || - code_point == static_cast(CBU_SENTINEL) || - !IsValidCharacter(code_point)) { - code_point = kReplacementCodePoint; - did_replacement = true; - } - - if (EscapeSpecialCodePoint(code_point, dest)) - continue; - - // Escape non-printing characters. - if (code_point < 32) - base::StringAppendF(dest, kU16EscapeFormat, code_point); - else - WriteUnicodeCharacter(code_point, dest); - } - - if (put_in_quotes) - dest->push_back('"'); - - return !did_replacement; -} - -} // namespace - -bool EscapeJSONString(StringPiece str, bool put_in_quotes, std::string* dest) { - return EscapeJSONStringImpl(str, put_in_quotes, dest); -} - -bool EscapeJSONString(StringPiece16 str, - bool put_in_quotes, - std::string* dest) { - return EscapeJSONStringImpl(str, put_in_quotes, dest); -} - -std::string GetQuotedJSONString(StringPiece str) { - std::string dest; - bool ok = EscapeJSONStringImpl(str, true, &dest); - DCHECK(ok); - return dest; -} - -std::string GetQuotedJSONString(StringPiece16 str) { - std::string dest; - bool ok = EscapeJSONStringImpl(str, true, &dest); - DCHECK(ok); - return dest; -} - -std::string EscapeBytesAsInvalidJSONString(StringPiece str, - bool put_in_quotes) { - std::string dest; - - if (put_in_quotes) - dest.push_back('"'); - - for (StringPiece::const_iterator it = str.begin(); it != str.end(); ++it) { - unsigned char c = *it; - if (EscapeSpecialCodePoint(c, &dest)) - continue; - - if (c < 32 || c > 126) - base::StringAppendF(&dest, kU16EscapeFormat, c); - else - dest.push_back(*it); - } - - if (put_in_quotes) - dest.push_back('"'); - - return dest; -} - -} // namespace base diff --git a/json/string_escape.h b/json/string_escape.h deleted file mode 100644 index f75f475af..000000000 --- a/json/string_escape.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file defines utility functions for escaping strings suitable for JSON. - -#ifndef BASE_JSON_STRING_ESCAPE_H_ -#define BASE_JSON_STRING_ESCAPE_H_ - -#include - -#include "base/base_export.h" -#include "base/strings/string_piece.h" - -namespace base { - -// Appends to |dest| an escaped version of |str|. Valid UTF-8 code units and -// characters will pass through from the input to the output. Invalid code -// units and characters will be replaced with the U+FFFD replacement character. -// This function returns true if no replacement was necessary and false if -// there was a lossy replacement. On return, |dest| will contain a valid UTF-8 -// JSON string. -// -// Non-printing control characters will be escaped as \uXXXX sequences for -// readability. -// -// If |put_in_quotes| is true, then a leading and trailing double-quote mark -// will be appended to |dest| as well. -BASE_EXPORT bool EscapeJSONString(StringPiece str, - bool put_in_quotes, - std::string* dest); - -// Performs a similar function to the UTF-8 StringPiece version above, -// converting UTF-16 code units to UTF-8 code units and escaping non-printing -// control characters. On return, |dest| will contain a valid UTF-8 JSON string. -BASE_EXPORT bool EscapeJSONString(StringPiece16 str, - bool put_in_quotes, - std::string* dest); - -// Helper functions that wrap the above two functions but return the value -// instead of appending. |put_in_quotes| is always true. -BASE_EXPORT std::string GetQuotedJSONString(StringPiece str); -BASE_EXPORT std::string GetQuotedJSONString(StringPiece16 str); - -// Given an arbitrary byte string |str|, this will escape all non-ASCII bytes -// as \uXXXX escape sequences. This function is *NOT* meant to be used with -// Unicode strings and does not validate |str| as one. -// -// CAVEAT CALLER: The output of this function may not be valid JSON, since -// JSON requires escape sequences to be valid UTF-16 code units. This output -// will be mangled if passed to to the base::JSONReader, since the reader will -// interpret it as UTF-16 and convert it to UTF-8. -// -// The output of this function takes the *appearance* of JSON but is not in -// fact valid according to RFC 4627. -BASE_EXPORT std::string EscapeBytesAsInvalidJSONString(StringPiece str, - bool put_in_quotes); - -} // namespace base - -#endif // BASE_JSON_STRING_ESCAPE_H_ diff --git a/json/string_escape_fuzzer.cc b/json/string_escape_fuzzer.cc deleted file mode 100644 index f4304118c..000000000 --- a/json/string_escape_fuzzer.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/json/string_escape.h" - -#include - -// Entry point for LibFuzzer. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - if (size < 2) - return 0; - - const bool put_in_quotes = data[size - 1]; - - // Create a copy of input buffer, as otherwise we don't catch - // overflow that touches the last byte (which is used in put_in_quotes). - size_t actual_size_char8 = size - 1; - std::unique_ptr input(new char[actual_size_char8]); - memcpy(input.get(), data, actual_size_char8); - - base::StringPiece input_string(input.get(), actual_size_char8); - std::string escaped_string; - base::EscapeJSONString(input_string, put_in_quotes, &escaped_string); - - // Test for wide-strings if available size is even. - if (actual_size_char8 & 1) - return 0; - - size_t actual_size_char16 = actual_size_char8 / 2; - base::StringPiece16 input_string16( - reinterpret_cast(input.get()), actual_size_char16); - escaped_string.clear(); - base::EscapeJSONString(input_string16, put_in_quotes, &escaped_string); - - return 0; -} diff --git a/json/string_escape_unittest.cc b/json/string_escape_unittest.cc deleted file mode 100644 index 1e962c604..000000000 --- a/json/string_escape_unittest.cc +++ /dev/null @@ -1,189 +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/json/string_escape.h" - -#include - -#include "base/macros.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(JSONStringEscapeTest, EscapeUTF8) { - const struct { - const char* to_escape; - const char* escaped; - } cases[] = { - {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"}, - {"a\b\f\n\r\t\v\1\\.\"z", "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"}, - {"b\x0f\x7f\xf0\xff!", // \xf0\xff is not a valid UTF-8 unit. - "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"}, - {"c<>d", "c\\u003C>d"}, - {"Hello\xe2\x80\xa8world", "Hello\\u2028world"}, - {"\xe2\x80\xa9purple", "\\u2029purple"}, - {"\xF3\xBF\xBF\xBF", "\xEF\xBF\xBD"}, - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - const char* in_ptr = cases[i].to_escape; - std::string in_str = in_ptr; - - std::string out; - EscapeJSONString(in_ptr, false, &out); - EXPECT_EQ(std::string(cases[i].escaped), out); - EXPECT_TRUE(IsStringUTF8(out)); - - out.erase(); - bool convert_ok = EscapeJSONString(in_str, false, &out); - EXPECT_EQ(std::string(cases[i].escaped), out); - EXPECT_TRUE(IsStringUTF8(out)); - - if (convert_ok) { - std::string fooout = GetQuotedJSONString(in_str); - EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", fooout); - EXPECT_TRUE(IsStringUTF8(out)); - } - } - - std::string in = cases[0].to_escape; - std::string out; - EscapeJSONString(in, false, &out); - EXPECT_TRUE(IsStringUTF8(out)); - - // test quoting - std::string out_quoted; - EscapeJSONString(in, true, &out_quoted); - EXPECT_EQ(out.length() + 2, out_quoted.length()); - EXPECT_EQ(out_quoted.find(out), 1U); - EXPECT_TRUE(IsStringUTF8(out_quoted)); - - // now try with a NULL in the string - std::string null_prepend = "test"; - null_prepend.push_back(0); - in = null_prepend + in; - std::string expected = "test\\u0000"; - expected += cases[0].escaped; - out.clear(); - EscapeJSONString(in, false, &out); - EXPECT_EQ(expected, out); - EXPECT_TRUE(IsStringUTF8(out)); -} - -TEST(JSONStringEscapeTest, EscapeUTF16) { - const struct { - const wchar_t* to_escape; - const char* escaped; - } cases[] = { - {L"b\uffb1\u00ff", "b\xEF\xBE\xB1\xC3\xBF"}, - {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"}, - {L"a\b\f\n\r\t\v\1\\.\"z", - "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"}, - {L"b\x0f\x7f\xf0\xff!", "b\\u000F\x7F\xC3\xB0\xC3\xBF!"}, - {L"c<>d", "c\\u003C>d"}, - {L"Hello\u2028world", "Hello\\u2028world"}, - {L"\u2029purple", "\\u2029purple"}, - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - string16 in = WideToUTF16(cases[i].to_escape); - - std::string out; - EscapeJSONString(in, false, &out); - EXPECT_EQ(std::string(cases[i].escaped), out); - EXPECT_TRUE(IsStringUTF8(out)); - - out = GetQuotedJSONString(in); - EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", out); - EXPECT_TRUE(IsStringUTF8(out)); - } - - string16 in = WideToUTF16(cases[0].to_escape); - std::string out; - EscapeJSONString(in, false, &out); - EXPECT_TRUE(IsStringUTF8(out)); - - // test quoting - std::string out_quoted; - EscapeJSONString(in, true, &out_quoted); - EXPECT_EQ(out.length() + 2, out_quoted.length()); - EXPECT_EQ(out_quoted.find(out), 1U); - EXPECT_TRUE(IsStringUTF8(out)); - - // now try with a NULL in the string - string16 null_prepend = WideToUTF16(L"test"); - null_prepend.push_back(0); - in = null_prepend + in; - std::string expected = "test\\u0000"; - expected += cases[0].escaped; - out.clear(); - EscapeJSONString(in, false, &out); - EXPECT_EQ(expected, out); - EXPECT_TRUE(IsStringUTF8(out)); -} - -TEST(JSONStringEscapeTest, EscapeUTF16OutsideBMP) { - { - // {a, U+10300, !}, SMP. - string16 test; - test.push_back('a'); - test.push_back(0xD800); - test.push_back(0xDF00); - test.push_back('!'); - std::string actual; - EXPECT_TRUE(EscapeJSONString(test, false, &actual)); - EXPECT_EQ("a\xF0\x90\x8C\x80!", actual); - } - { - // {U+20021, U+2002B}, SIP. - string16 test; - test.push_back(0xD840); - test.push_back(0xDC21); - test.push_back(0xD840); - test.push_back(0xDC2B); - std::string actual; - EXPECT_TRUE(EscapeJSONString(test, false, &actual)); - EXPECT_EQ("\xF0\xA0\x80\xA1\xF0\xA0\x80\xAB", actual); - } - { - // {?, U+D800, @}, lone surrogate. - string16 test; - test.push_back('?'); - test.push_back(0xD800); - test.push_back('@'); - std::string actual; - EXPECT_FALSE(EscapeJSONString(test, false, &actual)); - EXPECT_EQ("?\xEF\xBF\xBD@", actual); - } -} - -TEST(JSONStringEscapeTest, EscapeBytes) { - const struct { - const char* to_escape; - const char* escaped; - } cases[] = { - {"b\x0f\x7f\xf0\xff!", "b\\u000F\\u007F\\u00F0\\u00FF!"}, - {"\xe5\xc4\x4f\x05\xb6\xfd", "\\u00E5\\u00C4O\\u0005\\u00B6\\u00FD"}, - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - std::string in = std::string(cases[i].to_escape); - EXPECT_FALSE(IsStringUTF8(in)); - - EXPECT_EQ(std::string(cases[i].escaped), - EscapeBytesAsInvalidJSONString(in, false)); - EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", - EscapeBytesAsInvalidJSONString(in, true)); - } - - const char kEmbedNull[] = { '\xab', '\x39', '\0', '\x9f', '\xab' }; - std::string in(kEmbedNull, arraysize(kEmbedNull)); - EXPECT_FALSE(IsStringUTF8(in)); - EXPECT_EQ(std::string("\\u00AB9\\u0000\\u009F\\u00AB"), - EscapeBytesAsInvalidJSONString(in, false)); -} - -} // namespace base diff --git a/lazy_instance_helpers.cc b/lazy_instance_helpers.cc deleted file mode 100644 index 7b9e0de7c..000000000 --- a/lazy_instance_helpers.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018 The Chromium 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/lazy_instance_helpers.h" - -#include "base/at_exit.h" -#include "base/atomicops.h" -#include "base/threading/platform_thread.h" - -namespace base { -namespace internal { - -bool NeedsLazyInstance(subtle::AtomicWord* state) { - // Try to create the instance, if we're the first, will go from 0 to - // kLazyInstanceStateCreating, otherwise we've already been beaten here. - // The memory access has no memory ordering as state 0 and - // kLazyInstanceStateCreating have no associated data (memory barriers are - // all about ordering of memory accesses to *associated* data). - if (subtle::NoBarrier_CompareAndSwap(state, 0, kLazyInstanceStateCreating) == - 0) { - // Caller must create instance - return true; - } - - // It's either in the process of being created, or already created. Spin. - // The load has acquire memory ordering as a thread which sees - // state_ == STATE_CREATED needs to acquire visibility over - // the associated data (buf_). Pairing Release_Store is in - // CompleteLazyInstance(). - if (subtle::Acquire_Load(state) == kLazyInstanceStateCreating) { - const base::TimeTicks start = base::TimeTicks::Now(); - do { - const base::TimeDelta elapsed = base::TimeTicks::Now() - start; - // Spin with YieldCurrentThread for at most one ms - this ensures maximum - // responsiveness. After that spin with Sleep(1ms) so that we don't burn - // excessive CPU time - this also avoids infinite loops due to priority - // inversions (https://crbug.com/797129). - if (elapsed < TimeDelta::FromMilliseconds(1)) - PlatformThread::YieldCurrentThread(); - else - PlatformThread::Sleep(TimeDelta::FromMilliseconds(1)); - } while (subtle::Acquire_Load(state) == kLazyInstanceStateCreating); - } - // Someone else created the instance. - return false; -} - -void CompleteLazyInstance(subtle::AtomicWord* state, - subtle::AtomicWord new_instance, - void (*destructor)(void*), - void* destructor_arg) { - // Instance is created, go from CREATING to CREATED (or reset it if - // |new_instance| is null). Releases visibility over |private_buf_| to - // readers. Pairing Acquire_Load is in NeedsLazyInstance(). - subtle::Release_Store(state, new_instance); - - // Make sure that the lazily instantiated object will get destroyed at exit. - if (new_instance && destructor) - AtExitManager::RegisterCallback(destructor, destructor_arg); -} - -} // namespace internal -} // namespace base diff --git a/lazy_instance_helpers.h b/lazy_instance_helpers.h deleted file mode 100644 index 5a43d8b1f..000000000 --- a/lazy_instance_helpers.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2018 The Chromium 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_LAZY_INSTANCE_INTERNAL_H_ -#define BASE_LAZY_INSTANCE_INTERNAL_H_ - -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/logging.h" - -// Helper methods used by LazyInstance and a few other base APIs for thread-safe -// lazy construction. - -namespace base { -namespace internal { - -// Our AtomicWord doubles as a spinlock, where a value of -// kLazyInstanceStateCreating means the spinlock is being held for creation. -constexpr subtle::AtomicWord kLazyInstanceStateCreating = 1; - -// Helper for GetOrCreateLazyPointer(). Checks if instance needs to be created. -// If so returns true otherwise if another thread has beat us, waits for -// instance to be created and returns false. -BASE_EXPORT bool NeedsLazyInstance(subtle::AtomicWord* state); - -// Helper for GetOrCreateLazyPointer(). After creating an instance, this is -// called to register the dtor to be called at program exit and to update the -// atomic state to hold the |new_instance| -BASE_EXPORT void CompleteLazyInstance(subtle::AtomicWord* state, - subtle::AtomicWord new_instance, - void (*destructor)(void*), - void* destructor_arg); - -} // namespace internal - -namespace subtle { - -// If |state| is uninitialized (zero), constructs a value using -// |creator_func(creator_arg)|, stores it into |state| and registers -// |destructor(destructor_arg)| to be called when the current AtExitManager goes -// out of scope. Then, returns the value stored in |state|. It is safe to have -// concurrent calls to this function with the same |state|. |creator_func| may -// return nullptr if it doesn't want to create an instance anymore (e.g. on -// shutdown), it is from then on required to return nullptr to all callers (ref. -// StaticMemorySingletonTraits). In that case, callers need to synchronize -// before |creator_func| may return a non-null instance again (ref. -// StaticMemorySingletonTraits::ResurectForTesting()). -// Implementation note on |creator_func/creator_arg|. It makes for ugly adapters -// but it avoids redundant template instantiations (e.g. saves 27KB in -// chrome.dll) because linker is able to fold these for multiple Types but -// couldn't with the more advanced CreatorFunc template type which in turn -// improves code locality (and application startup) -- ref. -// https://chromium-review.googlesource.com/c/chromium/src/+/530984/5/base/lazy_instance.h#140, -// worsened by https://chromium-review.googlesource.com/c/chromium/src/+/868013 -// and caught then as https://crbug.com/804034. -template -Type* GetOrCreateLazyPointer(subtle::AtomicWord* state, - Type* (*creator_func)(void*), - void* creator_arg, - void (*destructor)(void*), - void* destructor_arg) { - DCHECK(state); - DCHECK(creator_func); - - // If any bit in the created mask is true, the instance has already been - // fully constructed. - constexpr subtle::AtomicWord kLazyInstanceCreatedMask = - ~internal::kLazyInstanceStateCreating; - - // We will hopefully have fast access when the instance is already created. - // Since a thread sees |state| == 0 or kLazyInstanceStateCreating at most - // once, the load is taken out of NeedsLazyInstance() as a fast-path. The load - // has acquire memory ordering as a thread which sees |state| > creating needs - // to acquire visibility over the associated data. Pairing Release_Store is in - // CompleteLazyInstance(). - subtle::AtomicWord instance = subtle::Acquire_Load(state); - if (!(instance & kLazyInstanceCreatedMask)) { - if (internal::NeedsLazyInstance(state)) { - // This thread won the race and is now responsible for creating the - // instance and storing it back into |state|. - instance = - reinterpret_cast((*creator_func)(creator_arg)); - internal::CompleteLazyInstance(state, instance, destructor, - destructor_arg); - } else { - // This thread lost the race but now has visibility over the constructed - // instance (NeedsLazyInstance() doesn't return until the constructing - // thread releases the instance via CompleteLazyInstance()). - instance = subtle::Acquire_Load(state); - DCHECK(instance & kLazyInstanceCreatedMask); - } - } - return reinterpret_cast(instance); -} - -} // namespace subtle - -} // namespace base - -#endif // BASE_LAZY_INSTANCE_INTERNAL_H_ diff --git a/lazy_instance_unittest.cc b/lazy_instance_unittest.cc deleted file mode 100644 index a5f024cf5..000000000 --- a/lazy_instance_unittest.cc +++ /dev/null @@ -1,322 +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 - -#include -#include - -#include "base/at_exit.h" -#include "base/atomic_sequence_num.h" -#include "base/atomicops.h" -#include "base/barrier_closure.h" -#include "base/bind.h" -#include "base/lazy_instance.h" -#include "base/sys_info.h" -#include "base/threading/platform_thread.h" -#include "base/threading/simple_thread.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -base::AtomicSequenceNumber constructed_seq_; -base::AtomicSequenceNumber destructed_seq_; - -class ConstructAndDestructLogger { - public: - ConstructAndDestructLogger() { - constructed_seq_.GetNext(); - } - ~ConstructAndDestructLogger() { - destructed_seq_.GetNext(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(ConstructAndDestructLogger); -}; - -class SlowConstructor { - public: - SlowConstructor() : some_int_(0) { - // Sleep for 1 second to try to cause a race. - base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); - ++constructed; - some_int_ = 12; - } - int some_int() const { return some_int_; } - - static int constructed; - private: - int some_int_; - - DISALLOW_COPY_AND_ASSIGN(SlowConstructor); -}; - -// static -int SlowConstructor::constructed = 0; - -class SlowDelegate : public base::DelegateSimpleThread::Delegate { - public: - explicit SlowDelegate( - base::LazyInstance::DestructorAtExit* lazy) - : lazy_(lazy) {} - - void Run() override { - EXPECT_EQ(12, lazy_->Get().some_int()); - EXPECT_EQ(12, lazy_->Pointer()->some_int()); - } - - private: - base::LazyInstance::DestructorAtExit* lazy_; - - DISALLOW_COPY_AND_ASSIGN(SlowDelegate); -}; - -} // namespace - -base::LazyInstance::DestructorAtExit lazy_logger = - LAZY_INSTANCE_INITIALIZER; - -TEST(LazyInstanceTest, Basic) { - { - base::ShadowingAtExitManager shadow; - - EXPECT_FALSE(lazy_logger.IsCreated()); - EXPECT_EQ(0, constructed_seq_.GetNext()); - EXPECT_EQ(0, destructed_seq_.GetNext()); - - lazy_logger.Get(); - EXPECT_TRUE(lazy_logger.IsCreated()); - EXPECT_EQ(2, constructed_seq_.GetNext()); - EXPECT_EQ(1, destructed_seq_.GetNext()); - - lazy_logger.Pointer(); - EXPECT_TRUE(lazy_logger.IsCreated()); - EXPECT_EQ(3, constructed_seq_.GetNext()); - EXPECT_EQ(2, destructed_seq_.GetNext()); - } - EXPECT_FALSE(lazy_logger.IsCreated()); - EXPECT_EQ(4, constructed_seq_.GetNext()); - EXPECT_EQ(4, destructed_seq_.GetNext()); -} - -base::LazyInstance::DestructorAtExit lazy_slow = - LAZY_INSTANCE_INITIALIZER; - -TEST(LazyInstanceTest, ConstructorThreadSafety) { - { - base::ShadowingAtExitManager shadow; - - SlowDelegate delegate(&lazy_slow); - EXPECT_EQ(0, SlowConstructor::constructed); - - base::DelegateSimpleThreadPool pool("lazy_instance_cons", 5); - pool.AddWork(&delegate, 20); - EXPECT_EQ(0, SlowConstructor::constructed); - - pool.Start(); - pool.JoinAll(); - EXPECT_EQ(1, SlowConstructor::constructed); - } -} - -namespace { - -// DeleteLogger is an object which sets a flag when it's destroyed. -// It accepts a bool* and sets the bool to true when the dtor runs. -class DeleteLogger { - public: - DeleteLogger() : deleted_(nullptr) {} - ~DeleteLogger() { *deleted_ = true; } - - void SetDeletedPtr(bool* deleted) { - deleted_ = deleted; - } - - private: - bool* deleted_; -}; - -} // anonymous namespace - -TEST(LazyInstanceTest, LeakyLazyInstance) { - // Check that using a plain LazyInstance causes the dtor to run - // when the AtExitManager finishes. - bool deleted1 = false; - { - base::ShadowingAtExitManager shadow; - static base::LazyInstance::DestructorAtExit test = - LAZY_INSTANCE_INITIALIZER; - test.Get().SetDeletedPtr(&deleted1); - } - EXPECT_TRUE(deleted1); - - // Check that using a *leaky* LazyInstance makes the dtor not run - // when the AtExitManager finishes. - bool deleted2 = false; - { - base::ShadowingAtExitManager shadow; - static base::LazyInstance::Leaky - test = LAZY_INSTANCE_INITIALIZER; - test.Get().SetDeletedPtr(&deleted2); - } - EXPECT_FALSE(deleted2); -} - -namespace { - -template -class AlignedData { - public: - AlignedData() = default; - ~AlignedData() = default; - alignas(alignment) char data_[alignment]; -}; - -} // namespace - -#define EXPECT_ALIGNED(ptr, align) \ - EXPECT_EQ(0u, reinterpret_cast(ptr) & (align - 1)) - -TEST(LazyInstanceTest, Alignment) { - using base::LazyInstance; - - // Create some static instances with increasing sizes and alignment - // requirements. By ordering this way, the linker will need to do some work to - // ensure proper alignment of the static data. - static LazyInstance>::DestructorAtExit align4 = - LAZY_INSTANCE_INITIALIZER; - static LazyInstance>::DestructorAtExit align32 = - LAZY_INSTANCE_INITIALIZER; - static LazyInstance>::DestructorAtExit align4096 = - LAZY_INSTANCE_INITIALIZER; - - EXPECT_ALIGNED(align4.Pointer(), 4); - EXPECT_ALIGNED(align32.Pointer(), 32); - EXPECT_ALIGNED(align4096.Pointer(), 4096); -} - -namespace { - -// A class whose constructor busy-loops until it is told to complete -// construction. -class BlockingConstructor { - public: - BlockingConstructor() { - EXPECT_FALSE(WasConstructorCalled()); - base::subtle::NoBarrier_Store(&constructor_called_, 1); - EXPECT_TRUE(WasConstructorCalled()); - while (!base::subtle::NoBarrier_Load(&complete_construction_)) - base::PlatformThread::YieldCurrentThread(); - done_construction_ = true; - } - - ~BlockingConstructor() { - // Restore static state for the next test. - base::subtle::NoBarrier_Store(&constructor_called_, 0); - base::subtle::NoBarrier_Store(&complete_construction_, 0); - } - - // Returns true if BlockingConstructor() was entered. - static bool WasConstructorCalled() { - return base::subtle::NoBarrier_Load(&constructor_called_); - } - - // Instructs BlockingConstructor() that it may now unblock its construction. - static void CompleteConstructionNow() { - base::subtle::NoBarrier_Store(&complete_construction_, 1); - } - - bool done_construction() { return done_construction_; } - - private: - // Use Atomic32 instead of AtomicFlag for them to be trivially initialized. - static base::subtle::Atomic32 constructor_called_; - static base::subtle::Atomic32 complete_construction_; - - bool done_construction_ = false; - - DISALLOW_COPY_AND_ASSIGN(BlockingConstructor); -}; - -// A SimpleThread running at |thread_priority| which invokes |before_get| -// (optional) and then invokes Get() on the LazyInstance it's assigned. -class BlockingConstructorThread : public base::SimpleThread { - public: - BlockingConstructorThread( - base::ThreadPriority thread_priority, - base::LazyInstance::DestructorAtExit* lazy, - base::OnceClosure before_get) - : SimpleThread("BlockingConstructorThread", Options(thread_priority)), - lazy_(lazy), - before_get_(std::move(before_get)) {} - - void Run() override { - if (before_get_) - std::move(before_get_).Run(); - EXPECT_TRUE(lazy_->Get().done_construction()); - } - - private: - base::LazyInstance::DestructorAtExit* lazy_; - base::OnceClosure before_get_; - - DISALLOW_COPY_AND_ASSIGN(BlockingConstructorThread); -}; - -// static -base::subtle::Atomic32 BlockingConstructor::constructor_called_ = 0; -// static -base::subtle::Atomic32 BlockingConstructor::complete_construction_ = 0; - -base::LazyInstance::DestructorAtExit lazy_blocking = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -// Tests that if the thread assigned to construct the LazyInstance runs at -// background priority : the foreground threads will yield to it enough for it -// to eventually complete construction. -// This is a regression test for https://crbug.com/797129. -TEST(LazyInstanceTest, PriorityInversionAtInitializationResolves) { - base::TimeTicks test_begin = base::TimeTicks::Now(); - - // Construct BlockingConstructor from a background thread. - BlockingConstructorThread background_getter( - base::ThreadPriority::BACKGROUND, &lazy_blocking, base::OnceClosure()); - background_getter.Start(); - - while (!BlockingConstructor::WasConstructorCalled()) - base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1)); - - // Spin 4 foreground thread per core contending to get the already under - // construction LazyInstance. When they are all running and poking at it : - // allow the background thread to complete its work. - const int kNumForegroundThreads = 4 * base::SysInfo::NumberOfProcessors(); - std::vector> foreground_threads; - base::RepeatingClosure foreground_thread_ready_callback = - base::BarrierClosure( - kNumForegroundThreads, - base::BindOnce(&BlockingConstructor::CompleteConstructionNow)); - for (int i = 0; i < kNumForegroundThreads; ++i) { - foreground_threads.push_back(std::make_unique( - base::ThreadPriority::NORMAL, &lazy_blocking, - foreground_thread_ready_callback)); - foreground_threads.back()->Start(); - } - - // This test will hang if the foreground threads become stuck in - // LazyInstance::Get() per the background thread never being scheduled to - // complete construction. - for (auto& foreground_thread : foreground_threads) - foreground_thread->Join(); - background_getter.Join(); - - // Fail if this test takes more than 5 seconds (it takes 5-10 seconds on a - // Z840 without r527445 but is expected to be fast (~30ms) with the fix). - EXPECT_LT(base::TimeTicks::Now() - test_begin, - base::TimeDelta::FromSeconds(5)); -} diff --git a/linux_util.cc b/linux_util.cc deleted file mode 100644 index ddf848eeb..000000000 --- a/linux_util.cc +++ /dev/null @@ -1,226 +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/linux_util.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "base/command_line.h" -#include "base/files/file_util.h" -#include "base/memory/singleton.h" -#include "base/process/launch.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_split.h" -#include "base/strings/string_tokenizer.h" -#include "base/strings/string_util.h" -#include "base/synchronization/lock.h" -#include "build/build_config.h" - -namespace { - -// Not needed for OS_CHROMEOS. -#if defined(OS_LINUX) -enum LinuxDistroState { - STATE_DID_NOT_CHECK = 0, - STATE_CHECK_STARTED = 1, - STATE_CHECK_FINISHED = 2, -}; - -// Helper class for GetLinuxDistro(). -class LinuxDistroHelper { - public: - // Retrieves the Singleton. - static LinuxDistroHelper* GetInstance() { - return base::Singleton::get(); - } - - // The simple state machine goes from: - // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED. - LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK) {} - ~LinuxDistroHelper() = default; - - // Retrieve the current state, if we're in STATE_DID_NOT_CHECK, - // we automatically move to STATE_CHECK_STARTED so nobody else will - // do the check. - LinuxDistroState State() { - base::AutoLock scoped_lock(lock_); - if (STATE_DID_NOT_CHECK == state_) { - state_ = STATE_CHECK_STARTED; - return STATE_DID_NOT_CHECK; - } - return state_; - } - - // Indicate the check finished, move to STATE_CHECK_FINISHED. - void CheckFinished() { - base::AutoLock scoped_lock(lock_); - DCHECK_EQ(STATE_CHECK_STARTED, state_); - state_ = STATE_CHECK_FINISHED; - } - - private: - base::Lock lock_; - LinuxDistroState state_; -}; -#endif // if defined(OS_LINUX) - -bool GetTasksForProcess(pid_t pid, std::vector* tids) { - char buf[256]; - snprintf(buf, sizeof(buf), "/proc/%d/task", pid); - - DIR* task = opendir(buf); - if (!task) { - DLOG(WARNING) << "Cannot open " << buf; - return false; - } - - struct dirent* dent; - while ((dent = readdir(task))) { - char* endptr; - const unsigned long int tid_ul = strtoul(dent->d_name, &endptr, 10); - if (tid_ul == ULONG_MAX || *endptr) - continue; - tids->push_back(tid_ul); - } - closedir(task); - return true; -} - -} // namespace - -namespace base { - -// Account for the terminating null character. -static const int kDistroSize = 128 + 1; - -// We use this static string to hold the Linux distro info. If we -// crash, the crash handler code will send this in the crash dump. -char g_linux_distro[kDistroSize] = -#if defined(OS_CHROMEOS) - "CrOS"; -#elif defined(OS_ANDROID) - "Android"; -#else // if defined(OS_LINUX) - "Unknown"; -#endif - -std::string GetLinuxDistro() { -#if defined(OS_CHROMEOS) || defined(OS_ANDROID) - return g_linux_distro; -#elif defined(OS_LINUX) - LinuxDistroHelper* distro_state_singleton = LinuxDistroHelper::GetInstance(); - LinuxDistroState state = distro_state_singleton->State(); - if (STATE_CHECK_FINISHED == state) - return g_linux_distro; - if (STATE_CHECK_STARTED == state) - return "Unknown"; // Don't wait for other thread to finish. - DCHECK_EQ(state, STATE_DID_NOT_CHECK); - // We do this check only once per process. If it fails, there's - // little reason to believe it will work if we attempt to run - // lsb_release again. - std::vector argv; - argv.push_back("lsb_release"); - argv.push_back("-d"); - std::string output; - GetAppOutput(CommandLine(argv), &output); - if (output.length() > 0) { - // lsb_release -d should return: Description:Distro Info - const char field[] = "Description:\t"; - if (output.compare(0, strlen(field), field) == 0) { - SetLinuxDistro(output.substr(strlen(field))); - } - } - distro_state_singleton->CheckFinished(); - return g_linux_distro; -#else - NOTIMPLEMENTED(); - return "Unknown"; -#endif -} - -void SetLinuxDistro(const std::string& distro) { - std::string trimmed_distro; - TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro); - strlcpy(g_linux_distro, trimmed_distro.c_str(), kDistroSize); -} - -pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data, - bool* syscall_supported) { - if (syscall_supported != nullptr) - *syscall_supported = false; - - std::vector tids; - if (!GetTasksForProcess(pid, &tids)) - return -1; - - std::unique_ptr syscall_data(new char[expected_data.length()]); - for (pid_t tid : tids) { - char buf[256]; - snprintf(buf, sizeof(buf), "/proc/%d/task/%d/syscall", pid, tid); - int fd = open(buf, O_RDONLY); - if (fd < 0) - continue; - if (syscall_supported != nullptr) - *syscall_supported = true; - bool read_ret = ReadFromFD(fd, syscall_data.get(), expected_data.length()); - close(fd); - if (!read_ret) - continue; - - if (0 == strncmp(expected_data.c_str(), syscall_data.get(), - expected_data.length())) { - return tid; - } - } - return -1; -} - -pid_t FindThreadID(pid_t pid, pid_t ns_tid, bool* ns_pid_supported) { - if (ns_pid_supported) - *ns_pid_supported = false; - - std::vector tids; - if (!GetTasksForProcess(pid, &tids)) - return -1; - - for (pid_t tid : tids) { - char buf[256]; - snprintf(buf, sizeof(buf), "/proc/%d/task/%d/status", pid, tid); - std::string status; - if (!ReadFileToString(FilePath(buf), &status)) - return -1; - StringTokenizer tokenizer(status, "\n"); - while (tokenizer.GetNext()) { - StringPiece value_str(tokenizer.token_piece()); - if (!value_str.starts_with("NSpid")) - continue; - if (ns_pid_supported) - *ns_pid_supported = true; - std::vector split_value_str = SplitStringPiece( - value_str, "\t", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); - DCHECK_GE(split_value_str.size(), 2u); - int value; - // The last value in the list is the PID in the namespace. - if (StringToInt(split_value_str.back(), &value) && value == ns_tid) { - // The second value in the list is the real PID. - if (StringToInt(split_value_str[1], &value)) - return value; - } - break; - } - } - return -1; -} - -} // namespace base diff --git a/linux_util.h b/linux_util.h deleted file mode 100644 index 272e06b7d..000000000 --- a/linux_util.h +++ /dev/null @@ -1,44 +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_LINUX_UTIL_H_ -#define BASE_LINUX_UTIL_H_ - -#include -#include - -#include - -#include "base/base_export.h" - -namespace base { - -// 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[]; - -// Get the Linux Distro if we can, or return "Unknown". -BASE_EXPORT std::string GetLinuxDistro(); - -// Set the Linux Distro string. -BASE_EXPORT void SetLinuxDistro(const std::string& distro); - -// 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|. -// Returns the thread id or -1 on error. If |syscall_supported| is -// set to false the kernel does not support syscall in procfs. -BASE_EXPORT pid_t FindThreadIDWithSyscall(pid_t pid, - const std::string& expected_data, - bool* syscall_supported); - -// For a given process |pid|, look through all its threads and find the first -// thread with /proc/[pid]/task/[thread_id]/status where NSpid matches |ns_tid|. -// Returns the thread id or -1 on error. If |ns_pid_supported| is -// set to false the kernel does not support NSpid in procfs. -BASE_EXPORT pid_t FindThreadID(pid_t pid, pid_t ns_tid, bool* ns_pid_supported); - -} // namespace base - -#endif // BASE_LINUX_UTIL_H_ diff --git a/location.cc b/location.cc deleted file mode 100644 index 8bbf6edaa..000000000 --- a/location.cc +++ /dev/null @@ -1,77 +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/location.h" - -#if defined(COMPILER_MSVC) -#include -#endif - -#include "base/compiler_specific.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" -#include "build/build_config.h" - -namespace base { - -Location::Location() = default; -Location::Location(const Location& other) = default; - -Location::Location(const char* file_name, const void* program_counter) - : file_name_(file_name), program_counter_(program_counter) {} - -Location::Location(const char* function_name, - const char* file_name, - int line_number, - const void* program_counter) - : function_name_(function_name), - file_name_(file_name), - line_number_(line_number), - program_counter_(program_counter) { -#if !defined(OS_NACL) - // The program counter should not be null except in a default constructed - // (empty) Location object. This value is used for identity, so if it doesn't - // uniquely identify a location, things will break. - // - // The program counter isn't supported in NaCl so location objects won't work - // properly in that context. - DCHECK(program_counter); -#endif -} - -std::string Location::ToString() const { - if (has_source_info()) { - return std::string(function_name_) + "@" + file_name_ + ":" + - IntToString(line_number_); - } - return StringPrintf("pc:%p", program_counter_); -} - -#if defined(COMPILER_MSVC) -#define RETURN_ADDRESS() _ReturnAddress() -#elif defined(COMPILER_GCC) && !defined(OS_NACL) -#define RETURN_ADDRESS() \ - __builtin_extract_return_addr(__builtin_return_address(0)) -#else -#define RETURN_ADDRESS() nullptr -#endif - -// static -NOINLINE Location Location::CreateFromHere(const char* file_name) { - return Location(file_name, RETURN_ADDRESS()); -} - -// static -NOINLINE Location Location::CreateFromHere(const char* function_name, - const char* file_name, - int line_number) { - return Location(function_name, file_name, line_number, RETURN_ADDRESS()); -} - -//------------------------------------------------------------------------------ -NOINLINE const void* GetProgramCounter() { - return RETURN_ADDRESS(); -} - -} // namespace base diff --git a/location.h b/location.h deleted file mode 100644 index 14fe2fa6b..000000000 --- a/location.h +++ /dev/null @@ -1,117 +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_LOCATION_H_ -#define BASE_LOCATION_H_ - -#include - -#include -#include - -#include "base/base_export.h" -#include "base/debug/debugging_buildflags.h" -#include "base/hash.h" - -namespace base { - -// Location provides basic info where of an object was constructed, or was -// significantly brought to life. -class BASE_EXPORT Location { - public: - Location(); - Location(const Location& other); - - // Only initializes the file name and program counter, the source information - // will be null for the strings, and -1 for the line number. - // TODO(http://crbug.com/760702) remove file name from this constructor. - Location(const char* file_name, const void* program_counter); - - // Constructor should be called with a long-lived char*, such as __FILE__. - // It assumes the provided value will persist as a global constant, and it - // will not make a copy of it. - Location(const char* function_name, - const char* file_name, - int line_number, - const void* program_counter); - - // Comparator for hash map insertion. The program counter should uniquely - // identify a location. - bool operator==(const Location& other) const { - return program_counter_ == other.program_counter_; - } - - // Returns true if there is source code location info. If this is false, - // the Location object only contains a program counter or is - // default-initialized (the program counter is also null). - bool has_source_info() const { return function_name_ && file_name_; } - - // Will be nullptr for default initialized Location objects and when source - // names are disabled. - const char* function_name() const { return function_name_; } - - // Will be nullptr for default initialized Location objects and when source - // names are disabled. - const char* file_name() const { return file_name_; } - - // Will be -1 for default initialized Location objects and when source names - // are disabled. - int line_number() const { return line_number_; } - - // The address of the code generating this Location object. Should always be - // valid except for default initialized Location objects, which will be - // nullptr. - const void* program_counter() const { return program_counter_; } - - // Converts to the most user-readable form possible. If function and filename - // are not available, this will return "pc:". - std::string ToString() const; - - static Location CreateFromHere(const char* file_name); - static Location CreateFromHere(const char* function_name, - const char* file_name, - int line_number); - - private: - const char* function_name_ = nullptr; - const char* file_name_ = nullptr; - int line_number_ = -1; - const void* program_counter_ = nullptr; -}; - -BASE_EXPORT const void* GetProgramCounter(); - -// The macros defined here will expand to the current function. -#if BUILDFLAG(ENABLE_LOCATION_SOURCE) - -// Full source information should be included. -#define FROM_HERE FROM_HERE_WITH_EXPLICIT_FUNCTION(__func__) -#define FROM_HERE_WITH_EXPLICIT_FUNCTION(function_name) \ - ::base::Location::CreateFromHere(function_name, __FILE__, __LINE__) - -#else - -// TODO(http://crbug.com/760702) remove the __FILE__ argument from these calls. -#define FROM_HERE ::base::Location::CreateFromHere(__FILE__) -#define FROM_HERE_WITH_EXPLICIT_FUNCTION(function_name) \ - ::base::Location::CreateFromHere(function_name, __FILE__, -1) - -#endif - -} // namespace base - -namespace std { - -// Specialization for using Location in hash tables. -template <> -struct hash<::base::Location> { - std::size_t operator()(const ::base::Location& loc) const { - const void* program_counter = loc.program_counter(); - return base::Hash(&program_counter, sizeof(void*)); - } -}; - -} // namespace std - -#endif // BASE_LOCATION_H_ diff --git a/logging.cc b/logging.cc deleted file mode 100644 index 8eabda0ee..000000000 --- a/logging.cc +++ /dev/null @@ -1,1054 +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/logging.h" - -#include -#include - -#include "base/macros.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#include -typedef HANDLE FileHandle; -typedef HANDLE MutexHandle; -// Windows warns on using write(). It prefers _write(). -#define write(fd, buf, count) _write(fd, buf, static_cast(count)) -// Windows doesn't define STDERR_FILENO. Define it here. -#define STDERR_FILENO 2 - -#elif defined(OS_MACOSX) -// In MacOS 10.12 and iOS 10.0 and later ASL (Apple System Log) was deprecated -// in favor of OS_LOG (Unified Logging). -#include -#if defined(OS_IOS) -#if !defined(__IPHONE_10_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0 -#define USE_ASL -#endif -#else // !defined(OS_IOS) -#if !defined(MAC_OS_X_VERSION_10_12) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 -#define USE_ASL -#endif -#endif // defined(OS_IOS) - -#if defined(USE_ASL) -#include -#else -#include -#endif - -#include -#include -#include -#include - -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#if defined(OS_NACL) -#include // timespec doesn't seem to be in -#endif -#include -#endif - -#if defined(OS_FUCHSIA) -#include -#include -#endif - -#if defined(OS_ANDROID) -#include -#endif - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#include -#include -#include -#include -#include -#include -#include -#define MAX_PATH PATH_MAX -typedef FILE* FileHandle; -typedef pthread_mutex_t* MutexHandle; -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "base/base_switches.h" -#include "base/callback.h" -#include "base/command_line.h" -#include "base/containers/stack.h" -#include "base/debug/activity_tracker.h" -#include "base/debug/alias.h" -#include "base/debug/debugger.h" -#include "base/debug/stack_trace.h" -#include "base/lazy_instance.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/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "base/synchronization/lock_impl.h" -#include "base/threading/platform_thread.h" -#include "base/vlog.h" - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include "base/posix/safe_strerror.h" -#endif - -namespace logging { - -namespace { - -VlogInfo* g_vlog_info = nullptr; -VlogInfo* g_vlog_info_prev = nullptr; - -const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"}; -static_assert(LOG_NUM_SEVERITIES == arraysize(log_severity_names), - "Incorrect number of log_severity_names"); - -const char* log_severity_name(int severity) { - if (severity >= 0 && severity < LOG_NUM_SEVERITIES) - return log_severity_names[severity]; - return "UNKNOWN"; -} - -int g_min_log_level = 0; - -LoggingDestination g_logging_destination = LOG_DEFAULT; - -// For LOG_ERROR and above, always print to stderr. -const int kAlwaysPrintErrorLevel = LOG_ERROR; - -// Which log file to use? This is initialized by InitLogging or -// will be lazily initialized to the default value when it is -// first needed. -#if defined(OS_WIN) -typedef std::wstring PathString; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -typedef std::string PathString; -#endif -PathString* g_log_file_name = nullptr; - -// This file is lazily opened and the handle may be nullptr -FileHandle g_log_file = nullptr; - -// What should be prepended to each message? -bool g_log_process_id = false; -bool g_log_thread_id = false; -bool g_log_timestamp = true; -bool g_log_tickcount = false; - -// Should we pop up fatal debug messages in a dialog? -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. Assert handlers are stored -// in stack to allow overriding and restoring. -base::LazyInstance>::Leaky - log_assert_handler_stack = LAZY_INSTANCE_INITIALIZER; - -// A log message handler that gets notified of every log message we process. -LogMessageHandlerFunction log_message_handler = nullptr; - -// Helper functions to wrap platform differences. - -int32_t CurrentProcessId() { -#if defined(OS_WIN) - return GetCurrentProcessId(); -#elif defined(OS_FUCHSIA) - zx_info_handle_basic_t basic = {}; - zx_object_get_info(zx_process_self(), ZX_INFO_HANDLE_BASIC, &basic, - sizeof(basic), nullptr, nullptr); - return basic.koid; -#elif defined(OS_POSIX) - return getpid(); -#endif -} - -uint64_t TickCount() { -#if defined(OS_WIN) - return GetTickCount(); -#elif defined(OS_FUCHSIA) - return zx_clock_get(ZX_CLOCK_MONOTONIC) / - static_cast(base::Time::kNanosecondsPerMicrosecond); -#elif defined(OS_MACOSX) - return mach_absolute_time(); -#elif defined(OS_NACL) - // NaCl sadly does not have _POSIX_TIMERS enabled in sys/features.h - // So we have to use clock() for now. - return clock(); -#elif defined(OS_POSIX) - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - - uint64_t absolute_micro = static_cast(ts.tv_sec) * 1000000 + - static_cast(ts.tv_nsec) / 1000; - - return absolute_micro; -#endif -} - -void DeleteFilePath(const PathString& log_name) { -#if defined(OS_WIN) - DeleteFile(log_name.c_str()); -#elif defined(OS_NACL) - // Do nothing; unlink() isn't supported on NaCl. -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - unlink(log_name.c_str()); -#else -#error Unsupported platform -#endif -} - -PathString GetDefaultLogFile() { -#if defined(OS_WIN) - // On Windows we use the same path as the exe. - wchar_t module_name[MAX_PATH]; - GetModuleFileName(nullptr, module_name, MAX_PATH); - - PathString log_name = module_name; - PathString::size_type last_backslash = log_name.rfind('\\', log_name.size()); - if (last_backslash != PathString::npos) - log_name.erase(last_backslash + 1); - log_name += L"debug.log"; - return log_name; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // On other platforms we just use the current directory. - return PathString("debug.log"); -#endif -} - -// We don't need locks on Windows for atomically appending to files. The OS -// provides this functionality. -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -// This class acts as a wrapper for locking the logging files. -// LoggingLock::Init() should be called from the main thread before any logging -// is done. Then whenever logging, be sure to have a local LoggingLock -// instance on the stack. This will ensure that the lock is unlocked upon -// exiting the frame. -// LoggingLocks can not be nested. -class LoggingLock { - public: - LoggingLock() { - LockLogging(); - } - - ~LoggingLock() { - UnlockLogging(); - } - - static void Init(LogLockingState lock_log, const PathChar* new_log_file) { - if (initialized) - return; - lock_log_file = lock_log; - - if (lock_log_file != LOCK_LOG_FILE) - log_lock = new base::internal::LockImpl(); - - initialized = true; - } - - private: - static void LockLogging() { - if (lock_log_file == LOCK_LOG_FILE) { - pthread_mutex_lock(&log_mutex); - } else { - // use the lock - log_lock->Lock(); - } - } - - static void UnlockLogging() { - if (lock_log_file == LOCK_LOG_FILE) { - pthread_mutex_unlock(&log_mutex); - } else { - log_lock->Unlock(); - } - } - - // The lock is used if log file locking is false. It helps us avoid problems - // with multiple threads writing to the log file at the same time. Use - // LockImpl directly instead of using Lock, because Lock makes logging calls. - static base::internal::LockImpl* log_lock; - - // When we don't use a lock, we are using a global mutex. We need to do this - // because LockFileEx is not thread safe. - static pthread_mutex_t log_mutex; - - static bool initialized; - static LogLockingState lock_log_file; -}; - -// static -bool LoggingLock::initialized = false; -// static -base::internal::LockImpl* LoggingLock::log_lock = nullptr; -// static -LogLockingState LoggingLock::lock_log_file = LOCK_LOG_FILE; - -pthread_mutex_t LoggingLock::log_mutex = PTHREAD_MUTEX_INITIALIZER; - -#endif // OS_POSIX || OS_FUCHSIA - -// Called by logging functions to ensure that |g_log_file| is initialized -// and can be used for writing. Returns false if the file could not be -// initialized. |g_log_file| will be nullptr in this case. -bool InitializeLogFileHandle() { - if (g_log_file) - return true; - - if (!g_log_file_name) { - // Nobody has called InitLogging to specify a debug log file, so here we - // initialize the log file name to a default. - g_log_file_name = new PathString(GetDefaultLogFile()); - } - - if ((g_logging_destination & LOG_TO_FILE) != 0) { -#if defined(OS_WIN) - // The FILE_APPEND_DATA access mask ensures that the file is atomically - // appended to across accesses from multiple threads. - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364399(v=vs.85).aspx - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx - g_log_file = CreateFile(g_log_file_name->c_str(), FILE_APPEND_DATA, - FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (g_log_file == INVALID_HANDLE_VALUE || g_log_file == nullptr) { - // We are intentionally not using FilePath or FileUtil here to reduce the - // dependencies of the logging implementation. For e.g. FilePath and - // FileUtil depend on shell32 and user32.dll. This is not acceptable for - // some consumers of base logging like chrome_elf, etc. - // Please don't change the code below to use FilePath. - // try the current directory - wchar_t system_buffer[MAX_PATH]; - system_buffer[0] = 0; - DWORD len = ::GetCurrentDirectory(arraysize(system_buffer), - system_buffer); - if (len == 0 || len > arraysize(system_buffer)) - return false; - - *g_log_file_name = system_buffer; - // Append a trailing backslash if needed. - if (g_log_file_name->back() != L'\\') - *g_log_file_name += L"\\"; - *g_log_file_name += L"debug.log"; - - g_log_file = CreateFile(g_log_file_name->c_str(), FILE_APPEND_DATA, - FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (g_log_file == INVALID_HANDLE_VALUE || g_log_file == nullptr) { - g_log_file = nullptr; - return false; - } - } -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - g_log_file = fopen(g_log_file_name->c_str(), "a"); - if (g_log_file == nullptr) - return false; -#else -#error Unsupported platform -#endif - } - - return true; -} - -void CloseFile(FileHandle log) { -#if defined(OS_WIN) - CloseHandle(log); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - fclose(log); -#else -#error Unsupported platform -#endif -} - -void CloseLogFileUnlocked() { - if (!g_log_file) - return; - - CloseFile(g_log_file); - g_log_file = nullptr; -} - -} // namespace - -#if DCHECK_IS_CONFIGURABLE -// In DCHECK-enabled Chrome builds, allow the meaning of LOG_DCHECK to be -// determined at run-time. We default it to INFO, to avoid it triggering -// crashes before the run-time has explicitly chosen the behaviour. -BASE_EXPORT logging::LogSeverity LOG_DCHECK = LOG_INFO; -#endif // DCHECK_IS_CONFIGURABLE - -// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have -// an object of the correct type on the LHS of the unused part of the ternary -// operator. -std::ostream* g_swallow_stream; - -LoggingSettings::LoggingSettings() - : logging_dest(LOG_DEFAULT), - log_file(nullptr), - lock_log(LOCK_LOG_FILE), - 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 - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - // Don't bother initializing |g_vlog_info| unless we use one of the - // vlog switches. - if (command_line->HasSwitch(switches::kV) || - command_line->HasSwitch(switches::kVModule)) { - // NOTE: If |g_vlog_info| has already been initialized, it might be in use - // by another thread. Don't delete the old VLogInfo, just create a second - // one. We keep track of both to avoid memory leak warnings. - CHECK(!g_vlog_info_prev); - g_vlog_info_prev = g_vlog_info; - - g_vlog_info = - new VlogInfo(command_line->GetSwitchValueASCII(switches::kV), - command_line->GetSwitchValueASCII(switches::kVModule), - &g_min_log_level); - } - - g_logging_destination = settings.logging_dest; - - // ignore file options unless logging to file is set. - if ((g_logging_destination & LOG_TO_FILE) == 0) - return true; - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - LoggingLock::Init(settings.lock_log, settings.log_file); - LoggingLock logging_lock; -#endif - - // Calling InitLogging twice or after some log call has already opened the - // default log file will re-initialize to the new options. - CloseLogFileUnlocked(); - - if (!g_log_file_name) - g_log_file_name = new PathString(); - *g_log_file_name = settings.log_file; - if (settings.delete_old == DELETE_OLD_LOG_FILE) - DeleteFilePath(*g_log_file_name); - - return InitializeLogFileHandle(); -} - -void SetMinLogLevel(int level) { - g_min_log_level = std::min(LOG_FATAL, level); -} - -int GetMinLogLevel() { - return g_min_log_level; -} - -bool ShouldCreateLogMessage(int severity) { - if (severity < g_min_log_level) - return false; - - // Return true here unless we know ~LogMessage won't do anything. Note that - // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even - // when g_logging_destination is LOG_NONE. - return g_logging_destination != LOG_NONE || log_message_handler || - severity >= kAlwaysPrintErrorLevel; -} - -int GetVlogVerbosity() { - return std::max(-1, LOG_INFO - GetMinLogLevel()); -} - -int GetVlogLevelHelper(const char* file, size_t N) { - DCHECK_GT(N, 0U); - // Note: |g_vlog_info| may change on a different thread during startup - // (but will always be valid or nullptr). - VlogInfo* vlog_info = g_vlog_info; - return vlog_info ? - vlog_info->GetVlogLevel(base::StringPiece(file, N - 1)) : - GetVlogVerbosity(); -} - -void SetLogItems(bool enable_process_id, bool enable_thread_id, - bool enable_timestamp, bool enable_tickcount) { - g_log_process_id = enable_process_id; - g_log_thread_id = enable_thread_id; - g_log_timestamp = enable_timestamp; - g_log_tickcount = enable_tickcount; -} - -void SetShowErrorDialogs(bool enable_dialogs) { - show_error_dialogs = enable_dialogs; -} - -ScopedLogAssertHandler::ScopedLogAssertHandler( - LogAssertHandlerFunction handler) { - log_assert_handler_stack.Get().push(std::move(handler)); -} - -ScopedLogAssertHandler::~ScopedLogAssertHandler() { - log_assert_handler_stack.Get().pop(); -} - -void SetLogMessageHandler(LogMessageHandlerFunction handler) { - log_message_handler = handler; -} - -LogMessageHandlerFunction GetLogMessageHandler() { - return log_message_handler; -} - -// Explicit instantiations for commonly used comparisons. -template std::string* MakeCheckOpString( - const int&, const int&, const char* names); -template std::string* MakeCheckOpString( - const unsigned long&, const unsigned long&, const char* names); -template std::string* MakeCheckOpString( - const unsigned long&, const unsigned int&, const char* names); -template std::string* MakeCheckOpString( - const unsigned int&, const unsigned long&, const char* names); -template std::string* MakeCheckOpString( - const std::string&, const std::string&, const char* name); - -void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p) { - (*os) << "nullptr"; -} - -#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 -// (like release builds) where users could see it, since users don't -// understand these messages anyway. -void DisplayDebugMessageInDialog(const std::string& str) { - if (str.empty()) - return; - - if (!show_error_dialogs) - return; - -#if defined(OS_WIN) - // We intentionally don't implement a dialog on other platforms. - // You can just look at stderr. - MessageBoxW(nullptr, base::UTF8ToUTF16(str).c_str(), L"Fatal error", - MB_OK | MB_ICONHAND | MB_TOPMOST); -#endif // defined(OS_WIN) -} -#endif // !defined(NDEBUG) - -#if defined(OS_WIN) -LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) { -} - -LogMessage::SaveLastError::~SaveLastError() { - ::SetLastError(last_error_); -} -#endif // defined(OS_WIN) - -LogMessage::LogMessage(const char* file, int line, LogSeverity severity) - : severity_(severity), file_(file), line_(line) { - Init(file, line); -} - -LogMessage::LogMessage(const char* file, int line, const char* condition) - : severity_(LOG_FATAL), file_(file), line_(line) { - Init(file, line); - stream_ << "Check failed: " << condition << ". "; -} - -LogMessage::LogMessage(const char* file, int line, std::string* result) - : severity_(LOG_FATAL), file_(file), line_(line) { - Init(file, line); - stream_ << "Check failed: " << *result; - delete result; -} - -LogMessage::LogMessage(const char* file, int line, LogSeverity severity, - std::string* result) - : severity_(severity), file_(file), line_(line) { - Init(file, line); - stream_ << "Check failed: " << *result; - delete result; -} - -LogMessage::~LogMessage() { - size_t stack_start = stream_.tellp(); -#if !defined(OFFICIAL_BUILD) && !defined(OS_NACL) && !defined(__UCLIBC__) && \ - !defined(OS_AIX) - if (severity_ == LOG_FATAL && !base::debug::BeingDebugged()) { - // Include a stack trace on a fatal, unless a debugger is attached. - base::debug::StackTrace trace; - stream_ << std::endl; // Newline to separate from log message. - trace.OutputToStream(&stream_); - } -#endif - stream_ << std::endl; - std::string str_newline(stream_.str()); - - // Give any log message handler first dibs on the message. - if (log_message_handler && - log_message_handler(severity_, file_, line_, - message_start_, str_newline)) { - // The handler took care of it, no further processing. - return; - } - - if ((g_logging_destination & LOG_TO_SYSTEM_DEBUG_LOG) != 0) { -#if defined(OS_WIN) - OutputDebugStringA(str_newline.c_str()); -#elif defined(OS_MACOSX) - // In LOG_TO_SYSTEM_DEBUG_LOG mode, log messages are always written to - // stderr. If stderr is /dev/null, also log via ASL (Apple System Log) or - // its successor OS_LOG. If there's something weird about stderr, assume - // that log messages are going nowhere and log via ASL/OS_LOG too. - // Messages logged via ASL/OS_LOG show up in Console.app. - // - // Programs started by launchd, as UI applications normally are, have had - // stderr connected to /dev/null since OS X 10.8. Prior to that, stderr was - // a pipe to launchd, which logged what it received (see log_redirect_fd in - // 10.7.5 launchd-392.39/launchd/src/launchd_core_logic.c). - // - // Another alternative would be to determine whether stderr is a pipe to - // launchd and avoid logging via ASL only in that case. See 10.7.5 - // CF-635.21/CFUtilities.c also_do_stderr(). This would result in logging to - // both stderr and ASL/OS_LOG even in tests, where it's undesirable to log - // to the system log at all. - // - // Note that the ASL client by default discards messages whose levels are - // below ASL_LEVEL_NOTICE. It's possible to change that with - // asl_set_filter(), but this is pointless because syslogd normally applies - // the same filter. - const bool log_to_system = []() { - struct stat stderr_stat; - if (fstat(fileno(stderr), &stderr_stat) == -1) { - return true; - } - if (!S_ISCHR(stderr_stat.st_mode)) { - return false; - } - - struct stat dev_null_stat; - if (stat(_PATH_DEVNULL, &dev_null_stat) == -1) { - return true; - } - - return !S_ISCHR(dev_null_stat.st_mode) || - stderr_stat.st_rdev == dev_null_stat.st_rdev; - }(); - - if (log_to_system) { - // Log roughly the same way that CFLog() and NSLog() would. See 10.10.5 - // CF-1153.18/CFUtilities.c __CFLogCString(). - CFBundleRef main_bundle = CFBundleGetMainBundle(); - CFStringRef main_bundle_id_cf = - main_bundle ? CFBundleGetIdentifier(main_bundle) : nullptr; - std::string main_bundle_id = - main_bundle_id_cf ? base::SysCFStringRefToUTF8(main_bundle_id_cf) - : std::string(""); -#if defined(USE_ASL) - // The facility is set to the main bundle ID if available. Otherwise, - // "com.apple.console" is used. - const class ASLClient { - public: - explicit ASLClient(const std::string& facility) - : client_(asl_open(nullptr, facility.c_str(), ASL_OPT_NO_DELAY)) {} - ~ASLClient() { asl_close(client_); } - - aslclient get() const { return client_; } - - private: - aslclient client_; - DISALLOW_COPY_AND_ASSIGN(ASLClient); - } asl_client(main_bundle_id.empty() ? main_bundle_id - : "com.apple.console"); - - const class ASLMessage { - public: - ASLMessage() : message_(asl_new(ASL_TYPE_MSG)) {} - ~ASLMessage() { asl_free(message_); } - - aslmsg get() const { return message_; } - - private: - aslmsg message_; - DISALLOW_COPY_AND_ASSIGN(ASLMessage); - } asl_message; - - // By default, messages are only readable by the admin group. Explicitly - // make them readable by the user generating the messages. - char euid_string[12]; - snprintf(euid_string, arraysize(euid_string), "%d", geteuid()); - asl_set(asl_message.get(), ASL_KEY_READ_UID, euid_string); - - // Map Chrome log severities to ASL log levels. - const char* const asl_level_string = [](LogSeverity severity) { - // ASL_LEVEL_* are ints, but ASL needs equivalent strings. This - // non-obvious two-step macro trick achieves what's needed. - // https://gcc.gnu.org/onlinedocs/cpp/Stringification.html -#define ASL_LEVEL_STR(level) ASL_LEVEL_STR_X(level) -#define ASL_LEVEL_STR_X(level) #level - switch (severity) { - case LOG_INFO: - return ASL_LEVEL_STR(ASL_LEVEL_INFO); - case LOG_WARNING: - return ASL_LEVEL_STR(ASL_LEVEL_WARNING); - case LOG_ERROR: - return ASL_LEVEL_STR(ASL_LEVEL_ERR); - case LOG_FATAL: - return ASL_LEVEL_STR(ASL_LEVEL_CRIT); - default: - return severity < 0 ? ASL_LEVEL_STR(ASL_LEVEL_DEBUG) - : ASL_LEVEL_STR(ASL_LEVEL_NOTICE); - } -#undef ASL_LEVEL_STR -#undef ASL_LEVEL_STR_X - }(severity_); - asl_set(asl_message.get(), ASL_KEY_LEVEL, asl_level_string); - - asl_set(asl_message.get(), ASL_KEY_MSG, str_newline.c_str()); - - asl_send(asl_client.get(), asl_message.get()); -#else // !defined(USE_ASL) - const class OSLog { - public: - explicit OSLog(const char* subsystem) - : os_log_(subsystem ? os_log_create(subsystem, "chromium_logging") - : OS_LOG_DEFAULT) {} - ~OSLog() { - if (os_log_ != OS_LOG_DEFAULT) { - os_release(os_log_); - } - } - os_log_t get() const { return os_log_; } - - private: - os_log_t os_log_; - DISALLOW_COPY_AND_ASSIGN(OSLog); - } log(main_bundle_id.empty() ? nullptr : main_bundle_id.c_str()); - const os_log_type_t os_log_type = [](LogSeverity severity) { - switch (severity) { - case LOG_INFO: - return OS_LOG_TYPE_INFO; - case LOG_WARNING: - return OS_LOG_TYPE_DEFAULT; - case LOG_ERROR: - return OS_LOG_TYPE_ERROR; - case LOG_FATAL: - return OS_LOG_TYPE_FAULT; - default: - return severity < 0 ? OS_LOG_TYPE_DEBUG : OS_LOG_TYPE_DEFAULT; - } - }(severity_); - os_log_with_type(log.get(), os_log_type, "%{public}s", - str_newline.c_str()); -#endif // defined(USE_ASL) - } -#elif defined(OS_ANDROID) - android_LogPriority priority = - (severity_ < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN; - switch (severity_) { - case LOG_INFO: - priority = ANDROID_LOG_INFO; - break; - case LOG_WARNING: - priority = ANDROID_LOG_WARN; - break; - case LOG_ERROR: - priority = ANDROID_LOG_ERROR; - break; - case LOG_FATAL: - priority = ANDROID_LOG_FATAL; - break; - } - __android_log_write(priority, "chromium", str_newline.c_str()); -#endif - 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. - ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr)); - fflush(stderr); - } - - // write to log file - if ((g_logging_destination & LOG_TO_FILE) != 0) { - // We can have multiple threads and/or processes, so try to prevent them - // from clobbering each other's writes. - // If the client app did not call InitLogging, and the lock has not - // been created do it now. We do this on demand, but if two threads try - // to do this at the same time, there will be a race condition to create - // the lock. This is why InitLogging should be called from the main - // thread at the beginning of execution. -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - LoggingLock::Init(LOCK_LOG_FILE, nullptr); - LoggingLock logging_lock; -#endif - if (InitializeLogFileHandle()) { -#if defined(OS_WIN) - DWORD num_written; - WriteFile(g_log_file, - static_cast(str_newline.c_str()), - static_cast(str_newline.length()), - &num_written, - nullptr); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - ignore_result(fwrite( - str_newline.data(), str_newline.size(), 1, g_log_file)); - fflush(g_log_file); -#else -#error Unsupported platform -#endif - } - } - - if (severity_ == LOG_FATAL) { - // Write the log message to the global activity tracker, if running. - base::debug::GlobalActivityTracker* tracker = - base::debug::GlobalActivityTracker::Get(); - if (tracker) - tracker->RecordLogMessage(str_newline); - - // Ensure the first characters of the string are on the stack so they - // are contained in minidumps for diagnostic purposes. - DEBUG_ALIAS_FOR_CSTR(str_stack, str_newline.c_str(), 1024); - - if (log_assert_handler_stack.IsCreated() && - !log_assert_handler_stack.Get().empty()) { - LogAssertHandlerFunction log_assert_handler = - log_assert_handler_stack.Get().top(); - - if (log_assert_handler) { - log_assert_handler.Run( - file_, line_, - base::StringPiece(str_newline.c_str() + message_start_, - stack_start - message_start_), - base::StringPiece(str_newline.c_str() + stack_start)); - } - } 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. -#ifndef NDEBUG - if (!base::debug::BeingDebugged()) { - // Displaying a dialog is unnecessary when debugging and can complicate - // debugging. - DisplayDebugMessageInDialog(stream_.str()); - } -#endif - // Crash the process to generate a dump. - base::debug::BreakDebugger(); - } - } -} - -// writes the common header info to the stream -void LogMessage::Init(const char* file, int line) { - base::StringPiece filename(file); - size_t last_slash_pos = filename.find_last_of("\\/"); - if (last_slash_pos != base::StringPiece::npos) - filename.remove_prefix(last_slash_pos + 1); - - // TODO(darin): It might be nice if the columns were fixed width. - - stream_ << '['; - if (g_log_process_id) - stream_ << CurrentProcessId() << ':'; - if (g_log_thread_id) - stream_ << base::PlatformThread::CurrentId() << ':'; - if (g_log_timestamp) { -#if defined(OS_WIN) - SYSTEMTIME local_time; - GetLocalTime(&local_time); - stream_ << std::setfill('0') - << std::setw(2) << local_time.wMonth - << std::setw(2) << local_time.wDay - << '/' - << std::setw(2) << local_time.wHour - << std::setw(2) << local_time.wMinute - << std::setw(2) << local_time.wSecond - << '.' - << std::setw(3) - << local_time.wMilliseconds - << ':'; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - timeval tv; - gettimeofday(&tv, nullptr); - time_t t = tv.tv_sec; - struct tm local_time; - localtime_r(&t, &local_time); - struct tm* tm_time = &local_time; - stream_ << std::setfill('0') - << std::setw(2) << 1 + tm_time->tm_mon - << std::setw(2) << tm_time->tm_mday - << '/' - << std::setw(2) << tm_time->tm_hour - << std::setw(2) << tm_time->tm_min - << std::setw(2) << tm_time->tm_sec - << '.' - << std::setw(6) << tv.tv_usec - << ':'; -#else -#error Unsupported platform -#endif - } - if (g_log_tickcount) - stream_ << TickCount() << ':'; - if (severity_ >= 0) - stream_ << log_severity_name(severity_); - else - stream_ << "VERBOSE" << -severity_; - - stream_ << ":" << filename << "(" << line << ")] "; - - message_start_ = stream_.str().length(); -} - -#if defined(OS_WIN) -// This has already been defined in the header, but defining it again as DWORD -// ensures that the type used in the header is equivalent to DWORD. If not, -// the redefinition is a compile error. -typedef DWORD SystemErrorCode; -#endif - -SystemErrorCode GetLastSystemErrorCode() { -#if defined(OS_WIN) - return ::GetLastError(); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - return errno; -#endif -} - -BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) { -#if defined(OS_WIN) - const int kErrorMessageBufferSize = 256; - char msgbuf[kErrorMessageBufferSize]; - DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; - DWORD len = FormatMessageA(flags, nullptr, error_code, 0, msgbuf, - arraysize(msgbuf), nullptr); - if (len) { - // Messages returned by system end with line breaks. - return base::CollapseWhitespaceASCII(msgbuf, true) + - base::StringPrintf(" (0x%lX)", error_code); - } - return base::StringPrintf("Error (0x%lX) while retrieving error. (0x%lX)", - GetLastError(), error_code); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - return base::safe_strerror(error_code) + - base::StringPrintf(" (%d)", error_code); -#endif // defined(OS_WIN) -} - - -#if defined(OS_WIN) -Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, - int line, - LogSeverity severity, - SystemErrorCode err) - : err_(err), - log_message_(file, line, severity) { -} - -Win32ErrorLogMessage::~Win32ErrorLogMessage() { - 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_; - base::debug::Alias(&last_error); -} -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -ErrnoLogMessage::ErrnoLogMessage(const char* file, - int line, - LogSeverity severity, - SystemErrorCode err) - : err_(err), - log_message_(file, line, severity) { -} - -ErrnoLogMessage::~ErrnoLogMessage() { - 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. - int last_error = err_; - base::debug::Alias(&last_error); -} -#endif // defined(OS_WIN) - -void CloseLogFile() { -#if defined(OS_POSIX) || defined(OS_FUCHSIA) - LoggingLock logging_lock; -#endif - CloseLogFileUnlocked(); -} - -void RawLog(int level, const char* message) { - if (level >= g_min_log_level && message) { - size_t bytes_written = 0; - const size_t message_len = strlen(message); - int rv; - while (bytes_written < message_len) { - rv = HANDLE_EINTR( - write(STDERR_FILENO, message + bytes_written, - message_len - bytes_written)); - if (rv < 0) { - // Give up, nothing we can do now. - break; - } - bytes_written += rv; - } - - if (message_len > 0 && message[message_len - 1] != '\n') { - do { - rv = HANDLE_EINTR(write(STDERR_FILENO, "\n", 1)); - if (rv < 0) { - // Give up, nothing we can do now. - break; - } - } while (rv != 1); - } - } - - if (level == LOG_FATAL) - base::debug::BreakDebugger(); -} - -// This was defined at the beginning of this file. -#undef write - -#if defined(OS_WIN) -bool IsLoggingToFileEnabled() { - return g_logging_destination & LOG_TO_FILE; -} - -std::wstring GetLogFileFullPath() { - if (g_log_file_name) - return *g_log_file_name; - return std::wstring(); -} -#endif - -BASE_EXPORT void LogErrorNotReached(const char* file, int line) { - LogMessage(file, line, LOG_ERROR).stream() - << "NOTREACHED() hit."; -} - -} // namespace logging - -std::ostream& std::operator<<(std::ostream& out, const wchar_t* wstr) { - return out << (wstr ? base::WideToUTF8(wstr) : std::string()); -} diff --git a/logging.h b/logging.h deleted file mode 100644 index 08c1f0fc5..000000000 --- a/logging.h +++ /dev/null @@ -1,1179 +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_LOGGING_H_ -#define BASE_LOGGING_H_ - -#include - -#include -#include -#include -#include -#include -#include - -#include "base/base_export.h" -#include "base/callback_forward.h" -#include "base/compiler_specific.h" -#include "base/debug/debugger.h" -#include "base/macros.h" -#include "base/strings/string_piece_forward.h" -#include "base/template_util.h" -#include "build/build_config.h" - -// -// Optional message capabilities -// ----------------------------- -// Assertion failed messages and fatal errors are displayed in a dialog box -// before the application exits. However, running this UI creates a message -// loop, which causes application messages to be processed and potentially -// dispatched to existing application windows. Since the application is in a -// bad state when this assertion dialog is displayed, these messages may not -// get processed and hang the dialog, or the application might go crazy. -// -// Therefore, it can be beneficial to display the error dialog in a separate -// process from the main application. When the logging system needs to display -// a fatal error dialog box, it will look for a program called -// "DebugMessage.exe" in the same directory as the application executable. It -// will run this application with the message as the command line, and will -// not include the name of the application as is traditional for easier -// parsing. -// -// The code for DebugMessage.exe is only one line. In WinMain, do: -// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0); -// -// If DebugMessage.exe is not found, the logging code will use a normal -// MessageBox, potentially causing the problems discussed above. - - -// Instructions -// ------------ -// -// Make a bunch of macros for logging. The way to log things is to stream -// things to LOG(). E.g., -// -// LOG(INFO) << "Found " << num_cookies << " cookies"; -// -// You can also do conditional logging: -// -// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; -// -// 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. -// -// There are also "debug mode" logging macros like the ones above: -// -// DLOG(INFO) << "Found cookies"; -// -// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; -// -// All "debug mode" logging is compiled away to nothing for non-debug mode -// compiles. LOG_IF and development flags also work well together -// because the code can be compiled away sometimes. -// -// We also have -// -// LOG_ASSERT(assertion); -// DLOG_ASSERT(assertion); -// -// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; -// -// There are "verbose level" logging macros. They look like -// -// VLOG(1) << "I'm printed when you run the program with --v=1 or more"; -// VLOG(2) << "I'm printed when you run the program with --v=2 or more"; -// -// These always log at the INFO log level (when they log at all). -// The verbose logging can also be turned on module-by-module. For instance, -// --vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0 -// will cause: -// a. VLOG(2) and lower messages to be printed from profile.{h,cc} -// b. VLOG(1) and lower messages to be printed from icon_loader.{h,cc} -// c. VLOG(3) and lower messages to be printed from files prefixed with -// "browser" -// d. VLOG(4) and lower messages to be printed from files under a -// "chromeos" directory. -// e. VLOG(0) and lower messages to be printed from elsewhere -// -// The wildcarding functionality shown by (c) supports both '*' (match -// 0 or more characters) and '?' (match any single character) -// wildcards. Any pattern containing a forward or backward slash will -// be tested against the whole pathname and not just the module. -// E.g., "*/foo/bar/*=2" would change the logging level for all code -// in source files under a "foo/bar" directory. -// -// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as -// -// if (VLOG_IS_ON(2)) { -// // do some logging preparation and logging -// // that can't be accomplished with just VLOG(2) << ...; -// } -// -// There is also a VLOG_IF "verbose level" condition macro for sample -// cases, when some extra computation and preparation for logs is not -// needed. -// -// VLOG_IF(1, (size > 1024)) -// << "I'm printed when size is more than 1024 and when you run the " -// "program with --v=1 or more"; -// -// We also override the standard 'assert' to use 'DLOG_ASSERT'. -// -// Lastly, there is: -// -// PLOG(ERROR) << "Couldn't do foo"; -// DPLOG(ERROR) << "Couldn't do foo"; -// PLOG_IF(ERROR, cond) << "Couldn't do foo"; -// DPLOG_IF(ERROR, cond) << "Couldn't do foo"; -// PCHECK(condition) << "Couldn't do foo"; -// DPCHECK(condition) << "Couldn't do foo"; -// -// which append the last system error to the message in string form (taken from -// 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, and FATAL. -// -// Very important: logging a message at the FATAL severity level causes -// the program to terminate (after the message is logged). -// -// There is the special severity of DFATAL, which logs FATAL in debug mode, -// ERROR in normal mode. - -namespace logging { - -// TODO(avi): do we want to do a unification of character types here? -#if defined(OS_WIN) -typedef wchar_t PathChar; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -typedef char PathChar; -#endif - -// Where to record logging output? A flat file and/or system debug log -// via OutputDebugString. -enum LoggingDestination { - LOG_NONE = 0, - LOG_TO_FILE = 1 << 0, - LOG_TO_SYSTEM_DEBUG_LOG = 1 << 1, - - LOG_TO_ALL = LOG_TO_FILE | LOG_TO_SYSTEM_DEBUG_LOG, - - // On Windows, use a file next to the exe; on POSIX platforms, where - // it may not even be possible to locate the executable on disk, use - // stderr. -#if defined(OS_WIN) - LOG_DEFAULT = LOG_TO_FILE, -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG, -#endif -}; - -// 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 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. -enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE }; - -// On startup, should we delete or append to an existing log file (if any)? -// Defaults to APPEND_TO_OLD_LOG_FILE. -enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE }; - -struct BASE_EXPORT LoggingSettings { - // The defaults values are: - // - // logging_dest: LOG_DEFAULT - // log_file: NULL - // lock_log: LOCK_LOG_FILE - // delete_old: APPEND_TO_OLD_LOG_FILE - LoggingSettings(); - - LoggingDestination logging_dest; - - // The three settings below have an effect only when LOG_TO_FILE is - // set in |logging_dest|. - const PathChar* log_file; - LogLockingState lock_log; - OldFileDeletionState delete_old; -}; - -// Define different names for the BaseInitLoggingImpl() function depending on -// whether NDEBUG is defined or not so that we'll fail to link if someone tries -// to compile logging.cc with NDEBUG but includes logging.h without defining it, -// or vice versa. -#if defined(NDEBUG) -#define BaseInitLoggingImpl BaseInitLoggingImpl_built_with_NDEBUG -#else -#define BaseInitLoggingImpl BaseInitLoggingImpl_built_without_NDEBUG -#endif - -// Implementation of the InitLogging() method declared below. We use a -// more-specific name so we can #define it above without affecting other code -// that has named stuff "InitLogging". -BASE_EXPORT bool BaseInitLoggingImpl(const LoggingSettings& settings); - -// Sets the log file name and other global logging state. Calling this function -// is recommended, and is normally done at the beginning of application init. -// If you don't call it, all the flags will be initialized to their default -// values, and there is a race condition that may leak a critical section -// object if two threads try to do the first log at the same time. -// See the definition of the enums above for descriptions and default values. -// -// The default log file is initialized to "debug.log" in the application -// directory. You probably don't want this, especially since the program -// directory may not be writable on an enduser's system. -// -// This function may be called a second time to re-direct logging (e.g after -// loging in to a user partition), however it should never be called more than -// twice. -inline bool InitLogging(const LoggingSettings& settings) { - return BaseInitLoggingImpl(settings); -} - -// Sets the log level. Anything at or above this level will be written to the -// log file/displayed to the user (if applicable). Anything below this level -// will be silently ignored. The log level defaults to 0 (everything is logged -// up to level INFO) if this function is not called. -// Note that log messages for VLOG(x) are logged at level -x, so setting -// the min log level to negative values enables verbose logging. -BASE_EXPORT void SetMinLogLevel(int level); - -// Gets the current log level. -BASE_EXPORT int GetMinLogLevel(); - -// Used by LOG_IS_ON to lazy-evaluate stream arguments. -BASE_EXPORT bool ShouldCreateLogMessage(int severity); - -// Gets the VLOG default verbosity level. -BASE_EXPORT int GetVlogVerbosity(); - -// Note that |N| is the size *with* the null terminator. -BASE_EXPORT int GetVlogLevelHelper(const char* file_start, size_t N); - -// Gets the current vlog level for the given file (usually taken from __FILE__). -template -int GetVlogLevel(const char (&file)[N]) { - return GetVlogLevelHelper(file, N); -} - -// Sets the common items you want to be prepended to each log message. -// process and thread IDs default to off, the timestamp defaults to on. -// If this function is not called, logging defaults to writing the timestamp -// only. -BASE_EXPORT void SetLogItems(bool enable_process_id, bool enable_thread_id, - bool enable_timestamp, bool enable_tickcount); - -// Sets whether or not you'd like to see fatal debug messages popped up in -// a dialog box or not. -// Dialogs are not shown by default. -BASE_EXPORT void SetShowErrorDialogs(bool enable_dialogs); - -// Sets the Log Assert Handler that will be used to notify of check failures. -// Resets Log Assert Handler on object destruction. -// The default handler shows a dialog box and then terminate the process, -// however clients can use this function to override with their own handling -// (e.g. a silent one for Unit Tests) -using LogAssertHandlerFunction = - base::Callback; - -class BASE_EXPORT ScopedLogAssertHandler { - public: - explicit ScopedLogAssertHandler(LogAssertHandlerFunction handler); - ~ScopedLogAssertHandler(); - - private: - DISALLOW_COPY_AND_ASSIGN(ScopedLogAssertHandler); -}; - -// 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 -// should not be sent to other log destinations. -typedef bool (*LogMessageHandlerFunction)(int severity, - const char* file, int line, size_t message_start, const std::string& str); -BASE_EXPORT void SetLogMessageHandler(LogMessageHandlerFunction handler); -BASE_EXPORT LogMessageHandlerFunction GetLogMessageHandler(); - -// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints -// to Clang which control what code paths are statically analyzed, -// and is meant to be used in conjunction with assert & assert-like functions. -// The expression is passed straight through if analysis isn't enabled. -// -// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current -// codepath and any other branching codepaths that might follow. -#if defined(__clang_analyzer__) - -inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) { - return false; -} - -inline constexpr bool AnalyzerAssumeTrue(bool arg) { - // AnalyzerNoReturn() is invoked and analysis is terminated if |arg| is - // false. - return arg || AnalyzerNoReturn(); -} - -#define ANALYZER_ASSUME_TRUE(arg) logging::AnalyzerAssumeTrue(!!(arg)) -#define ANALYZER_SKIP_THIS_PATH() \ - static_cast(::logging::AnalyzerNoReturn()) -#define ANALYZER_ALLOW_UNUSED(var) static_cast(var); - -#else // !defined(__clang_analyzer__) - -#define ANALYZER_ASSUME_TRUE(arg) (arg) -#define ANALYZER_SKIP_THIS_PATH() -#define ANALYZER_ALLOW_UNUSED(var) static_cast(var); - -#endif // defined(__clang_analyzer__) - -typedef int LogSeverity; -const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity -// Note: the log severities are used to index into the array of names, -// see log_severity_names. -const LogSeverity LOG_INFO = 0; -const LogSeverity LOG_WARNING = 1; -const LogSeverity LOG_ERROR = 2; -const LogSeverity LOG_FATAL = 3; -const LogSeverity LOG_NUM_SEVERITIES = 4; - -// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode -#if defined(NDEBUG) -const LogSeverity LOG_DFATAL = LOG_ERROR; -#else -const LogSeverity LOG_DFATAL = LOG_FATAL; -#endif - -// A few definitions of macros that don't generate much code. These are used -// by LOG() and LOG_IF, etc. Since these are used all over our code, it's -// better to have compact code for these operations. -#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \ - ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_INFO, ##__VA_ARGS__) -#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \ - ::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_FATAL(ClassName, ...) \ - ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_FATAL, ##__VA_ARGS__) -#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \ - ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DFATAL, ##__VA_ARGS__) -#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ - ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DCHECK, ##__VA_ARGS__) - -#define COMPACT_GOOGLE_LOG_INFO COMPACT_GOOGLE_LOG_EX_INFO(LogMessage) -#define COMPACT_GOOGLE_LOG_WARNING COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage) -#define COMPACT_GOOGLE_LOG_ERROR COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage) -#define COMPACT_GOOGLE_LOG_FATAL COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage) -#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage) -#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_EX_DCHECK(LogMessage) - -#if defined(OS_WIN) -// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets -// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us -// to keep using this syntax, we define this macro to do the same thing -// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that -// the Windows SDK does for consistency. -#define ERROR 0 -#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \ - COMPACT_GOOGLE_LOG_EX_ERROR(ClassName , ##__VA_ARGS__) -#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR -// Needed for LOG_IS_ON(ERROR). -const LogSeverity LOG_0 = LOG_ERROR; -#endif - -// 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::ShouldCreateLogMessage(::logging::LOG_##severity)) - -// We can't do any caching tricks with VLOG_IS_ON() like the -// google-glog version since it requires GCC extensions. This means -// that using the v-logging functions in conjunction with --vmodule -// may be slow. -#define VLOG_IS_ON(verboselevel) \ - ((verboselevel) <= ::logging::GetVlogLevel(__FILE__)) - -// Helper macro which avoids evaluating the arguments to a stream if -// the condition doesn't hold. Condition is evaluated once and only once. -#define LAZY_STREAM(stream, condition) \ - !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream) - -// We use the preprocessor's merging operator, "##", so that, e.g., -// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny -// subtle difference between ostream member streaming functions (e.g., -// ostream::operator<<(int) and ostream non-member streaming functions -// (e.g., ::operator<<(ostream&, string&): it turns out that it's -// impossible to stream something like a string directly to an unnamed -// ostream. We employ a neat hack by calling the stream() member -// function of LogMessage which seems to avoid the problem. -#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() - -#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity)) -#define LOG_IF(severity, condition) \ - LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) - -// The VLOG macros log with negative verbosities. -#define VLOG_STREAM(verbose_level) \ - ::logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream() - -#define VLOG(verbose_level) \ - LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) - -#define VLOG_IF(verbose_level, condition) \ - LAZY_STREAM(VLOG_STREAM(verbose_level), \ - VLOG_IS_ON(verbose_level) && (condition)) - -#if defined (OS_WIN) -#define VPLOG_STREAM(verbose_level) \ - ::logging::Win32ErrorLogMessage(__FILE__, __LINE__, -verbose_level, \ - ::logging::GetLastSystemErrorCode()).stream() -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#define VPLOG_STREAM(verbose_level) \ - ::logging::ErrnoLogMessage(__FILE__, __LINE__, -verbose_level, \ - ::logging::GetLastSystemErrorCode()).stream() -#endif - -#define VPLOG(verbose_level) \ - LAZY_STREAM(VPLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) - -#define VPLOG_IF(verbose_level, condition) \ - LAZY_STREAM(VPLOG_STREAM(verbose_level), \ - VLOG_IS_ON(verbose_level) && (condition)) - -// TODO(akalin): Add more VLOG variants, e.g. VPLOG. - -#define LOG_ASSERT(condition) \ - LOG_IF(FATAL, !(ANALYZER_ASSUME_TRUE(condition))) \ - << "Assert failed: " #condition ". " - -#if defined(OS_WIN) -#define PLOG_STREAM(severity) \ - COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \ - ::logging::GetLastSystemErrorCode()).stream() -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#define PLOG_STREAM(severity) \ - COMPACT_GOOGLE_LOG_EX_ ## severity(ErrnoLogMessage, \ - ::logging::GetLastSystemErrorCode()).stream() -#endif - -#define PLOG(severity) \ - LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity)) - -#define PLOG_IF(severity, condition) \ - LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) - -BASE_EXPORT extern std::ostream* g_swallow_stream; - -// Note that g_swallow_stream is used instead of an arbitrary LOG() stream to -// avoid the creation of an object with a non-trivial destructor (LogMessage). -// On MSVC x86 (checked on 2015 Update 3), this causes a few additional -// pointless instructions to be emitted even at full optimization level, even -// though the : arm of the ternary operator is clearly never executed. Using a -// simpler object to be &'d with Voidify() avoids these extra instructions. -// Using a simpler POD object with a templated operator<< also works to avoid -// these instructions. However, this causes warnings on statically defined -// implementations of operator<<(std::ostream, ...) in some .cc files, because -// they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an -// ostream* also is not suitable, because some compilers warn of undefined -// behavior. -#define EAT_STREAM_PARAMETERS \ - true ? (void)0 \ - : ::logging::LogMessageVoidify() & (*::logging::g_swallow_stream) - -// Captures the result of a CHECK_EQ (for example) and facilitates testing as a -// boolean. -class CheckOpResult { - public: - // |message| must be non-null if and only if the check failed. - CheckOpResult(std::string* message) : message_(message) {} - // Returns true if the check succeeded. - operator bool() const { return !message_; } - // Returns the message. - std::string* message() { return message_; } - - private: - std::string* message_; -}; - -// Crashes in the fastest possible way with no attempt at logging. -// There are different constraints to satisfy here, see http://crbug.com/664209 -// for more context: -// - The trap instructions, and hence the PC value at crash time, have to be -// distinct and not get folded into the same opcode by the compiler. -// On Linux/Android this is tricky because GCC still folds identical -// asm volatile blocks. The workaround is generating distinct opcodes for -// each CHECK using the __COUNTER__ macro. -// - The debug info for the trap instruction has to be attributed to the source -// line that has the CHECK(), to make crash reports actionable. This rules -// out the ability of using a inline function, at least as long as clang -// doesn't support attribute(artificial). -// - Failed CHECKs should produce a signal that is distinguishable from an -// invalid memory access, to improve the actionability of crash reports. -// - The compiler should treat the CHECK as no-return instructions, so that the -// trap code can be efficiently packed in the prologue of the function and -// doesn't interfere with the main execution flow. -// - When debugging, developers shouldn't be able to accidentally step over a -// CHECK. This is achieved by putting opcodes that will cause a non -// continuable exception after the actual trap instruction. -// - Don't cause too much binary bloat. -#if defined(COMPILER_GCC) - -#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_NACL) -// int 3 will generate a SIGTRAP. -#define TRAP_SEQUENCE() \ - asm volatile( \ - "int3; ud2; push %0;" ::"i"(static_cast(__COUNTER__))) - -#elif defined(ARCH_CPU_ARMEL) && !defined(OS_NACL) -// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running -// as a 32 bit userspace app on arm64. There doesn't seem to be any way to -// cause a SIGTRAP from userspace without using a syscall (which would be a -// problem for sandboxing). -#define TRAP_SEQUENCE() \ - asm volatile("bkpt #0; udf %0;" ::"i"(__COUNTER__ % 256)) - -#elif defined(ARCH_CPU_ARM64) && !defined(OS_NACL) -// This will always generate a SIGTRAP on arm64. -#define TRAP_SEQUENCE() \ - asm volatile("brk #0; hlt %0;" ::"i"(__COUNTER__ % 65536)) - -#else -// Crash report accuracy will not be guaranteed on other architectures, but at -// least this will crash as expected. -#define TRAP_SEQUENCE() __builtin_trap() -#endif // ARCH_CPU_* - -// CHECK() and the trap sequence can be invoked from a constexpr function. -// This could make compilation fail on GCC, as it forbids directly using inline -// asm inside a constexpr function. However, it allows calling a lambda -// expression including the same asm. -// The side effect is that the top of the stacktrace will not point to the -// calling function, but to this anonymous lambda. This is still useful as the -// full name of the lambda will typically include the name of the function that -// calls CHECK() and the debugger will still break at the right line of code. -#if !defined(__clang__) -#define WRAPPED_TRAP_SEQUENCE() \ - do { \ - [] { TRAP_SEQUENCE(); }(); \ - } while (false) -#else -#define WRAPPED_TRAP_SEQUENCE() TRAP_SEQUENCE() -#endif - -#define IMMEDIATE_CRASH() \ - ({ \ - WRAPPED_TRAP_SEQUENCE(); \ - __builtin_unreachable(); \ - }) - -#elif defined(COMPILER_MSVC) - -// Clang is cleverer about coalescing int3s, so we need to add a unique-ish -// instruction following the __debugbreak() to have it emit distinct locations -// for CHECKs rather than collapsing them all together. It would be nice to use -// a short intrinsic to do this (and perhaps have only one implementation for -// both clang and MSVC), however clang-cl currently does not support intrinsics. -// On the flip side, MSVC x64 doesn't support inline asm. So, we have to have -// two implementations. Normally clang-cl's version will be 5 bytes (1 for -// `int3`, 2 for `ud2`, 2 for `push byte imm`, however, TODO(scottmg): -// https://crbug.com/694670 clang-cl doesn't currently support %'ing -// __COUNTER__, so eventually it will emit the dword form of push. -// TODO(scottmg): Reinvestigate a short sequence that will work on both -// compilers once clang supports more intrinsics. See https://crbug.com/693713. -#if defined(__clang__) -#define IMMEDIATE_CRASH() \ - ({ \ - {__asm int 3 __asm ud2 __asm push __COUNTER__}; \ - __builtin_unreachable(); \ - }) -#else -#define IMMEDIATE_CRASH() __debugbreak() -#endif // __clang__ - -#else -#error Port -#endif - -// CHECK dies with a fatal error if condition is not true. It is *not* -// controlled by NDEBUG, so the check will be executed regardless of -// compilation mode. -// -// We make sure CHECK et al. always evaluates their arguments, as -// doing CHECK(FunctionWithSideEffect()) is a common idiom. - -#if defined(OFFICIAL_BUILD) && defined(NDEBUG) - -// Make all CHECK functions discard their log strings to reduce code bloat, and -// improve performance, for official release builds. -// -// This is not calling BreakDebugger since this is called frequently, and -// calling an out-of-line function instead of a noreturn inline macro prevents -// compiler optimizations. -#define CHECK(condition) \ - UNLIKELY(!(condition)) ? IMMEDIATE_CRASH() : EAT_STREAM_PARAMETERS - -// PCHECK includes the system error code, which is useful for determining -// why the condition failed. In official builds, preserve only the error code -// message so that it is available in crash reports. The stringified -// condition and any additional stream parameters are dropped. -#define PCHECK(condition) \ - LAZY_STREAM(PLOG_STREAM(FATAL), UNLIKELY(!(condition))); \ - EAT_STREAM_PARAMETERS - -#define CHECK_OP(name, op, val1, val2) CHECK((val1) op (val2)) - -#else // !(OFFICIAL_BUILD && NDEBUG) - -#if defined(_PREFAST_) && defined(OS_WIN) -// Use __analysis_assume to tell the VC++ static analysis engine that -// assert conditions are true, to suppress warnings. The LAZY_STREAM -// parameter doesn't reference 'condition' in /analyze builds because -// this evaluation confuses /analyze. The !! before condition is because -// __analysis_assume gets confused on some conditions: -// http://randomascii.wordpress.com/2011/09/13/analyze-for-visual-studio-the-ugly-part-5/ - -#define CHECK(condition) \ - __analysis_assume(!!(condition)), \ - LAZY_STREAM(LOG_STREAM(FATAL), false) \ - << "Check failed: " #condition ". " - -#define PCHECK(condition) \ - __analysis_assume(!!(condition)), \ - LAZY_STREAM(PLOG_STREAM(FATAL), false) \ - << "Check failed: " #condition ". " - -#else // _PREFAST_ - -// Do as much work as possible out of line to reduce inline code size. -#define CHECK(condition) \ - LAZY_STREAM(::logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \ - !ANALYZER_ASSUME_TRUE(condition)) - -#define PCHECK(condition) \ - LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \ - << "Check failed: " #condition ". " - -#endif // _PREFAST_ - -// Helper macro for binary operators. -// Don't use this macro directly in your code, use CHECK_EQ et al below. -// The 'switch' is used to prevent the 'else' from being ambiguous when the -// macro is used in an 'if' clause such as: -// if (a == 1) -// CHECK_EQ(2, a); -#define CHECK_OP(name, op, val1, val2) \ - switch (0) case 0: default: \ - if (::logging::CheckOpResult true_if_passed = \ - ::logging::Check##name##Impl((val1), (val2), \ - #val1 " " #op " " #val2)) \ - ; \ - else \ - ::logging::LogMessage(__FILE__, __LINE__, true_if_passed.message()).stream() - -#endif // !(OFFICIAL_BUILD && NDEBUG) - -// This formats a value for a failing CHECK_XX statement. Ordinarily, -// it uses the definition for operator<<, with a few special cases below. -template -inline typename std::enable_if< - base::internal::SupportsOstreamOperator::value && - !std::is_function::type>::value, - void>::type -MakeCheckOpValueString(std::ostream* os, const T& v) { - (*os) << v; -} - -// Provide an overload for functions and function pointers. Function pointers -// don't implicitly convert to void* but do implicitly convert to bool, so -// without this function pointers are always printed as 1 or 0. (MSVC isn't -// standards-conforming here and converts function pointers to regular -// pointers, so this is a no-op for MSVC.) -template -inline typename std::enable_if< - std::is_function::type>::value, - void>::type -MakeCheckOpValueString(std::ostream* os, const T& v) { - (*os) << reinterpret_cast(v); -} - -// We need overloads for enums that don't support operator<<. -// (i.e. scoped enums where no operator<< overload was declared). -template -inline typename std::enable_if< - !base::internal::SupportsOstreamOperator::value && - std::is_enum::value, - void>::type -MakeCheckOpValueString(std::ostream* os, const T& v) { - (*os) << static_cast::type>(v); -} - -// We need an explicit overload for std::nullptr_t. -BASE_EXPORT void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p); - -// Build the error message string. This is separate from the "Impl" -// function template because it is not performance critical and so can -// be out of line, while the "Impl" code should be inline. Caller -// takes ownership of the returned string. -template -std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { - std::ostringstream ss; - ss << names << " ("; - MakeCheckOpValueString(&ss, v1); - ss << " vs. "; - MakeCheckOpValueString(&ss, v2); - ss << ")"; - std::string* msg = new std::string(ss.str()); - return msg; -} - -// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated -// in logging.cc. -extern template BASE_EXPORT std::string* MakeCheckOpString( - const int&, const int&, const char* names); -extern template BASE_EXPORT -std::string* MakeCheckOpString( - const unsigned long&, const unsigned long&, const char* names); -extern template BASE_EXPORT -std::string* MakeCheckOpString( - const unsigned long&, const unsigned int&, const char* names); -extern template BASE_EXPORT -std::string* MakeCheckOpString( - const unsigned int&, const unsigned long&, const char* names); -extern template BASE_EXPORT -std::string* MakeCheckOpString( - const std::string&, const std::string&, const char* name); - -// Helper functions for CHECK_OP macro. -// The (int, int) specialization works around the issue that the compiler -// will not instantiate the template version of the function on values of -// unnamed enum type - see comment below. -// -// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under -// static analysis builds, blocks analysis of the current path if the -// condition is false. -#define DEFINE_CHECK_OP_IMPL(name, op) \ - template \ - inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ - const char* names) { \ - if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ - return NULL; \ - else \ - return ::logging::MakeCheckOpString(v1, v2, names); \ - } \ - inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ - if (ANALYZER_ASSUME_TRUE(v1 op v2)) \ - return NULL; \ - else \ - return ::logging::MakeCheckOpString(v1, v2, names); \ - } -DEFINE_CHECK_OP_IMPL(EQ, ==) -DEFINE_CHECK_OP_IMPL(NE, !=) -DEFINE_CHECK_OP_IMPL(LE, <=) -DEFINE_CHECK_OP_IMPL(LT, < ) -DEFINE_CHECK_OP_IMPL(GE, >=) -DEFINE_CHECK_OP_IMPL(GT, > ) -#undef DEFINE_CHECK_OP_IMPL - -#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2) -#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2) -#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2) -#define CHECK_LT(val1, val2) CHECK_OP(LT, < , val1, val2) -#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2) -#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2) - -#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) -#define DCHECK_IS_ON() 0 -#else -#define DCHECK_IS_ON() 1 -#endif - -// Definitions for DLOG et al. - -#if DCHECK_IS_ON() - -#define DLOG_IS_ON(severity) LOG_IS_ON(severity) -#define DLOG_IF(severity, condition) LOG_IF(severity, condition) -#define DLOG_ASSERT(condition) LOG_ASSERT(condition) -#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition) -#define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition) -#define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition) - -#else // DCHECK_IS_ON() - -// If !DCHECK_IS_ON(), we want to avoid emitting any references to |condition| -// (which may reference a variable defined only if DCHECK_IS_ON()). -// Contrast this with DCHECK et al., which has different behavior. - -#define DLOG_IS_ON(severity) false -#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS -#define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS -#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS -#define DVLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS -#define DVPLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS - -#endif // DCHECK_IS_ON() - -#define DLOG(severity) \ - LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity)) - -#define DPLOG(severity) \ - LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity)) - -#define DVLOG(verboselevel) DVLOG_IF(verboselevel, VLOG_IS_ON(verboselevel)) - -#define DVPLOG(verboselevel) DVPLOG_IF(verboselevel, VLOG_IS_ON(verboselevel)) - -// Definitions for DCHECK et al. - -#if DCHECK_IS_ON() - -#if DCHECK_IS_CONFIGURABLE -BASE_EXPORT extern LogSeverity LOG_DCHECK; -#else -const LogSeverity LOG_DCHECK = LOG_FATAL; -#endif - -#else // DCHECK_IS_ON() - -// There may be users of LOG_DCHECK that are enabled independently -// of DCHECK_IS_ON(), so default to FATAL logging for those. -const LogSeverity LOG_DCHECK = LOG_FATAL; - -#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. -// -// Note that the definition of the DCHECK macros depends on whether or not -// DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use -// EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries. - -#if defined(_PREFAST_) && defined(OS_WIN) -// See comments on the previous use of __analysis_assume. - -#define DCHECK(condition) \ - __analysis_assume(!!(condition)), \ - LAZY_STREAM(LOG_STREAM(DCHECK), false) \ - << "Check failed: " #condition ". " - -#define DPCHECK(condition) \ - __analysis_assume(!!(condition)), \ - LAZY_STREAM(PLOG_STREAM(DCHECK), false) \ - << "Check failed: " #condition ". " - -#else // !(defined(_PREFAST_) && defined(OS_WIN)) - -#if DCHECK_IS_ON() - -#define DCHECK(condition) \ - LAZY_STREAM(LOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \ - << "Check failed: " #condition ". " -#define DPCHECK(condition) \ - LAZY_STREAM(PLOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \ - << "Check failed: " #condition ". " - -#else // DCHECK_IS_ON() - -#define DCHECK(condition) EAT_STREAM_PARAMETERS << !(condition) -#define DPCHECK(condition) EAT_STREAM_PARAMETERS << !(condition) - -#endif // DCHECK_IS_ON() - -#endif // defined(_PREFAST_) && defined(OS_WIN) - -// Helper macro for binary operators. -// Don't use this macro directly in your code, use DCHECK_EQ et al below. -// The 'switch' is used to prevent the 'else' from being ambiguous when the -// macro is used in an 'if' clause such as: -// if (a == 1) -// DCHECK_EQ(2, a); -#if DCHECK_IS_ON() - -#define DCHECK_OP(name, op, val1, val2) \ - switch (0) case 0: default: \ - if (::logging::CheckOpResult true_if_passed = \ - ::logging::Check##name##Impl((val1), (val2), \ - #val1 " " #op " " #val2)) \ - ; \ - else \ - ::logging::LogMessage(__FILE__, __LINE__, ::logging::LOG_DCHECK, \ - true_if_passed.message()).stream() - -#else // DCHECK_IS_ON() - -// When DCHECKs aren't enabled, DCHECK_OP still needs to reference operator<< -// overloads for |val1| and |val2| to avoid potential compiler warnings about -// unused functions. For the same reason, it also compares |val1| and |val2| -// using |op|. -// -// Note that the contract of DCHECK_EQ, etc is that arguments are only evaluated -// once. Even though |val1| and |val2| appear twice in this version of the macro -// expansion, this is OK, since the expression is never actually evaluated. -#define DCHECK_OP(name, op, val1, val2) \ - EAT_STREAM_PARAMETERS << (::logging::MakeCheckOpValueString( \ - ::logging::g_swallow_stream, val1), \ - ::logging::MakeCheckOpValueString( \ - ::logging::g_swallow_stream, val2), \ - (val1)op(val2)) - -#endif // DCHECK_IS_ON() - -// Equality/Inequality checks - compare two values, and log a -// LOG_DCHECK message including the two values when the result is not -// as expected. The values must have operator<<(ostream, ...) -// defined. -// -// You may append to the error message like so: -// DCHECK_NE(1, 2) << "The world must be ending!"; -// -// We are very careful to ensure that each argument is evaluated exactly -// once, and that anything which is legal to pass as a function argument is -// legal here. In particular, the arguments may be temporary expressions -// which will end up being destroyed at the end of the apparent statement, -// for example: -// DCHECK_EQ(string("abc")[1], 'b'); -// -// WARNING: These don't compile correctly if one of the arguments is a pointer -// and the other is NULL. In new code, prefer nullptr instead. To -// work around this for C++98, simply static_cast NULL to the type of the -// desired pointer. - -#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2) -#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2) -#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2) -#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2) -#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) -#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2) - -#if !DCHECK_IS_ON() && defined(OS_CHROMEOS) -// Implement logging of NOTREACHED() as a dedicated function to get function -// call overhead down to a minimum. -void LogErrorNotReached(const char* file, int line); -#define NOTREACHED() \ - true ? ::logging::LogErrorNotReached(__FILE__, __LINE__) \ - : EAT_STREAM_PARAMETERS -#else -#define NOTREACHED() DCHECK(false) -#endif - -// Redefine the standard assert to use our nice log files -#undef assert -#define assert(x) DLOG_ASSERT(x) - -// This class more or less represents a particular log message. You -// create an instance of LogMessage and then stream stuff to it. -// When you finish streaming to it, ~LogMessage is called and the -// full message gets streamed to the appropriate destination. -// -// You shouldn't actually use LogMessage's constructor to log things, -// though. You should use the LOG() macro (and variants thereof) -// above. -class BASE_EXPORT LogMessage { - public: - // Used for LOG(severity). - LogMessage(const char* file, int line, LogSeverity severity); - - // Used for CHECK(). Implied severity = LOG_FATAL. - LogMessage(const char* file, int line, const char* condition); - - // Used for CHECK_EQ(), etc. Takes ownership of the given string. - // Implied severity = LOG_FATAL. - LogMessage(const char* file, int line, std::string* result); - - // Used for DCHECK_EQ(), etc. Takes ownership of the given string. - LogMessage(const char* file, int line, LogSeverity severity, - std::string* result); - - ~LogMessage(); - - std::ostream& stream() { return stream_; } - - LogSeverity severity() { return severity_; } - std::string str() { return stream_.str(); } - - private: - void Init(const char* file, int line); - - LogSeverity severity_; - std::ostringstream stream_; - size_t message_start_; // Offset of the start of the message (past prefix - // info). - // The file and line information passed in to the constructor. - const char* file_; - const int line_; - -#if defined(OS_WIN) - // Stores the current value of GetLastError in the constructor and restores - // it in the destructor by calling SetLastError. - // This is useful since the LogMessage class uses a lot of Win32 calls - // that will lose the value of GLE and the code that called the log function - // will have lost the thread error value when the log call returns. - class SaveLastError { - public: - SaveLastError(); - ~SaveLastError(); - - unsigned long get_error() const { return last_error_; } - - protected: - unsigned long last_error_; - }; - - SaveLastError last_error_; -#endif - - DISALLOW_COPY_AND_ASSIGN(LogMessage); -}; - -// This class is used to explicitly ignore values in the conditional -// logging macros. This avoids compiler warnings like "value computed -// is not used" and "statement has no effect". -class LogMessageVoidify { - public: - LogMessageVoidify() = default; - // This has to be an operator with a precedence lower than << but - // higher than ?: - void operator&(std::ostream&) { } -}; - -#if defined(OS_WIN) -typedef unsigned long SystemErrorCode; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -typedef int SystemErrorCode; -#endif - -// 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. -class BASE_EXPORT Win32ErrorLogMessage { - public: - Win32ErrorLogMessage(const char* file, - int line, - LogSeverity severity, - SystemErrorCode err); - - // Appends the error message before destructing the encapsulated class. - ~Win32ErrorLogMessage(); - - std::ostream& stream() { return log_message_.stream(); } - - private: - SystemErrorCode err_; - LogMessage log_message_; - - DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage); -}; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -// Appends a formatted system message of the errno type -class BASE_EXPORT ErrnoLogMessage { - public: - ErrnoLogMessage(const char* file, - int line, - LogSeverity severity, - SystemErrorCode err); - - // Appends the error message before destructing the encapsulated class. - ~ErrnoLogMessage(); - - std::ostream& stream() { return log_message_.stream(); } - - private: - SystemErrorCode err_; - LogMessage log_message_; - - DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage); -}; -#endif // OS_WIN - -// Closes the log file explicitly if open. -// NOTE: Since the log file is opened as necessary by the action of logging -// statements, there's no guarantee that it will stay closed -// after this call. -BASE_EXPORT void CloseLogFile(); - -// Async signal safe logging mechanism. -BASE_EXPORT void RawLog(int level, const char* message); - -#define RAW_LOG(level, message) \ - ::logging::RawLog(::logging::LOG_##level, message) - -#define RAW_CHECK(condition) \ - do { \ - if (!(condition)) \ - ::logging::RawLog(::logging::LOG_FATAL, \ - "Check failed: " #condition "\n"); \ - } while (0) - -#if defined(OS_WIN) -// Returns true if logging to file is enabled. -BASE_EXPORT bool IsLoggingToFileEnabled(); - -// Returns the default log file path. -BASE_EXPORT std::wstring GetLogFileFullPath(); -#endif - -} // namespace logging - -// Note that "The behavior of a C++ program is undefined if it adds declarations -// or definitions to namespace std or to a namespace within namespace std unless -// otherwise specified." --C++11[namespace.std] -// -// We've checked that this particular definition has the intended behavior on -// our implementations, but it's prone to breaking in the future, and please -// don't imitate this in your own definitions without checking with some -// standard library experts. -namespace std { -// These functions are provided as a convenience for logging, which is where we -// use streams (it is against Google style to use streams in other places). It -// is designed to allow you to emit non-ASCII Unicode strings to the log file, -// which is normally ASCII. It is relatively slow, so try not to use it for -// common cases. Non-ASCII characters will be converted to UTF-8 by these -// operators. -BASE_EXPORT std::ostream& operator<<(std::ostream& out, const wchar_t* wstr); -inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) { - return out << wstr.c_str(); -} -} // namespace std - -// The NOTIMPLEMENTED() macro annotates codepaths which have not been -// implemented yet. If output spam is a serious concern, -// NOTIMPLEMENTED_LOG_ONCE can be used. - -#if defined(COMPILER_GCC) -// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name -// of the current function in the NOTIMPLEMENTED message. -#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__ -#else -#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED" -#endif - -#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD) -#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS -#define NOTIMPLEMENTED_LOG_ONCE() EAT_STREAM_PARAMETERS -#else -#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG -#define NOTIMPLEMENTED_LOG_ONCE() \ - do { \ - static bool logged_once = false; \ - LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \ - logged_once = true; \ - } while (0); \ - EAT_STREAM_PARAMETERS -#endif - -#endif // BASE_LOGGING_H_ diff --git a/logging_unittest.cc b/logging_unittest.cc deleted file mode 100644 index 9d37b4c69..000000000 --- a/logging_unittest.cc +++ /dev/null @@ -1,804 +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/logging.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" -#include "base/test/scoped_feature_list.h" -#include "build/build_config.h" - -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_POSIX) -#include -#include -#include "base/posix/eintr_wrapper.h" -#endif // OS_POSIX - -#if defined(OS_LINUX) || defined(OS_ANDROID) -#include -#endif - -#if defined(OS_WIN) -#include -#include -#endif // OS_WIN - -#if defined(OS_FUCHSIA) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "base/fuchsia/fuchsia_logging.h" -#endif - -namespace logging { - -namespace { - -using ::testing::Return; -using ::testing::_; - -// Needs to be global since log assert handlers can't maintain state. -int g_log_sink_call_count = 0; - -#if !defined(OFFICIAL_BUILD) || defined(DCHECK_ALWAYS_ON) || !defined(NDEBUG) -void LogSink(const char* file, - int line, - const base::StringPiece message, - const base::StringPiece stack_trace) { - ++g_log_sink_call_count; -} -#endif - -// Class to make sure any manipulations we do to the min log level are -// contained (i.e., do not affect other unit tests). -class LogStateSaver { - public: - LogStateSaver() : old_min_log_level_(GetMinLogLevel()) {} - - ~LogStateSaver() { - SetMinLogLevel(old_min_log_level_); - g_log_sink_call_count = 0; - } - - private: - int old_min_log_level_; - - DISALLOW_COPY_AND_ASSIGN(LogStateSaver); -}; - -class LoggingTest : public testing::Test { - private: - LogStateSaver log_state_saver_; -}; - -class MockLogSource { - public: - MOCK_METHOD0(Log, const char*()); -}; - -class MockLogAssertHandler { - public: - MOCK_METHOD4( - HandleLogAssert, - void(const char*, int, const base::StringPiece, const base::StringPiece)); -}; - -TEST_F(LoggingTest, BasicLogging) { - MockLogSource mock_log_source; - EXPECT_CALL(mock_log_source, Log()) - .Times(DCHECK_IS_ON() ? 16 : 8) - .WillRepeatedly(Return("log message")); - - SetMinLogLevel(LOG_INFO); - - EXPECT_TRUE(LOG_IS_ON(INFO)); - EXPECT_TRUE((DCHECK_IS_ON() != 0) == DLOG_IS_ON(INFO)); - EXPECT_TRUE(VLOG_IS_ON(0)); - - LOG(INFO) << mock_log_source.Log(); - LOG_IF(INFO, true) << mock_log_source.Log(); - PLOG(INFO) << mock_log_source.Log(); - 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(); - DPLOG(INFO) << mock_log_source.Log(); - 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) { -#if defined(NDEBUG) - const bool kDfatalIsFatal = false; -#else // defined(NDEBUG) - const bool kDfatalIsFatal = true; -#endif // defined(NDEBUG) - - SetMinLogLevel(LOG_INFO); - EXPECT_TRUE(LOG_IS_ON(INFO)); - EXPECT_TRUE(LOG_IS_ON(WARNING)); - EXPECT_TRUE(LOG_IS_ON(ERROR)); - EXPECT_TRUE(LOG_IS_ON(FATAL)); - EXPECT_TRUE(LOG_IS_ON(DFATAL)); - - SetMinLogLevel(LOG_WARNING); - EXPECT_FALSE(LOG_IS_ON(INFO)); - EXPECT_TRUE(LOG_IS_ON(WARNING)); - EXPECT_TRUE(LOG_IS_ON(ERROR)); - EXPECT_TRUE(LOG_IS_ON(FATAL)); - EXPECT_TRUE(LOG_IS_ON(DFATAL)); - - SetMinLogLevel(LOG_ERROR); - EXPECT_FALSE(LOG_IS_ON(INFO)); - EXPECT_FALSE(LOG_IS_ON(WARNING)); - EXPECT_TRUE(LOG_IS_ON(ERROR)); - EXPECT_TRUE(LOG_IS_ON(FATAL)); - EXPECT_TRUE(LOG_IS_ON(DFATAL)); - - // 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(FATAL)); - EXPECT_EQ(kDfatalIsFatal, LOG_IS_ON(DFATAL)); -} - -TEST_F(LoggingTest, LoggingIsLazyBySeverity) { - MockLogSource mock_log_source; - EXPECT_CALL(mock_log_source, Log()).Times(0); - - SetMinLogLevel(LOG_WARNING); - - EXPECT_FALSE(LOG_IS_ON(INFO)); - EXPECT_FALSE(DLOG_IS_ON(INFO)); - EXPECT_FALSE(VLOG_IS_ON(1)); - - LOG(INFO) << mock_log_source.Log(); - LOG_IF(INFO, false) << mock_log_source.Log(); - PLOG(INFO) << mock_log_source.Log(); - 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(); - DPLOG(INFO) << mock_log_source.Log(); - 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(); -} - -TEST_F(LoggingTest, LoggingIsLazyByDestination) { - MockLogSource mock_log_source; - MockLogSource mock_log_source_error; - EXPECT_CALL(mock_log_source, Log()).Times(0); - - // Severity >= ERROR is always printed to stderr. - EXPECT_CALL(mock_log_source_error, Log()).Times(1). - WillRepeatedly(Return("log message")); - - LoggingSettings settings; - settings.logging_dest = LOG_NONE; - InitLogging(settings); - - LOG(INFO) << mock_log_source.Log(); - LOG(WARNING) << mock_log_source.Log(); - LOG(ERROR) << mock_log_source_error.Log(); -} - -// Official builds have CHECKs directly call BreakDebugger. -#if !defined(OFFICIAL_BUILD) - -// https://crbug.com/709067 tracks test flakiness on iOS. -#if defined(OS_IOS) -#define MAYBE_CheckStreamsAreLazy DISABLED_CheckStreamsAreLazy -#else -#define MAYBE_CheckStreamsAreLazy CheckStreamsAreLazy -#endif -TEST_F(LoggingTest, MAYBE_CheckStreamsAreLazy) { - MockLogSource mock_log_source, uncalled_mock_log_source; - EXPECT_CALL(mock_log_source, Log()).Times(8). - WillRepeatedly(Return("check message")); - EXPECT_CALL(uncalled_mock_log_source, Log()).Times(0); - - ScopedLogAssertHandler scoped_assert_handler(base::Bind(LogSink)); - - CHECK(mock_log_source.Log()) << uncalled_mock_log_source.Log(); - PCHECK(!mock_log_source.Log()) << mock_log_source.Log(); - CHECK_EQ(mock_log_source.Log(), mock_log_source.Log()) - << uncalled_mock_log_source.Log(); - CHECK_NE(mock_log_source.Log(), mock_log_source.Log()) - << mock_log_source.Log(); -} - -#endif - -#if defined(OFFICIAL_BUILD) && defined(OS_WIN) -NOINLINE void CheckContainingFunc(int death_location) { - CHECK(death_location != 1); - CHECK(death_location != 2); - CHECK(death_location != 3); -} - -int GetCheckExceptionData(EXCEPTION_POINTERS* p, DWORD* code, void** addr) { - *code = p->ExceptionRecord->ExceptionCode; - *addr = p->ExceptionRecord->ExceptionAddress; - return EXCEPTION_EXECUTE_HANDLER; -} - -TEST_F(LoggingTest, CheckCausesDistinctBreakpoints) { - DWORD code1 = 0; - DWORD code2 = 0; - DWORD code3 = 0; - void* addr1 = nullptr; - void* addr2 = nullptr; - void* addr3 = nullptr; - - // Record the exception code and addresses. - __try { - CheckContainingFunc(1); - } __except ( - GetCheckExceptionData(GetExceptionInformation(), &code1, &addr1)) { - } - - __try { - CheckContainingFunc(2); - } __except ( - GetCheckExceptionData(GetExceptionInformation(), &code2, &addr2)) { - } - - __try { - CheckContainingFunc(3); - } __except ( - GetCheckExceptionData(GetExceptionInformation(), &code3, &addr3)) { - } - - // Ensure that the exception codes are correct (in particular, breakpoints, - // not access violations). - EXPECT_EQ(STATUS_BREAKPOINT, code1); - EXPECT_EQ(STATUS_BREAKPOINT, code2); - EXPECT_EQ(STATUS_BREAKPOINT, code3); - - // Ensure that none of the CHECKs are colocated. - EXPECT_NE(addr1, addr2); - EXPECT_NE(addr1, addr3); - EXPECT_NE(addr2, addr3); -} -#elif defined(OS_FUCHSIA) - -// CHECK causes a direct crash (without jumping to another function) only in -// official builds. Unfortunately, continuous test coverage on official builds -// is lower. Furthermore, since the Fuchsia implementation uses threads, it is -// not possible to rely on an implementation of CHECK that calls abort(), which -// takes down the whole process, preventing the thread exception handler from -// handling the exception. DO_CHECK here falls back on IMMEDIATE_CRASH() in -// non-official builds, to catch regressions earlier in the CQ. -#if defined(OFFICIAL_BUILD) -#define DO_CHECK CHECK -#else -#define DO_CHECK(cond) \ - if (!(cond)) { \ - IMMEDIATE_CRASH(); \ - } -#endif - -static const unsigned int kExceptionPortKey = 1u; -static const unsigned int kThreadEndedPortKey = 2u; - -struct thread_data_t { - // For signaling the thread ended properly. - zx::unowned_event event; - // For registering thread termination. - zx::unowned_port port; - // Location where the thread is expected to crash. - int death_location; -}; - -void* CrashThread(void* arg) { - zx_status_t status; - - thread_data_t* data = (thread_data_t*)arg; - int death_location = data->death_location; - - // Register the exception handler on the port. - status = - zx::thread::self().bind_exception_port(*data->port, kExceptionPortKey, 0); - if (status != ZX_OK) { - data->event->signal(0, ZX_USER_SIGNAL_0); - return nullptr; - } - - DO_CHECK(death_location != 1); - DO_CHECK(death_location != 2); - DO_CHECK(death_location != 3); - - // We should never reach this point, signal the thread incorrectly ended - // properly. - data->event->signal(0, ZX_USER_SIGNAL_0); - return nullptr; -} - -// Runs the CrashThread function in a separate thread. -void SpawnCrashThread(int death_location, uintptr_t* child_crash_addr) { - zx::port port; - zx::event event; - zx_status_t status; - - status = zx::port::create(0, &port); - ASSERT_EQ(status, ZX_OK); - status = zx::event::create(0, &event); - ASSERT_EQ(status, ZX_OK); - - // Register the thread ended event on the port. - status = event.wait_async(port, kThreadEndedPortKey, ZX_USER_SIGNAL_0, - ZX_WAIT_ASYNC_ONCE); - ASSERT_EQ(status, ZX_OK); - - // Run the thread. - thread_data_t thread_data = {zx::unowned_event(event), zx::unowned_port(port), - death_location}; - pthread_t thread; - int ret = pthread_create(&thread, nullptr, CrashThread, &thread_data); - ASSERT_EQ(ret, 0); - - // Wait on the port. - zx_port_packet_t packet; - status = port.wait(zx::time::infinite(), &packet); - ASSERT_EQ(status, ZX_OK); - // Check the thread did crash and not terminate. - ASSERT_EQ(packet.key, kExceptionPortKey); - - // Get the crash address. - zx::thread zircon_thread; - status = zx::process::self().get_child(packet.exception.tid, - ZX_RIGHT_SAME_RIGHTS, &zircon_thread); - ASSERT_EQ(status, ZX_OK); - zx_thread_state_general_regs_t buffer; - status = zircon_thread.read_state(ZX_THREAD_STATE_GENERAL_REGS, &buffer, - sizeof(buffer)); - ASSERT_EQ(status, ZX_OK); -#if defined(ARCH_CPU_X86_64) - *child_crash_addr = static_cast(buffer.rip); -#elif defined(ARCH_CPU_ARM64) - *child_crash_addr = static_cast(buffer.pc); -#else -#error Unsupported architecture -#endif - - status = zircon_thread.kill(); - ASSERT_EQ(status, ZX_OK); -} - -TEST_F(LoggingTest, CheckCausesDistinctBreakpoints) { - uintptr_t child_crash_addr_1 = 0; - uintptr_t child_crash_addr_2 = 0; - uintptr_t child_crash_addr_3 = 0; - - SpawnCrashThread(1, &child_crash_addr_1); - SpawnCrashThread(2, &child_crash_addr_2); - SpawnCrashThread(3, &child_crash_addr_3); - - ASSERT_NE(0u, child_crash_addr_1); - ASSERT_NE(0u, child_crash_addr_2); - ASSERT_NE(0u, child_crash_addr_3); - ASSERT_NE(child_crash_addr_1, child_crash_addr_2); - ASSERT_NE(child_crash_addr_1, child_crash_addr_3); - ASSERT_NE(child_crash_addr_2, child_crash_addr_3); -} -#elif defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_IOS) && \ - (defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY)) - -int g_child_crash_pipe; - -void CheckCrashTestSighandler(int, siginfo_t* info, void* context_ptr) { - // Conversely to what clearly stated in "man 2 sigaction", some Linux kernels - // do NOT populate the |info->si_addr| in the case of a SIGTRAP. Hence we - // need the arch-specific boilerplate below, which is inspired by breakpad. - // At the same time, on OSX, ucontext.h is deprecated but si_addr works fine. - uintptr_t crash_addr = 0; -#if defined(OS_MACOSX) - crash_addr = reinterpret_cast(info->si_addr); -#else // OS_POSIX && !OS_MACOSX - ucontext_t* context = reinterpret_cast(context_ptr); -#if defined(ARCH_CPU_X86) - crash_addr = static_cast(context->uc_mcontext.gregs[REG_EIP]); -#elif defined(ARCH_CPU_X86_64) - crash_addr = static_cast(context->uc_mcontext.gregs[REG_RIP]); -#elif defined(ARCH_CPU_ARMEL) - crash_addr = static_cast(context->uc_mcontext.arm_pc); -#elif defined(ARCH_CPU_ARM64) - crash_addr = static_cast(context->uc_mcontext.pc); -#endif // ARCH_* -#endif // OS_POSIX && !OS_MACOSX - HANDLE_EINTR(write(g_child_crash_pipe, &crash_addr, sizeof(uintptr_t))); - _exit(0); -} - -// CHECK causes a direct crash (without jumping to another function) only in -// official builds. Unfortunately, continuous test coverage on official builds -// is lower. DO_CHECK here falls back on a home-brewed implementation in -// non-official builds, to catch regressions earlier in the CQ. -#if defined(OFFICIAL_BUILD) -#define DO_CHECK CHECK -#else -#define DO_CHECK(cond) \ - if (!(cond)) \ - IMMEDIATE_CRASH() -#endif - -void CrashChildMain(int death_location) { - struct sigaction act = {}; - act.sa_sigaction = CheckCrashTestSighandler; - act.sa_flags = SA_SIGINFO; - ASSERT_EQ(0, sigaction(SIGTRAP, &act, nullptr)); - ASSERT_EQ(0, sigaction(SIGBUS, &act, nullptr)); - ASSERT_EQ(0, sigaction(SIGILL, &act, nullptr)); - DO_CHECK(death_location != 1); - DO_CHECK(death_location != 2); - printf("\n"); - DO_CHECK(death_location != 3); - - // Should never reach this point. - const uintptr_t failed = 0; - HANDLE_EINTR(write(g_child_crash_pipe, &failed, sizeof(uintptr_t))); -}; - -void SpawnChildAndCrash(int death_location, uintptr_t* child_crash_addr) { - int pipefd[2]; - ASSERT_EQ(0, pipe(pipefd)); - - int pid = fork(); - ASSERT_GE(pid, 0); - - if (pid == 0) { // child process. - close(pipefd[0]); // Close reader (parent) end. - g_child_crash_pipe = pipefd[1]; - CrashChildMain(death_location); - FAIL() << "The child process was supposed to crash. It didn't."; - } - - close(pipefd[1]); // Close writer (child) end. - DCHECK(child_crash_addr); - int res = HANDLE_EINTR(read(pipefd[0], child_crash_addr, sizeof(uintptr_t))); - ASSERT_EQ(static_cast(sizeof(uintptr_t)), res); -} - -TEST_F(LoggingTest, CheckCausesDistinctBreakpoints) { - uintptr_t child_crash_addr_1 = 0; - uintptr_t child_crash_addr_2 = 0; - uintptr_t child_crash_addr_3 = 0; - - SpawnChildAndCrash(1, &child_crash_addr_1); - SpawnChildAndCrash(2, &child_crash_addr_2); - SpawnChildAndCrash(3, &child_crash_addr_3); - - ASSERT_NE(0u, child_crash_addr_1); - ASSERT_NE(0u, child_crash_addr_2); - ASSERT_NE(0u, child_crash_addr_3); - ASSERT_NE(child_crash_addr_1, child_crash_addr_2); - ASSERT_NE(child_crash_addr_1, child_crash_addr_3); - ASSERT_NE(child_crash_addr_2, child_crash_addr_3); -} -#endif // OS_POSIX - -TEST_F(LoggingTest, DebugLoggingReleaseBehavior) { -#if DCHECK_IS_ON() - int debug_only_variable = 1; -#endif - // These should avoid emitting references to |debug_only_variable| - // in release mode. - DLOG_IF(INFO, debug_only_variable) << "test"; - DLOG_ASSERT(debug_only_variable) << "test"; - DPLOG_IF(INFO, debug_only_variable) << "test"; - DVLOG_IF(1, debug_only_variable) << "test"; -} - -TEST_F(LoggingTest, DcheckStreamsAreLazy) { - MockLogSource mock_log_source; - EXPECT_CALL(mock_log_source, Log()).Times(0); -#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(); - DCHECK_EQ(mock_log_source.Log(), static_cast(nullptr)) - << mock_log_source.Log(); -#endif -} - -void DcheckEmptyFunction1() { - // Provide a body so that Release builds do not cause the compiler to - // optimize DcheckEmptyFunction1 and DcheckEmptyFunction2 as a single - // function, which breaks the Dcheck tests below. - LOG(INFO) << "DcheckEmptyFunction1"; -} -void DcheckEmptyFunction2() {} - -#if DCHECK_IS_CONFIGURABLE -class ScopedDcheckSeverity { - public: - ScopedDcheckSeverity(LogSeverity new_severity) : old_severity_(LOG_DCHECK) { - LOG_DCHECK = new_severity; - } - - ~ScopedDcheckSeverity() { LOG_DCHECK = old_severity_; } - - private: - LogSeverity old_severity_; -}; -#endif // DCHECK_IS_CONFIGURABLE - -// https://crbug.com/709067 tracks test flakiness on iOS. -#if defined(OS_IOS) -#define MAYBE_Dcheck DISABLED_Dcheck -#else -#define MAYBE_Dcheck Dcheck -#endif -TEST_F(LoggingTest, MAYBE_Dcheck) { -#if DCHECK_IS_CONFIGURABLE - // DCHECKs are enabled, and LOG_DCHECK is mutable, but defaults to non-fatal. - // Set it to LOG_FATAL to get the expected behavior from the rest of this - // test. - ScopedDcheckSeverity dcheck_severity(LOG_FATAL); -#endif // DCHECK_IS_CONFIGURABLE - -#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) - // Release build with real DCHECKS. - ScopedLogAssertHandler scoped_assert_handler(base::Bind(LogSink)); - EXPECT_TRUE(DCHECK_IS_ON()); - EXPECT_TRUE(DLOG_IS_ON(DCHECK)); -#else - // Debug build. - ScopedLogAssertHandler scoped_assert_handler(base::Bind(LogSink)); - EXPECT_TRUE(DCHECK_IS_ON()); - EXPECT_TRUE(DLOG_IS_ON(DCHECK)); -#endif - - // DCHECKs are fatal iff they're compiled in DCHECK_IS_ON() and the DCHECK - // log level is set to fatal. - const bool dchecks_are_fatal = DCHECK_IS_ON() && LOG_DCHECK == LOG_FATAL; - EXPECT_EQ(0, g_log_sink_call_count); - DCHECK(false); - EXPECT_EQ(dchecks_are_fatal ? 1 : 0, g_log_sink_call_count); - DPCHECK(false); - EXPECT_EQ(dchecks_are_fatal ? 2 : 0, g_log_sink_call_count); - DCHECK_EQ(0, 1); - EXPECT_EQ(dchecks_are_fatal ? 3 : 0, g_log_sink_call_count); - - // Test DCHECK on std::nullptr_t - g_log_sink_call_count = 0; - const void* p_null = nullptr; - const void* p_not_null = &p_null; - DCHECK_EQ(p_null, nullptr); - DCHECK_EQ(nullptr, p_null); - DCHECK_NE(p_not_null, nullptr); - DCHECK_NE(nullptr, p_not_null); - EXPECT_EQ(0, g_log_sink_call_count); - - // Test DCHECK on a scoped enum. - enum class Animal { DOG, CAT }; - DCHECK_EQ(Animal::DOG, Animal::DOG); - EXPECT_EQ(0, g_log_sink_call_count); - DCHECK_EQ(Animal::DOG, Animal::CAT); - EXPECT_EQ(dchecks_are_fatal ? 1 : 0, g_log_sink_call_count); - - // Test DCHECK on functions and function pointers. - g_log_sink_call_count = 0; - struct MemberFunctions { - void MemberFunction1() { - // See the comment in DcheckEmptyFunction1(). - LOG(INFO) << "Do not merge with MemberFunction2."; - } - void MemberFunction2() {} - }; - void (MemberFunctions::*mp1)() = &MemberFunctions::MemberFunction1; - void (MemberFunctions::*mp2)() = &MemberFunctions::MemberFunction2; - void (*fp1)() = DcheckEmptyFunction1; - void (*fp2)() = DcheckEmptyFunction2; - void (*fp3)() = DcheckEmptyFunction1; - DCHECK_EQ(fp1, fp3); - EXPECT_EQ(0, g_log_sink_call_count); - DCHECK_EQ(mp1, &MemberFunctions::MemberFunction1); - EXPECT_EQ(0, g_log_sink_call_count); - DCHECK_EQ(mp2, &MemberFunctions::MemberFunction2); - EXPECT_EQ(0, g_log_sink_call_count); - DCHECK_EQ(fp1, fp2); - EXPECT_EQ(dchecks_are_fatal ? 1 : 0, g_log_sink_call_count); - DCHECK_EQ(mp2, &MemberFunctions::MemberFunction1); - EXPECT_EQ(dchecks_are_fatal ? 2 : 0, g_log_sink_call_count); -} - -TEST_F(LoggingTest, DcheckReleaseBehavior) { - int some_variable = 1; - // These should still reference |some_variable| so we don't get - // unused variable warnings. - DCHECK(some_variable) << "test"; - DPCHECK(some_variable) << "test"; - DCHECK_EQ(some_variable, 1) << "test"; -} - -TEST_F(LoggingTest, DCheckEqStatements) { - bool reached = false; - if (false) - DCHECK_EQ(false, true); // Unreached. - else - DCHECK_EQ(true, reached = true); // Reached, passed. - ASSERT_EQ(DCHECK_IS_ON() ? true : false, reached); - - if (false) - DCHECK_EQ(false, true); // Unreached. -} - -TEST_F(LoggingTest, CheckEqStatements) { - bool reached = false; - if (false) - CHECK_EQ(false, true); // Unreached. - else - CHECK_EQ(true, reached = true); // Reached, passed. - ASSERT_TRUE(reached); - - if (false) - CHECK_EQ(false, true); // Unreached. -} - -TEST_F(LoggingTest, NestedLogAssertHandlers) { - ::testing::InSequence dummy; - ::testing::StrictMock handler_a, handler_b; - - EXPECT_CALL( - handler_a, - HandleLogAssert( - _, _, base::StringPiece("First assert must be caught by handler_a"), - _)); - EXPECT_CALL( - handler_b, - HandleLogAssert( - _, _, base::StringPiece("Second assert must be caught by handler_b"), - _)); - EXPECT_CALL( - handler_a, - HandleLogAssert( - _, _, - base::StringPiece("Last assert must be caught by handler_a again"), - _)); - - logging::ScopedLogAssertHandler scoped_handler_a(base::Bind( - &MockLogAssertHandler::HandleLogAssert, base::Unretained(&handler_a))); - - // Using LOG(FATAL) rather than CHECK(false) here since log messages aren't - // preserved for CHECKs in official builds. - LOG(FATAL) << "First assert must be caught by handler_a"; - - { - logging::ScopedLogAssertHandler scoped_handler_b(base::Bind( - &MockLogAssertHandler::HandleLogAssert, base::Unretained(&handler_b))); - LOG(FATAL) << "Second assert must be caught by handler_b"; - } - - LOG(FATAL) << "Last assert must be caught by handler_a again"; -} - -// Test that defining an operator<< for a type in a namespace doesn't prevent -// other code in that namespace from calling the operator<<(ostream, wstring) -// defined by logging.h. This can fail if operator<<(ostream, wstring) can't be -// found by ADL, since defining another operator<< prevents name lookup from -// looking in the global namespace. -namespace nested_test { - class Streamable {}; - ALLOW_UNUSED_TYPE std::ostream& operator<<(std::ostream& out, - const Streamable&) { - return out << "Streamable"; - } - TEST_F(LoggingTest, StreamingWstringFindsCorrectOperator) { - std::wstring wstr = L"Hello World"; - std::ostringstream ostr; - ostr << wstr; - EXPECT_EQ("Hello World", ostr.str()); - } -} // namespace nested_test - -#if DCHECK_IS_CONFIGURABLE -TEST_F(LoggingTest, ConfigurableDCheck) { - // Verify that DCHECKs default to non-fatal in configurable-DCHECK builds. - // Note that we require only that DCHECK is non-fatal by default, rather - // than requiring that it be exactly INFO, ERROR, etc level. - EXPECT_LT(LOG_DCHECK, LOG_FATAL); - DCHECK(false); - - // Verify that DCHECK* aren't hard-wired to crash on failure. - LOG_DCHECK = LOG_INFO; - DCHECK(false); - DCHECK_EQ(1, 2); - - // Verify that DCHECK does crash if LOG_DCHECK is set to LOG_FATAL. - LOG_DCHECK = LOG_FATAL; - - ::testing::StrictMock handler; - EXPECT_CALL(handler, HandleLogAssert(_, _, _, _)).Times(2); - { - logging::ScopedLogAssertHandler scoped_handler_b(base::Bind( - &MockLogAssertHandler::HandleLogAssert, base::Unretained(&handler))); - DCHECK(false); - DCHECK_EQ(1, 2); - } -} - -TEST_F(LoggingTest, ConfigurableDCheckFeature) { - // Initialize FeatureList with and without DcheckIsFatal, and verify the - // value of LOG_DCHECK. Note that we don't require that DCHECK take a - // specific value when the feature is off, only that it is non-fatal. - - { - base::test::ScopedFeatureList feature_list; - feature_list.InitFromCommandLine("DcheckIsFatal", ""); - EXPECT_EQ(LOG_DCHECK, LOG_FATAL); - } - - { - base::test::ScopedFeatureList feature_list; - feature_list.InitFromCommandLine("", "DcheckIsFatal"); - EXPECT_LT(LOG_DCHECK, LOG_FATAL); - } - - // The default case is last, so we leave LOG_DCHECK in the default state. - { - base::test::ScopedFeatureList feature_list; - feature_list.InitFromCommandLine("", ""); - EXPECT_LT(LOG_DCHECK, LOG_FATAL); - } -} -#endif // DCHECK_IS_CONFIGURABLE - -#if defined(OS_FUCHSIA) -TEST_F(LoggingTest, FuchsiaLogging) { - MockLogSource mock_log_source; - EXPECT_CALL(mock_log_source, Log()) - .Times(DCHECK_IS_ON() ? 2 : 1) - .WillRepeatedly(Return("log message")); - - SetMinLogLevel(LOG_INFO); - - EXPECT_TRUE(LOG_IS_ON(INFO)); - EXPECT_TRUE((DCHECK_IS_ON() != 0) == DLOG_IS_ON(INFO)); - - ZX_LOG(INFO, ZX_ERR_INTERNAL) << mock_log_source.Log(); - ZX_DLOG(INFO, ZX_ERR_INTERNAL) << mock_log_source.Log(); - - ZX_CHECK(true, ZX_ERR_INTERNAL); - ZX_DCHECK(true, ZX_ERR_INTERNAL); -} -#endif // defined(OS_FUCHSIA) - -} // namespace - -} // namespace logging diff --git a/logging_win.cc b/logging_win.cc deleted file mode 100644 index 319ae8a9d..000000000 --- a/logging_win.cc +++ /dev/null @@ -1,138 +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/logging_win.h" -#include "base/memory/singleton.h" -#include // NOLINT - -namespace logging { - -using base::win::EtwEventLevel; -using base::win::EtwMofEvent; - -DEFINE_GUID(kLogEventId, - 0x7fe69228, 0x633e, 0x4f06, 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7); - -LogEventProvider::LogEventProvider() : old_log_level_(LOG_NONE) { -} - -LogEventProvider* LogEventProvider::GetInstance() { - return base::Singleton>::get(); -} - -bool LogEventProvider::LogMessage(logging::LogSeverity severity, - const char* file, int line, size_t message_start, - const std::string& message) { - EtwEventLevel level = TRACE_LEVEL_NONE; - - // Convert the log severity to the most appropriate ETW trace level. - if (severity >= 0) { - switch (severity) { - case LOG_INFO: - level = TRACE_LEVEL_INFORMATION; - break; - case LOG_WARNING: - level = TRACE_LEVEL_WARNING; - break; - case LOG_ERROR: - level = TRACE_LEVEL_ERROR; - break; - case LOG_FATAL: - level = TRACE_LEVEL_FATAL; - break; - } - } else { // severity < 0 is VLOG verbosity levels. - level = static_cast(TRACE_LEVEL_INFORMATION - severity); - } - - // Bail if we're not logging, not at that level, - // or if we're post-atexit handling. - LogEventProvider* provider = LogEventProvider::GetInstance(); - if (provider == NULL || level > provider->enable_level()) - return false; - - // And now log the event. - if (provider->enable_flags() & ENABLE_LOG_MESSAGE_ONLY) { - EtwMofEvent<1> event(kLogEventId, LOG_MESSAGE, level); - event.SetField(0, message.length() + 1 - message_start, - message.c_str() + message_start); - - provider->Log(event.get()); - } else { - const size_t kMaxBacktraceDepth = 32; - void* backtrace[kMaxBacktraceDepth]; - DWORD depth = 0; - - // Capture a stack trace if one is requested. - // requested per our enable flags. - if (provider->enable_flags() & ENABLE_STACK_TRACE_CAPTURE) - depth = CaptureStackBackTrace(2, kMaxBacktraceDepth, backtrace, NULL); - - EtwMofEvent<5> event(kLogEventId, LOG_MESSAGE_FULL, level); - if (file == NULL) - file = ""; - - // Add the stack trace. - event.SetField(0, sizeof(depth), &depth); - event.SetField(1, sizeof(backtrace[0]) * depth, &backtrace); - // The line. - event.SetField(2, sizeof(line), &line); - // The file. - event.SetField(3, strlen(file) + 1, file); - // And finally the message. - event.SetField(4, message.length() + 1 - message_start, - message.c_str() + message_start); - - provider->Log(event.get()); - } - - // Don't increase verbosity in other log destinations. - if (severity < provider->old_log_level_) - return true; - - return false; -} - -void LogEventProvider::Initialize(const GUID& provider_name) { - LogEventProvider* provider = LogEventProvider::GetInstance(); - - provider->set_provider_name(provider_name); - provider->Register(); - - // Register our message handler with logging. - SetLogMessageHandler(LogMessage); -} - -void LogEventProvider::Uninitialize() { - LogEventProvider::GetInstance()->Unregister(); -} - -void LogEventProvider::OnEventsEnabled() { - // Grab the old log level so we can restore it later. - old_log_level_ = GetMinLogLevel(); - - // Convert the new trace level to a logging severity - // and enable logging at that level. - EtwEventLevel level = enable_level(); - if (level == TRACE_LEVEL_NONE || level == TRACE_LEVEL_FATAL) { - SetMinLogLevel(LOG_FATAL); - } else if (level == TRACE_LEVEL_ERROR) { - SetMinLogLevel(LOG_ERROR); - } else if (level == TRACE_LEVEL_WARNING) { - SetMinLogLevel(LOG_WARNING); - } else if (level == TRACE_LEVEL_INFORMATION) { - SetMinLogLevel(LOG_INFO); - } else if (level >= TRACE_LEVEL_VERBOSE) { - // Above INFO, we enable verbose levels with negative severities. - SetMinLogLevel(TRACE_LEVEL_INFORMATION - level); - } -} - -void LogEventProvider::OnEventsDisabled() { - // Restore the old log level. - SetMinLogLevel(old_log_level_); -} - -} // namespace logging diff --git a/logging_win.h b/logging_win.h deleted file mode 100644 index cdde7bb68..000000000 --- a/logging_win.h +++ /dev/null @@ -1,84 +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_LOGGING_WIN_H_ -#define BASE_LOGGING_WIN_H_ - -#include - -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/win/event_trace_provider.h" - -namespace base { -template -struct StaticMemorySingletonTraits; -} // namespace base - -namespace logging { - -// Event ID for the log messages we generate. -EXTERN_C BASE_EXPORT const GUID kLogEventId; - -// Feature enable mask for LogEventProvider. -enum LogEnableMask { - // If this bit is set in our provider enable mask, we will include - // a stack trace with every log message. - ENABLE_STACK_TRACE_CAPTURE = 0x0001, - // If this bit is set in our provider enable mask, the provider will log - // a LOG message with only the textual content of the message, and no - // stack trace. - ENABLE_LOG_MESSAGE_ONLY = 0x0002, -}; - -// The message types our log event provider generates. -// ETW likes user message types to start at 10. -enum LogMessageTypes { - // A textual only log message, contains a zero-terminated string. - LOG_MESSAGE = 10, - // A message with a stack trace, followed by the zero-terminated - // message text. - LOG_MESSAGE_WITH_STACKTRACE = 11, - // A message with: - // a stack trace, - // the line number as a four byte integer, - // the file as a zero terminated UTF8 string, - // the zero-terminated UTF8 message text. - LOG_MESSAGE_FULL = 12, -}; - -// Trace provider class to drive log control and transport -// with Event Tracing for Windows. -class BASE_EXPORT LogEventProvider : public base::win::EtwTraceProvider { - public: - static LogEventProvider* GetInstance(); - - static bool LogMessage(logging::LogSeverity severity, const char* file, - int line, size_t message_start, const std::string& str); - - static void Initialize(const GUID& provider_name); - static void Uninitialize(); - - protected: - // Overridden to manipulate the log level on ETW control callbacks. - void OnEventsEnabled() override; - void OnEventsDisabled() override; - - private: - LogEventProvider(); - - // The log severity prior to OnEventsEnabled, - // restored in OnEventsDisabled. - logging::LogSeverity old_log_level_; - - friend struct base::StaticMemorySingletonTraits; - DISALLOW_COPY_AND_ASSIGN(LogEventProvider); -}; - -} // namespace logging - -#endif // BASE_LOGGING_WIN_H_ diff --git a/mac/OWNERS b/mac/OWNERS deleted file mode 100644 index 93e90e0a1..000000000 --- a/mac/OWNERS +++ /dev/null @@ -1,8 +0,0 @@ -mark@chromium.org -thakis@chromium.org - -# sdk_forward_declarations.[h|mm] will likely need to be modified by Cocoa -# developers in general. -per-file sdk_forward_declarations.*=file://chrome/browser/ui/cocoa/OWNERS - -# COMPONENT: Internals diff --git a/mac/authorization_util.h b/mac/authorization_util.h deleted file mode 100644 index 4629039c4..000000000 --- a/mac/authorization_util.h +++ /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. - -#ifndef BASE_MAC_AUTHORIZATION_UTIL_H_ -#define BASE_MAC_AUTHORIZATION_UTIL_H_ - -// AuthorizationExecuteWithPrivileges fork()s and exec()s the tool, but it -// does not wait() for it. It also doesn't provide the caller with access to -// the forked pid. If used irresponsibly, zombie processes will accumulate. -// -// Apple's really gotten us between a rock and a hard place, here. -// -// Fortunately, AuthorizationExecuteWithPrivileges does give access to the -// tool's stdout (and stdin) via a FILE* pipe. The tool can output its pid -// to this pipe, and the main program can read it, and then have something -// that it can wait() for. -// -// The contract is that any tool executed by the wrappers declared in this -// file must print its pid to stdout on a line by itself before doing anything -// else. -// -// http://developer.apple.com/library/mac/#samplecode/BetterAuthorizationSample/Listings/BetterAuthorizationSampleLib_c.html -// (Look for "What's This About Zombies?") - -#include -#include -#include -#include - -#include "base/base_export.h" - -namespace base { -namespace mac { - -// Obtains an AuthorizationRef for the rights indicated by |rights|. If -// necessary, prompts the user for authentication. If the user is prompted, -// |prompt| will be used as the prompt string and an icon appropriate for the -// application will be displayed in a prompt dialog. Note that the system -// appends its own text to the prompt string. |extraFlags| will be ORed -// together with the default flags. Returns NULL on failure. -BASE_EXPORT -AuthorizationRef GetAuthorizationRightsWithPrompt( - AuthorizationRights* rights, - CFStringRef prompt, - AuthorizationFlags extraFlags); - -// Obtains an AuthorizationRef (using |GetAuthorizationRightsWithPrompt|) that -// can be used to run commands as root. -BASE_EXPORT -AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt); - -// Calls straight through to AuthorizationExecuteWithPrivileges. If that -// call succeeds, |pid| will be set to the pid of the executed tool. If the -// pid can't be determined, |pid| will be set to -1. |pid| must not be NULL. -// |pipe| may be NULL, but the tool will always be executed with a pipe in -// order to read the pid from its stdout. -BASE_EXPORT -OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization, - const char* tool_path, - AuthorizationFlags options, - const char** arguments, - FILE** pipe, - pid_t* pid); - -// Calls ExecuteWithPrivilegesAndGetPID, and if that call succeeds, calls -// waitpid() to wait for the process to exit. If waitpid() succeeds, the -// exit status is placed in |exit_status|, otherwise, -1 is stored. -// |exit_status| may be NULL and this function will still wait for the process -// to exit. -BASE_EXPORT -OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization, - const char* tool_path, - AuthorizationFlags options, - const char** arguments, - FILE** pipe, - int* exit_status); - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_AUTHORIZATION_UTIL_H_ diff --git a/mac/authorization_util.mm b/mac/authorization_util.mm deleted file mode 100644 index a3bc4f959..000000000 --- a/mac/authorization_util.mm +++ /dev/null @@ -1,201 +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/mac/authorization_util.h" - -#import -#include -#include - -#include - -#include "base/logging.h" -#include "base/mac/bundle_locations.h" -#include "base/mac/foundation_util.h" -#include "base/mac/mac_logging.h" -#include "base/mac/scoped_authorizationref.h" -#include "base/macros.h" -#include "base/posix/eintr_wrapper.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" - -namespace base { -namespace mac { - -AuthorizationRef GetAuthorizationRightsWithPrompt( - AuthorizationRights* rights, - CFStringRef prompt, - AuthorizationFlags extraFlags) { - // Create an empty AuthorizationRef. - ScopedAuthorizationRef authorization; - OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, - kAuthorizationFlagDefaults, - authorization.get_pointer()); - if (status != errAuthorizationSuccess) { - OSSTATUS_LOG(ERROR, status) << "AuthorizationCreate"; - return NULL; - } - - AuthorizationFlags flags = kAuthorizationFlagDefaults | - kAuthorizationFlagInteractionAllowed | - kAuthorizationFlagExtendRights | - kAuthorizationFlagPreAuthorize | - extraFlags; - - // product_logo_32.png is used instead of app.icns because Authorization - // Services can't deal with .icns files. - NSString* icon_path = - [base::mac::FrameworkBundle() pathForResource:@"product_logo_32" - ofType:@"png"]; - const char* icon_path_c = [icon_path fileSystemRepresentation]; - size_t icon_path_length = icon_path_c ? strlen(icon_path_c) : 0; - - // The OS will dispay |prompt| along with a sentence asking the user to type - // the "password to allow this." - NSString* prompt_ns = base::mac::CFToNSCast(prompt); - const char* prompt_c = [prompt_ns UTF8String]; - size_t prompt_length = prompt_c ? strlen(prompt_c) : 0; - - AuthorizationItem environment_items[] = { - {kAuthorizationEnvironmentIcon, icon_path_length, (void*)icon_path_c, 0}, - {kAuthorizationEnvironmentPrompt, prompt_length, (void*)prompt_c, 0} - }; - - AuthorizationEnvironment environment = {arraysize(environment_items), - environment_items}; - - status = AuthorizationCopyRights(authorization, - rights, - &environment, - flags, - NULL); - - if (status != errAuthorizationSuccess) { - if (status != errAuthorizationCanceled) { - OSSTATUS_LOG(ERROR, status) << "AuthorizationCopyRights"; - } - return NULL; - } - - return authorization.release(); -} - -AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) { - // Specify the "system.privilege.admin" right, which allows - // AuthorizationExecuteWithPrivileges to run commands as root. - AuthorizationItem right_items[] = { - {kAuthorizationRightExecute, 0, NULL, 0} - }; - AuthorizationRights rights = {arraysize(right_items), right_items}; - - return GetAuthorizationRightsWithPrompt(&rights, prompt, 0); -} - -OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization, - const char* tool_path, - AuthorizationFlags options, - const char** arguments, - FILE** pipe, - pid_t* pid) { - // pipe may be NULL, but this function needs one. In that case, use a local - // pipe. - FILE* local_pipe; - FILE** pipe_pointer; - if (pipe) { - pipe_pointer = pipe; - } else { - pipe_pointer = &local_pipe; - } - -// AuthorizationExecuteWithPrivileges is deprecated in macOS 10.7, but no good -// replacement exists. https://crbug.com/593133. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // AuthorizationExecuteWithPrivileges wants |char* const*| for |arguments|, - // but it doesn't actually modify the arguments, and that type is kind of - // silly and callers probably aren't dealing with that. Put the cast here - // to make things a little easier on callers. - OSStatus status = AuthorizationExecuteWithPrivileges(authorization, - tool_path, - options, - (char* const*)arguments, - pipe_pointer); -#pragma clang diagnostic pop - if (status != errAuthorizationSuccess) { - return status; - } - - int line_pid = -1; - size_t line_length = 0; - char* line_c = fgetln(*pipe_pointer, &line_length); - if (line_c) { - if (line_length > 0 && line_c[line_length - 1] == '\n') { - // line_c + line_length is the start of the next line if there is one. - // Back up one character. - --line_length; - } - std::string line(line_c, line_length); - if (!base::StringToInt(line, &line_pid)) { - // StringToInt may have set line_pid to something, but if the conversion - // was imperfect, use -1. - LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: funny line: " << line; - line_pid = -1; - } - } else { - LOG(ERROR) << "ExecuteWithPrivilegesAndGetPid: no line"; - } - - if (!pipe) { - fclose(*pipe_pointer); - } - - if (pid) { - *pid = line_pid; - } - - return status; -} - -OSStatus ExecuteWithPrivilegesAndWait(AuthorizationRef authorization, - const char* tool_path, - AuthorizationFlags options, - const char** arguments, - FILE** pipe, - int* exit_status) { - pid_t pid; - OSStatus status = ExecuteWithPrivilegesAndGetPID(authorization, - tool_path, - options, - arguments, - pipe, - &pid); - if (status != errAuthorizationSuccess) { - return status; - } - - // exit_status may be NULL, but this function needs it. In that case, use a - // local version. - int local_exit_status; - int* exit_status_pointer; - if (exit_status) { - exit_status_pointer = exit_status; - } else { - exit_status_pointer = &local_exit_status; - } - - if (pid != -1) { - pid_t wait_result = HANDLE_EINTR(waitpid(pid, exit_status_pointer, 0)); - if (wait_result != pid) { - PLOG(ERROR) << "waitpid"; - *exit_status_pointer = -1; - } - } else { - *exit_status_pointer = -1; - } - - return status; -} - -} // namespace mac -} // namespace base diff --git a/mac/availability.h b/mac/availability.h deleted file mode 100644 index 6d0bcc7a2..000000000 --- a/mac/availability.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Provides the definition of API_AVAILABLE while we're on an SDK that doesn't -// contain it yet. -// TODO(thakis): Remove this file once we're on the 10.12 SDK. - -#ifndef BASE_MAC_AVAILABILITY_H_ -#define BASE_MAC_AVAILABILITY_H_ - -#include - -#if !defined(MAC_OS_X_VERSION_10_12) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 -#define __API_AVAILABLE_PLATFORM_macos(x) macos, introduced = x -#define __API_AVAILABLE_PLATFORM_macosx(x) macosx, introduced = x -#define __API_AVAILABLE_PLATFORM_ios(x) ios, introduced = x -#define __API_AVAILABLE_PLATFORM_watchos(x) watchos, introduced = x -#define __API_AVAILABLE_PLATFORM_tvos(x) tvos, introduced = x -#define __API_A(x) __attribute__((availability(__API_AVAILABLE_PLATFORM_##x))) -#define __API_AVAILABLE1(x) __API_A(x) -#define __API_AVAILABLE2(x, y) __API_A(x) __API_A(y) -#define __API_AVAILABLE3(x, y, z) __API_A(x) __API_A(y) __API_A(z) -#define __API_AVAILABLE4(x, y, z, t) __API_A(x) __API_A(y) __API_A(z) __API_A(t) -#define __API_AVAILABLE_GET_MACRO(_1, _2, _3, _4, NAME, ...) NAME -#define API_AVAILABLE(...) \ - __API_AVAILABLE_GET_MACRO(__VA_ARGS__, __API_AVAILABLE4, __API_AVAILABLE3, \ - __API_AVAILABLE2, __API_AVAILABLE1) \ - (__VA_ARGS__) -#else -#import -#endif // MAC_OS_X_VERSION_10_12 - -#endif // BASE_MAC_AVAILABILITY_H_ diff --git a/mac/bind_objc_block_unittest.mm b/mac/bind_objc_block_unittest.mm deleted file mode 100644 index b34514670..000000000 --- a/mac/bind_objc_block_unittest.mm +++ /dev/null @@ -1,156 +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 - -#include "base/bind.h" -#include "base/callback.h" -#include "base/callback_helpers.h" -#include "base/mac/scoped_nsautorelease_pool.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/gtest_mac.h" - -#if defined(OS_IOS) -#include "base/ios/weak_nsobject.h" -#endif - -namespace { - -TEST(BindObjcBlockTest, TestScopedClosureRunnerExitScope) { - int run_count = 0; - int* ptr = &run_count; - { - base::ScopedClosureRunner runner(base::BindOnce(base::RetainBlock(^{ - (*ptr)++; - }))); - EXPECT_EQ(0, run_count); - } - EXPECT_EQ(1, run_count); -} - -TEST(BindObjcBlockTest, TestScopedClosureRunnerRelease) { - int run_count = 0; - int* ptr = &run_count; - base::OnceClosure c; - { - base::ScopedClosureRunner runner(base::BindOnce(base::RetainBlock(^{ - (*ptr)++; - }))); - c = runner.Release(); - EXPECT_EQ(0, run_count); - } - EXPECT_EQ(0, run_count); - std::move(c).Run(); - EXPECT_EQ(1, run_count); -} - -TEST(BindObjcBlockTest, TestReturnValue) { - const int kReturnValue = 42; - base::OnceCallback c = base::BindOnce(base::RetainBlock(^{ - return kReturnValue; - })); - EXPECT_EQ(kReturnValue, std::move(c).Run()); -} - -TEST(BindObjcBlockTest, TestArgument) { - const int kArgument = 42; - base::OnceCallback c = base::BindOnce(base::RetainBlock(^(int a) { - return a + 1; - })); - EXPECT_EQ(kArgument + 1, std::move(c).Run(kArgument)); -} - -TEST(BindObjcBlockTest, TestTwoArguments) { - std::string result; - std::string* ptr = &result; - base::OnceCallback c = - base::BindOnce( - base::RetainBlock(^(const std::string& a, const std::string& b) { - *ptr = a + b; - })); - std::move(c).Run("forty", "two"); - EXPECT_EQ(result, "fortytwo"); -} - -TEST(BindObjcBlockTest, TestThreeArguments) { - std::string result; - std::string* ptr = &result; - base::OnceCallback - c = base::BindOnce(base::RetainBlock( - ^(const std::string& a, const std::string& b, const std::string& c) { - *ptr = a + b + c; - })); - std::move(c).Run("six", "times", "nine"); - EXPECT_EQ(result, "sixtimesnine"); -} - -TEST(BindObjcBlockTest, TestSixArguments) { - std::string result1; - std::string* ptr = &result1; - int result2; - int* ptr2 = &result2; - base::OnceCallback - c = base::BindOnce(base::RetainBlock(^(int a, int b, const std::string& c, - const std::string& d, int e, - const std::string& f) { - *ptr = c + d + f; - *ptr2 = a + b + e; - })); - std::move(c).Run(1, 2, "infinite", "improbability", 3, "drive"); - EXPECT_EQ(result1, "infiniteimprobabilitydrive"); - EXPECT_EQ(result2, 6); -} - -TEST(BindObjcBlockTest, TestBlockMoveable) { - base::OnceClosure c; - __block BOOL invoked_block = NO; - { - base::mac::ScopedNSAutoreleasePool autorelease_pool; - c = base::BindOnce(base::RetainBlock(^(std::unique_ptr v) { - invoked_block = *v; - }), - std::make_unique(YES)); - }; - std::move(c).Run(); - EXPECT_TRUE(invoked_block); -} - -// Tests that the bound block is retained until the end of its execution, -// even if the callback itself is destroyed during the invocation. It was -// found that some code depends on this behaviour (see crbug.com/845687). -TEST(BindObjcBlockTest, TestBlockDeallocation) { - base::RepeatingClosure closure; - __block BOOL invoked_block = NO; - closure = base::BindRepeating( - base::RetainBlock(^(base::RepeatingClosure* this_closure) { - *this_closure = base::RepeatingClosure(); - invoked_block = YES; - }), - &closure); - closure.Run(); - EXPECT_TRUE(invoked_block); -} - -#if defined(OS_IOS) - -TEST(BindObjcBlockTest, TestBlockReleased) { - base::WeakNSObject weak_nsobject; - { - base::mac::ScopedNSAutoreleasePool autorelease_pool; - NSObject* nsobject = [[[NSObject alloc] init] autorelease]; - weak_nsobject.reset(nsobject); - - auto callback = base::BindOnce(base::RetainBlock(^{ - [nsobject description]; - })); - } - EXPECT_NSEQ(nil, weak_nsobject); -} - -#endif - -} // namespace diff --git a/mac/bind_objc_block_unittest_arc.mm b/mac/bind_objc_block_unittest_arc.mm deleted file mode 100644 index 1fd25d7ce..000000000 --- a/mac/bind_objc_block_unittest_arc.mm +++ /dev/null @@ -1,152 +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 - -#include "base/bind.h" -#include "base/callback.h" -#include "base/callback_helpers.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/gtest_mac.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -namespace { - -TEST(BindObjcBlockTestARC, TestScopedClosureRunnerExitScope) { - int run_count = 0; - int* ptr = &run_count; - { - base::ScopedClosureRunner runner(base::BindOnce(^{ - (*ptr)++; - })); - EXPECT_EQ(0, run_count); - } - EXPECT_EQ(1, run_count); -} - -TEST(BindObjcBlockTestARC, TestScopedClosureRunnerRelease) { - int run_count = 0; - int* ptr = &run_count; - base::OnceClosure c; - { - base::ScopedClosureRunner runner(base::BindOnce(^{ - (*ptr)++; - })); - c = runner.Release(); - EXPECT_EQ(0, run_count); - } - EXPECT_EQ(0, run_count); - std::move(c).Run(); - EXPECT_EQ(1, run_count); -} - -TEST(BindObjcBlockTestARC, TestReturnValue) { - const int kReturnValue = 42; - base::OnceCallback c = base::BindOnce(^{ - return kReturnValue; - }); - EXPECT_EQ(kReturnValue, std::move(c).Run()); -} - -TEST(BindObjcBlockTestARC, TestArgument) { - const int kArgument = 42; - base::OnceCallback c = base::BindOnce(^(int a) { - return a + 1; - }); - EXPECT_EQ(kArgument + 1, std::move(c).Run(kArgument)); -} - -TEST(BindObjcBlockTestARC, TestTwoArguments) { - std::string result; - std::string* ptr = &result; - base::OnceCallback c = - base::BindOnce(^(const std::string& a, const std::string& b) { - *ptr = a + b; - }); - std::move(c).Run("forty", "two"); - EXPECT_EQ(result, "fortytwo"); -} - -TEST(BindObjcBlockTestARC, TestThreeArguments) { - std::string result; - std::string* ptr = &result; - base::OnceCallback - c = base::BindOnce( - ^(const std::string& a, const std::string& b, const std::string& c) { - *ptr = a + b + c; - }); - std::move(c).Run("six", "times", "nine"); - EXPECT_EQ(result, "sixtimesnine"); -} - -TEST(BindObjcBlockTestARC, TestSixArguments) { - std::string result1; - std::string* ptr = &result1; - int result2; - int* ptr2 = &result2; - base::OnceCallback - c = base::BindOnce(^(int a, int b, const std::string& c, - const std::string& d, int e, const std::string& f) { - *ptr = c + d + f; - *ptr2 = a + b + e; - }); - std::move(c).Run(1, 2, "infinite", "improbability", 3, "drive"); - EXPECT_EQ(result1, "infiniteimprobabilitydrive"); - EXPECT_EQ(result2, 6); -} - -TEST(BindObjcBlockTestARC, TestBlockMoveable) { - base::OnceClosure c; - __block BOOL invoked_block = NO; - @autoreleasepool { - c = base::BindOnce( - ^(std::unique_ptr v) { - invoked_block = *v; - }, - std::make_unique(YES)); - }; - std::move(c).Run(); - EXPECT_TRUE(invoked_block); -} - -// Tests that the bound block is retained until the end of its execution, -// even if the callback itself is destroyed during the invocation. It was -// found that some code depends on this behaviour (see crbug.com/845687). -TEST(BindObjcBlockTestARC, TestBlockDeallocation) { - base::RepeatingClosure closure; - __block BOOL invoked_block = NO; - closure = base::BindRepeating( - ^(base::RepeatingClosure* this_closure) { - *this_closure = base::RepeatingClosure(); - invoked_block = YES; - }, - &closure); - closure.Run(); - EXPECT_TRUE(invoked_block); -} - -#if defined(OS_IOS) - -TEST(BindObjcBlockTestARC, TestBlockReleased) { - __weak NSObject* weak_nsobject; - @autoreleasepool { - NSObject* nsobject = [[NSObject alloc] init]; - weak_nsobject = nsobject; - - auto callback = base::BindOnce(^{ - [nsobject description]; - }); - } - EXPECT_NSEQ(nil, weak_nsobject); -} - -#endif - -} // namespace diff --git a/mac/bundle_locations.h b/mac/bundle_locations.h deleted file mode 100644 index 5cc44ba96..000000000 --- a/mac/bundle_locations.h +++ /dev/null @@ -1,66 +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_MAC_BUNDLE_LOCATIONS_H_ -#define BASE_MAC_BUNDLE_LOCATIONS_H_ - -#include "base/base_export.h" -#include "base/files/file_path.h" - -#if defined(__OBJC__) -#import -#else // __OBJC__ -class NSBundle; -#endif // __OBJC__ - -namespace base { - -class FilePath; - -namespace mac { - -// This file provides several functions to explicitly request the various -// component bundles of Chrome. Please use these methods rather than calling -// +[NSBundle mainBundle] or CFBundleGetMainBundle(). -// -// Terminology -// - "Outer Bundle" - This is the main bundle for Chrome; it's what -// +[NSBundle mainBundle] returns when Chrome is launched normally. -// -// - "Main Bundle" - This is the bundle from which Chrome was launched. -// This will be the same as the outer bundle except when Chrome is launched -// via an app shortcut, in which case this will return the app shortcut's -// bundle rather than the main Chrome bundle. -// -// - "Framework Bundle" - This is the bundle corresponding to the Chrome -// framework. -// -// Guidelines for use: -// - To access a resource, the Framework bundle should be used. -// - If the choice is between the Outer or Main bundles then please choose -// carefully. Most often the Outer bundle will be the right choice, but for -// cases such as adding an app to the "launch on startup" list, the Main -// bundle is probably the one to use. - -// Methods for retrieving the various bundles. -BASE_EXPORT NSBundle* MainBundle(); -BASE_EXPORT FilePath MainBundlePath(); -BASE_EXPORT NSBundle* OuterBundle(); -BASE_EXPORT FilePath OuterBundlePath(); -BASE_EXPORT NSBundle* FrameworkBundle(); -BASE_EXPORT FilePath FrameworkBundlePath(); - -// Set the bundle that the preceding functions will return, overriding the -// default values. Restore the default by passing in |nil|. -BASE_EXPORT void SetOverrideOuterBundle(NSBundle* bundle); -BASE_EXPORT void SetOverrideFrameworkBundle(NSBundle* bundle); - -// Same as above but accepting a FilePath argument. -BASE_EXPORT void SetOverrideOuterBundlePath(const FilePath& file_path); -BASE_EXPORT void SetOverrideFrameworkBundlePath(const FilePath& file_path); - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_BUNDLE_LOCATIONS_H_ diff --git a/mac/bundle_locations.mm b/mac/bundle_locations.mm deleted file mode 100644 index 54021b85e..000000000 --- a/mac/bundle_locations.mm +++ /dev/null @@ -1,83 +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/mac/bundle_locations.h" - -#include "base/logging.h" -#include "base/mac/foundation_util.h" -#include "base/strings/sys_string_conversions.h" - -namespace base { -namespace mac { - -// NSBundle isn't threadsafe, all functions in this file must be called on the -// main thread. -static NSBundle* g_override_framework_bundle = nil; -static NSBundle* g_override_outer_bundle = nil; - -NSBundle* MainBundle() { - return [NSBundle mainBundle]; -} - -FilePath MainBundlePath() { - NSBundle* bundle = MainBundle(); - return NSStringToFilePath([bundle bundlePath]); -} - -NSBundle* OuterBundle() { - if (g_override_outer_bundle) - return g_override_outer_bundle; - return [NSBundle mainBundle]; -} - -FilePath OuterBundlePath() { - NSBundle* bundle = OuterBundle(); - return NSStringToFilePath([bundle bundlePath]); -} - -NSBundle* FrameworkBundle() { - if (g_override_framework_bundle) - return g_override_framework_bundle; - return [NSBundle mainBundle]; -} - -FilePath FrameworkBundlePath() { - NSBundle* bundle = FrameworkBundle(); - return NSStringToFilePath([bundle bundlePath]); -} - -static void AssignOverrideBundle(NSBundle* new_bundle, - NSBundle** override_bundle) { - if (new_bundle != *override_bundle) { - [*override_bundle release]; - *override_bundle = [new_bundle retain]; - } -} - -static void AssignOverridePath(const FilePath& file_path, - NSBundle** override_bundle) { - NSString* path = base::SysUTF8ToNSString(file_path.value()); - NSBundle* new_bundle = [NSBundle bundleWithPath:path]; - DCHECK(new_bundle) << "Failed to load the bundle at " << file_path.value(); - AssignOverrideBundle(new_bundle, override_bundle); -} - -void SetOverrideOuterBundle(NSBundle* bundle) { - AssignOverrideBundle(bundle, &g_override_outer_bundle); -} - -void SetOverrideFrameworkBundle(NSBundle* bundle) { - AssignOverrideBundle(bundle, &g_override_framework_bundle); -} - -void SetOverrideOuterBundlePath(const FilePath& file_path) { - AssignOverridePath(file_path, &g_override_outer_bundle); -} - -void SetOverrideFrameworkBundlePath(const FilePath& file_path) { - AssignOverridePath(file_path, &g_override_framework_bundle); -} - -} // namespace mac -} // namespace base diff --git a/mac/call_with_eh_frame.cc b/mac/call_with_eh_frame.cc deleted file mode 100644 index 457854141..000000000 --- a/mac/call_with_eh_frame.cc +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/mac/call_with_eh_frame.h" - -#include -#include - -#include "build/build_config.h" - -namespace base { -namespace mac { - -#if defined(OS_IOS) -// No iOS assembly implementation exists, so just call the block directly. -void CallWithEHFrame(void (^block)(void)) { - block(); -} -#else // OS_MACOSX -extern "C" _Unwind_Reason_Code __gxx_personality_v0(int, - _Unwind_Action, - uint64_t, - struct _Unwind_Exception*, - struct _Unwind_Context*); - -_Unwind_Reason_Code CxxPersonalityRoutine( - int version, - _Unwind_Action actions, - uint64_t exception_class, - struct _Unwind_Exception* exception_object, - struct _Unwind_Context* context) { - // Unwinding is a two-phase process: phase one searches for an exception - // handler, and phase two performs cleanup. For phase one, this custom - // personality will terminate the search. For phase two, this should delegate - // back to the standard personality routine. - - if ((actions & _UA_SEARCH_PHASE) != 0) { - // Tell libunwind that this is the end of the stack. When it encounters the - // CallWithEHFrame, it will stop searching for an exception handler. The - // result is that no exception handler has been found higher on the stack, - // and any that are lower on the stack (e.g. in CFRunLoopRunSpecific), will - // now be skipped. Since this is reporting the end of the stack, and no - // exception handler will have been found, std::terminate() will be called. - return _URC_END_OF_STACK; - } - - return __gxx_personality_v0(version, actions, exception_class, - exception_object, context); -} -#endif // defined(OS_IOS) - -} // namespace mac -} // namespace base diff --git a/mac/call_with_eh_frame.h b/mac/call_with_eh_frame.h deleted file mode 100644 index 1f7d5e0de..000000000 --- a/mac/call_with_eh_frame.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MAC_CALL_WITH_EH_FRAME_H_ -#define BASE_MAC_CALL_WITH_EH_FRAME_H_ - -#include "base/base_export.h" - -namespace base { -namespace mac { - -// Invokes the specified block in a stack frame with a special exception -// handler. This function creates an exception handling stack frame that -// specifies a custom C++ exception personality routine, which terminates the -// search for an exception handler at this frame. -// -// The purpose of this function is to prevent a try/catch statement in system -// libraries, acting as a global exception handler, from handling exceptions -// in such a way that disrupts the generation of useful stack traces. -void BASE_EXPORT CallWithEHFrame(void (^block)(void)); - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_CALL_WITH_EH_FRAME_H_ diff --git a/mac/call_with_eh_frame_asm.S b/mac/call_with_eh_frame_asm.S deleted file mode 100644 index 0e399cf2a..000000000 --- a/mac/call_with_eh_frame_asm.S +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// base::mac::CallWithEHFrame(void () block_pointer) -#define CALL_WITH_EH_FRAME __ZN4base3mac15CallWithEHFrameEU13block_pointerFvvE - - .section __TEXT,__text,regular,pure_instructions -#if !defined(COMPONENT_BUILD) - .private_extern CALL_WITH_EH_FRAME -#endif - .globl CALL_WITH_EH_FRAME - .align 4 -CALL_WITH_EH_FRAME: - - .cfi_startproc - - // Configure the C++ exception handler personality routine. Normally the - // compiler would emit ___gxx_personality_v0 here. The purpose of this - // function is to use a custom personality routine. - .cfi_personality 155, __ZN4base3mac21CxxPersonalityRoutineEi14_Unwind_ActionyP17_Unwind_ExceptionP15_Unwind_Context - .cfi_lsda 16, CallWithEHFrame_exception_table - -Lfunction_start: - pushq %rbp - .cfi_def_cfa_offset 16 - .cfi_offset %rbp, -16 - movq %rsp, %rbp - .cfi_def_cfa_register %rbp - - // Load the function pointer from the block descriptor. - movq 16(%rdi), %rax - - // Execute the block in the context of a C++ try{}. -Ltry_start: - callq *%rax -Ltry_end: - popq %rbp - ret - - // Landing pad for the exception handler. This should never be called, since - // the personality routine will stop the search for an exception handler, - // which will cause the runtime to invoke the default terminate handler. -Lcatch: - movq %rax, %rdi - callq ___cxa_begin_catch // The ABI requires a call to the catch handler. - ud2 // In the event this is called, make it fatal. - -Lfunction_end: - .cfi_endproc - -// The C++ exception table that is used to identify this frame as an -// exception handler. See http://llvm.org/docs/ExceptionHandling.html and -// http://mentorembedded.github.io/cxx-abi/exceptions.pdf. - .section __TEXT,__gcc_except_tab - .align 2 -CallWithEHFrame_exception_table: - .byte 255 // DW_EH_PE_omit - .byte 155 // DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4 - .asciz "\242\200\200" // LE int128 for the number of bytes in this table. - .byte 3 // DW_EH_PE_udata4 - .byte 26 // Callsite table length. - -// First callsite. -CS1_begin = Ltry_start - Lfunction_start - .long CS1_begin -CS1_end = Ltry_end - Ltry_start - .long CS1_end - -// First landing pad. -LP1 = Lcatch - Lfunction_start - .long LP1 - .byte 1 // Action record. - -// Second callsite. -CS2_begin = Ltry_end - Lfunction_start - .long CS2_begin -CS2_end = Lfunction_end - Ltry_end - .long CS2_end - -// Second landing pad (none). - .long 0 - .byte 0 // No action. - -// Action table. - .byte 1 // Action record 1. - .byte 0 // No further action to take. - .long 0 // No type filter for this catch(){} clause. - .align 2 diff --git a/mac/call_with_eh_frame_unittest.mm b/mac/call_with_eh_frame_unittest.mm deleted file mode 100644 index 4dad82209..000000000 --- a/mac/call_with_eh_frame_unittest.mm +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/mac/call_with_eh_frame.h" - -#import - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace mac { -namespace { - -class CallWithEHFrameTest : public testing::Test { - protected: - void ThrowException() { - @throw [NSException exceptionWithName:@"TestException" - reason:@"Testing exceptions" - userInfo:nil]; - } -}; - -// Catching from within the EHFrame is allowed. -TEST_F(CallWithEHFrameTest, CatchExceptionHigher) { - bool __block saw_exception = false; - base::mac::CallWithEHFrame(^{ - @try { - ThrowException(); - } @catch (NSException* exception) { - saw_exception = true; - } - }); - EXPECT_TRUE(saw_exception); -} - -// Trying to catch an exception outside the EHFrame is blocked. -TEST_F(CallWithEHFrameTest, CatchExceptionLower) { - auto catch_exception_lower = ^{ - bool saw_exception = false; - @try { - base::mac::CallWithEHFrame(^{ - ThrowException(); - }); - } @catch (NSException* exception) { - saw_exception = true; - } - ASSERT_FALSE(saw_exception); - }; - EXPECT_DEATH(catch_exception_lower(), ""); -} - -} // namespace -} // namespace mac -} // namespace base diff --git a/mac/close_nocancel.cc b/mac/close_nocancel.cc deleted file mode 100644 index 8971e731c..000000000 --- a/mac/close_nocancel.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// http://crbug.com/269623 -// http://openradar.appspot.com/14999594 -// -// When the default version of close used on Mac OS X fails with EINTR, the -// file descriptor is not in a deterministic state. It may have been closed, -// or it may not have been. This makes it impossible to gracefully recover -// from the error. If the close is retried after the FD has been closed, the -// subsequent close can report EBADF, or worse, it can close an unrelated FD -// opened by another thread. If the close is not retried after the FD has been -// left open, the FD is leaked. Neither of these are good options. -// -// Mac OS X provides an alternate version of close, close$NOCANCEL. This -// version will never fail with EINTR before the FD is actually closed. With -// this version, it is thus safe to call close without checking for EINTR (as -// the HANDLE_EINTR macro does) and not risk leaking the FD. In fact, mixing -// this verison of close with HANDLE_EINTR is hazardous. -// -// The $NOCANCEL variants of various system calls are activated by compiling -// with __DARWIN_NON_CANCELABLE, which prevents them from being pthread -// cancellation points. Rather than taking such a heavy-handed approach, this -// file implements an alternative: to use the $NOCANCEL variant of close (thus -// preventing it from being a pthread cancellation point) without affecting -// any other system calls. -// -// This file operates by providing a close function with the non-$NOCANCEL -// symbol name expected for the compilation environment as set by -// and (the DARWIN_ALIAS_C macro). That name is set by an asm -// label on the declaration of the close function, so the definition of that -// function receives that name. The function calls the $NOCANCEL variant, which -// is resolved from libsyscall. By linking with this version of close prior to -// the libsyscall version, close's implementation is overridden. - -#include -#include - -// If the non-cancelable variants of all system calls have already been -// chosen, do nothing. -#if !__DARWIN_NON_CANCELABLE - -extern "C" { - -#if !__DARWIN_ONLY_UNIX_CONFORMANCE - -// When there's a choice between UNIX2003 and pre-UNIX2003. There's no -// close$NOCANCEL symbol in this case, so use close$NOCANCEL$UNIX2003 as the -// implementation. It does the same thing that close$NOCANCEL would do. -#define close_implementation close$NOCANCEL$UNIX2003 - -#else // __DARWIN_ONLY_UNIX_CONFORMANCE - -// When only UNIX2003 is supported: -#define close_implementation close$NOCANCEL - -#endif - -int close_implementation(int fd); - -int close(int fd) { - return close_implementation(fd); -} - -#undef close_implementation - -} // extern "C" - -#endif // !__DARWIN_NON_CANCELABLE diff --git a/mac/dispatch_source_mach.cc b/mac/dispatch_source_mach.cc deleted file mode 100644 index 0858f30c2..000000000 --- a/mac/dispatch_source_mach.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/mac/dispatch_source_mach.h" - -namespace base { - -DispatchSourceMach::DispatchSourceMach(const char* name, - mach_port_t port, - void (^event_handler)()) - // TODO(rsesek): Specify DISPATCH_QUEUE_SERIAL, in the 10.7 SDK. NULL - // means the same thing but is not symbolically clear. - : DispatchSourceMach(dispatch_queue_create(name, NULL), - port, - event_handler) { - // Since the queue was created above in the delegated constructor, and it was - // subsequently retained, release it here. - dispatch_release(queue_); -} - -DispatchSourceMach::DispatchSourceMach(dispatch_queue_t queue, - mach_port_t port, - void (^event_handler)()) - : queue_(queue, base::scoped_policy::RETAIN), - source_(dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, - port, 0, queue_)), - source_canceled_(dispatch_semaphore_create(0)) { - dispatch_source_set_event_handler(source_, event_handler); - dispatch_source_set_cancel_handler(source_, ^{ - dispatch_semaphore_signal(source_canceled_); - }); -} - -DispatchSourceMach::~DispatchSourceMach() { - // Cancel the source and wait for the semaphore to be signaled. This will - // ensure the source managed by this class is not used after it is freed. - dispatch_source_cancel(source_); - source_.reset(); - - dispatch_semaphore_wait(source_canceled_, DISPATCH_TIME_FOREVER); -} - -void DispatchSourceMach::Resume() { - dispatch_resume(source_); -} - -} // namespace base diff --git a/mac/dispatch_source_mach.h b/mac/dispatch_source_mach.h deleted file mode 100644 index 336125f85..000000000 --- a/mac/dispatch_source_mach.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MAC_DISPATCH_SOURCE_MACH_H_ -#define BASE_MAC_DISPATCH_SOURCE_MACH_H_ - -#include - -#include "base/base_export.h" -#include "base/mac/scoped_dispatch_object.h" -#include "base/macros.h" - -namespace base { - -// This class encapsulates a MACH_RECV dispatch source. When this object is -// destroyed, the source will be cancelled and it will wait for the source -// to stop executing work. The source can run on either a user-supplied queue, -// or it can create its own for the source. -class BASE_EXPORT DispatchSourceMach { - public: - // Creates a new dispatch source for the |port| and schedules it on a new - // queue that will be created with |name|. When a Mach message is received, - // the |event_handler| will be called. - DispatchSourceMach(const char* name, - mach_port_t port, - void (^event_handler)()); - - // Creates a new dispatch source with the same semantics as above, but rather - // than creating a new queue, it schedules the source on |queue|. - DispatchSourceMach(dispatch_queue_t queue, - mach_port_t port, - void (^event_handler)()); - - // Cancels the source and waits for it to become fully cancelled before - // releasing the source. - ~DispatchSourceMach(); - - // Resumes the source. This must be called before any Mach messages will - // be received. - void Resume(); - - private: - // The dispatch queue used to service the source_. - ScopedDispatchObject queue_; - - // A MACH_RECV dispatch source. - ScopedDispatchObject source_; - - // Semaphore used to wait on the |source_|'s cancellation in the destructor. - ScopedDispatchObject source_canceled_; - - DISALLOW_COPY_AND_ASSIGN(DispatchSourceMach); -}; - -} // namespace base - -#endif // BASE_MAC_DISPATCH_SOURCE_MACH_H_ diff --git a/mac/dispatch_source_mach_unittest.cc b/mac/dispatch_source_mach_unittest.cc deleted file mode 100644 index 738a13720..000000000 --- a/mac/dispatch_source_mach_unittest.cc +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/mac/dispatch_source_mach.h" - -#include - -#include - -#include "base/logging.h" -#include "base/mac/scoped_mach_port.h" -#include "base/test/test_timeouts.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -class DispatchSourceMachTest : public testing::Test { - public: - void SetUp() override { - mach_port_t port = MACH_PORT_NULL; - ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(mach_task_self(), - MACH_PORT_RIGHT_RECEIVE, &port)); - receive_right_.reset(port); - - ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(mach_task_self(), port, - port, MACH_MSG_TYPE_MAKE_SEND)); - send_right_.reset(port); - } - - mach_port_t GetPort() { return receive_right_.get(); } - - void WaitForSemaphore(dispatch_semaphore_t semaphore) { - dispatch_semaphore_wait(semaphore, dispatch_time( - DISPATCH_TIME_NOW, - TestTimeouts::action_timeout().InSeconds() * NSEC_PER_SEC)); - } - - private: - base::mac::ScopedMachReceiveRight receive_right_; - base::mac::ScopedMachSendRight send_right_; -}; - -TEST_F(DispatchSourceMachTest, ReceiveAfterResume) { - dispatch_semaphore_t signal = dispatch_semaphore_create(0); - mach_port_t port = GetPort(); - - bool __block did_receive = false; - DispatchSourceMach source("org.chromium.base.test.ReceiveAfterResume", - port, ^{ - mach_msg_empty_rcv_t msg = {{0}}; - msg.header.msgh_size = sizeof(msg); - msg.header.msgh_local_port = port; - mach_msg_receive(&msg.header); - did_receive = true; - - dispatch_semaphore_signal(signal); - }); - - mach_msg_empty_send_t msg = {{0}}; - msg.header.msgh_size = sizeof(msg); - msg.header.msgh_remote_port = port; - msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND); - ASSERT_EQ(KERN_SUCCESS, mach_msg_send(&msg.header)); - - EXPECT_FALSE(did_receive); - - source.Resume(); - - WaitForSemaphore(signal); - dispatch_release(signal); - - EXPECT_TRUE(did_receive); -} - -TEST_F(DispatchSourceMachTest, NoMessagesAfterDestruction) { - mach_port_t port = GetPort(); - - std::unique_ptr count(new int(0)); - int* __block count_ptr = count.get(); - - std::unique_ptr source(new DispatchSourceMach( - "org.chromium.base.test.NoMessagesAfterDestruction", port, ^{ - mach_msg_empty_rcv_t msg = {{0}}; - msg.header.msgh_size = sizeof(msg); - msg.header.msgh_local_port = port; - mach_msg_receive(&msg.header); - LOG(INFO) << "Receieve " << *count_ptr; - ++(*count_ptr); - })); - source->Resume(); - - dispatch_queue_t queue = - dispatch_queue_create("org.chromium.base.test.MessageSend", NULL); - dispatch_semaphore_t signal = dispatch_semaphore_create(0); - for (int i = 0; i < 30; ++i) { - dispatch_async(queue, ^{ - mach_msg_empty_send_t msg = {{0}}; - msg.header.msgh_size = sizeof(msg); - msg.header.msgh_remote_port = port; - msg.header.msgh_bits = - MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND); - mach_msg_send(&msg.header); - }); - - // After sending five messages, shut down the source and taint the - // pointer the handler dereferences. The test will crash if |count_ptr| - // is being used after "free". - if (i == 5) { - std::unique_ptr* source_ptr = &source; - dispatch_async(queue, ^{ - source_ptr->reset(); - count_ptr = reinterpret_cast(0xdeaddead); - dispatch_semaphore_signal(signal); - }); - } - } - - WaitForSemaphore(signal); - dispatch_release(signal); - - dispatch_release(queue); -} - -} // namespace base diff --git a/mac/foundation_util.h b/mac/foundation_util.h deleted file mode 100644 index 26a2f18bc..000000000 --- a/mac/foundation_util.h +++ /dev/null @@ -1,406 +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_MAC_FOUNDATION_UTIL_H_ -#define BASE_MAC_FOUNDATION_UTIL_H_ - -#include - -#include -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/mac/scoped_cftyperef.h" -#include "build/build_config.h" - -#if defined(__OBJC__) -#import -@class NSFont; -@class UIFont; -#else // __OBJC__ -#include -class NSBundle; -class NSFont; -class NSString; -class UIFont; -#endif // __OBJC__ - -#if defined(OS_IOS) -#include -#else -#include -#endif - -// Adapted from NSObjCRuntime.h NS_ENUM definition (used in Foundation starting -// with the OS X 10.8 SDK and the iOS 6.0 SDK). -#if __has_extension(cxx_strong_enums) && \ - (defined(OS_IOS) || (defined(MAC_OS_X_VERSION_10_8) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8)) -#define CR_FORWARD_ENUM(_type, _name) enum _name : _type _name -#else -#define CR_FORWARD_ENUM(_type, _name) _type _name -#endif - -// Adapted from NSPathUtilities.h and NSObjCRuntime.h. -#if __LP64__ || NS_BUILD_32_LIKE_64 -typedef CR_FORWARD_ENUM(unsigned long, NSSearchPathDirectory); -typedef unsigned long NSSearchPathDomainMask; -#else -typedef CR_FORWARD_ENUM(unsigned int, NSSearchPathDirectory); -typedef unsigned int NSSearchPathDomainMask; -#endif - -#if defined(OS_IOS) -typedef struct CF_BRIDGED_TYPE(id) __SecKey* SecKeyRef; -typedef struct CF_BRIDGED_TYPE(id) __SecPolicy* SecPolicyRef; -#else -typedef struct OpaqueSecKeyRef* SecKeyRef; -typedef struct OpaqueSecPolicyRef* SecPolicyRef; -#endif - -namespace base { - -class FilePath; - -namespace mac { - -// Returns true if the application is running from a bundle -BASE_EXPORT bool AmIBundled(); -BASE_EXPORT void SetOverrideAmIBundled(bool value); - -#if defined(UNIT_TEST) -// This is required because instantiating some tests requires checking the -// directory structure, which sets the AmIBundled cache state. Individual tests -// may or may not be bundled, and this would trip them up if the cache weren't -// cleared. This should not be called from individual tests, just from test -// instantiation code that gets a path from PathService. -BASE_EXPORT void ClearAmIBundledCache(); -#endif - -// Returns true if this process is marked as a "Background only process". -BASE_EXPORT bool IsBackgroundOnlyProcess(); - -// Returns the path to a resource within the framework bundle. -BASE_EXPORT FilePath PathForFrameworkBundleResource(CFStringRef resourceName); - -// Returns the creator code associated with the CFBundleRef at bundle. -OSType CreatorCodeForCFBundleRef(CFBundleRef bundle); - -// Returns the creator code associated with this application, by calling -// CreatorCodeForCFBundleRef for the application's main bundle. If this -// information cannot be determined, returns kUnknownType ('????'). This -// does not respect the override app bundle because it's based on CFBundle -// instead of NSBundle, and because callers probably don't want the override -// app bundle's creator code anyway. -BASE_EXPORT OSType CreatorCodeForApplication(); - -// Searches for directories for the given key in only the given |domain_mask|. -// If found, fills result (which must always be non-NULL) with the -// first found directory and returns true. Otherwise, returns false. -BASE_EXPORT bool GetSearchPathDirectory(NSSearchPathDirectory directory, - NSSearchPathDomainMask domain_mask, - FilePath* result); - -// Searches for directories for the given key in only the local domain. -// If found, fills result (which must always be non-NULL) with the -// first found directory and returns true. Otherwise, returns false. -BASE_EXPORT bool GetLocalDirectory(NSSearchPathDirectory directory, - FilePath* result); - -// Searches for directories for the given key in only the user domain. -// If found, fills result (which must always be non-NULL) with the -// first found directory and returns true. Otherwise, returns false. -BASE_EXPORT bool GetUserDirectory(NSSearchPathDirectory directory, - FilePath* result); - -// Returns the ~/Library directory. -BASE_EXPORT FilePath GetUserLibraryPath(); - -// Takes a path to an (executable) binary and tries to provide the path to an -// application bundle containing it. It takes the outermost bundle that it can -// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). -// |exec_name| - path to the binary -// returns - path to the application bundle, or empty on error -BASE_EXPORT FilePath GetAppBundlePath(const FilePath& exec_name); - -#define TYPE_NAME_FOR_CF_TYPE_DECL(TypeCF) \ -BASE_EXPORT std::string TypeNameForCFType(TypeCF##Ref); - -TYPE_NAME_FOR_CF_TYPE_DECL(CFArray); -TYPE_NAME_FOR_CF_TYPE_DECL(CFBag); -TYPE_NAME_FOR_CF_TYPE_DECL(CFBoolean); -TYPE_NAME_FOR_CF_TYPE_DECL(CFData); -TYPE_NAME_FOR_CF_TYPE_DECL(CFDate); -TYPE_NAME_FOR_CF_TYPE_DECL(CFDictionary); -TYPE_NAME_FOR_CF_TYPE_DECL(CFNull); -TYPE_NAME_FOR_CF_TYPE_DECL(CFNumber); -TYPE_NAME_FOR_CF_TYPE_DECL(CFSet); -TYPE_NAME_FOR_CF_TYPE_DECL(CFString); -TYPE_NAME_FOR_CF_TYPE_DECL(CFURL); -TYPE_NAME_FOR_CF_TYPE_DECL(CFUUID); - -TYPE_NAME_FOR_CF_TYPE_DECL(CGColor); - -TYPE_NAME_FOR_CF_TYPE_DECL(CTFont); -TYPE_NAME_FOR_CF_TYPE_DECL(CTRun); - -TYPE_NAME_FOR_CF_TYPE_DECL(SecKey); -TYPE_NAME_FOR_CF_TYPE_DECL(SecPolicy); - -#undef TYPE_NAME_FOR_CF_TYPE_DECL - -// Retain/release calls for memory management in C++. -BASE_EXPORT void NSObjectRetain(void* obj); -BASE_EXPORT void NSObjectRelease(void* obj); - -// CFTypeRefToNSObjectAutorelease transfers ownership of a Core Foundation -// object (one derived from CFTypeRef) to the Foundation memory management -// system. In a traditional managed-memory environment, cf_object is -// autoreleased and returned as an NSObject. In a garbage-collected -// environment, cf_object is marked as eligible for garbage collection. -// -// This function should only be used to convert a concrete CFTypeRef type to -// its equivalent "toll-free bridged" NSObject subclass, for example, -// converting a CFStringRef to NSString. -// -// By calling this function, callers relinquish any ownership claim to -// cf_object. In a managed-memory environment, the object's ownership will be -// managed by the innermost NSAutoreleasePool, so after this function returns, -// callers should not assume that cf_object is valid any longer than the -// returned NSObject. -// -// Returns an id, typed here for C++'s sake as a void*. -BASE_EXPORT void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object); - -// Returns the base bundle ID, which can be set by SetBaseBundleID but -// defaults to a reasonable string. This never returns NULL. BaseBundleID -// returns a pointer to static storage that must not be freed. -BASE_EXPORT const char* BaseBundleID(); - -// Sets the base bundle ID to override the default. The implementation will -// make its own copy of new_base_bundle_id. -BASE_EXPORT void SetBaseBundleID(const char* new_base_bundle_id); - -} // namespace mac -} // namespace base - -#if !defined(__OBJC__) -#define OBJC_CPP_CLASS_DECL(x) class x; -#else // __OBJC__ -#define OBJC_CPP_CLASS_DECL(x) -#endif // __OBJC__ - -// Convert toll-free bridged CFTypes to NSTypes and vice-versa. This does not -// autorelease |cf_val|. This is useful for the case where there is a CFType in -// a call that expects an NSType and the compiler is complaining about const -// casting problems. -// The calls are used like this: -// NSString *foo = CFToNSCast(CFSTR("Hello")); -// CFStringRef foo2 = NSToCFCast(@"Hello"); -// The macro magic below is to enforce safe casting. It could possibly have -// been done using template function specialization, but template function -// specialization doesn't always work intuitively, -// (http://www.gotw.ca/publications/mill17.htm) so the trusty combination -// of macros and function overloading is used instead. - -#define CF_TO_NS_CAST_DECL(TypeCF, TypeNS) \ -OBJC_CPP_CLASS_DECL(TypeNS) \ -\ -namespace base { \ -namespace mac { \ -BASE_EXPORT TypeNS* CFToNSCast(TypeCF##Ref cf_val); \ -BASE_EXPORT TypeCF##Ref NSToCFCast(TypeNS* ns_val); \ -} \ -} - -#define CF_TO_NS_MUTABLE_CAST_DECL(name) \ -CF_TO_NS_CAST_DECL(CF##name, NS##name) \ -OBJC_CPP_CLASS_DECL(NSMutable##name) \ -\ -namespace base { \ -namespace mac { \ -BASE_EXPORT NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val); \ -BASE_EXPORT CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val); \ -} \ -} - -// List of toll-free bridged types taken from: -// http://www.cocoadev.com/index.pl?TollFreeBridged - -CF_TO_NS_MUTABLE_CAST_DECL(Array); -CF_TO_NS_MUTABLE_CAST_DECL(AttributedString); -CF_TO_NS_CAST_DECL(CFCalendar, NSCalendar); -CF_TO_NS_MUTABLE_CAST_DECL(CharacterSet); -CF_TO_NS_MUTABLE_CAST_DECL(Data); -CF_TO_NS_CAST_DECL(CFDate, NSDate); -CF_TO_NS_MUTABLE_CAST_DECL(Dictionary); -CF_TO_NS_CAST_DECL(CFError, NSError); -CF_TO_NS_CAST_DECL(CFLocale, NSLocale); -CF_TO_NS_CAST_DECL(CFNumber, NSNumber); -CF_TO_NS_CAST_DECL(CFRunLoopTimer, NSTimer); -CF_TO_NS_CAST_DECL(CFTimeZone, NSTimeZone); -CF_TO_NS_MUTABLE_CAST_DECL(Set); -CF_TO_NS_CAST_DECL(CFReadStream, NSInputStream); -CF_TO_NS_CAST_DECL(CFWriteStream, NSOutputStream); -CF_TO_NS_MUTABLE_CAST_DECL(String); -CF_TO_NS_CAST_DECL(CFURL, NSURL); - -#if defined(OS_IOS) -CF_TO_NS_CAST_DECL(CTFont, UIFont); -#else -CF_TO_NS_CAST_DECL(CTFont, NSFont); -#endif - -#undef CF_TO_NS_CAST_DECL -#undef CF_TO_NS_MUTABLE_CAST_DECL -#undef OBJC_CPP_CLASS_DECL - -namespace base { -namespace mac { - -// CFCast<>() and CFCastStrict<>() cast a basic CFTypeRef to a more -// specific CoreFoundation type. The compatibility of the passed -// object is found by comparing its opaque type against the -// requested type identifier. If the supplied object is not -// compatible with the requested return type, CFCast<>() returns -// NULL and CFCastStrict<>() will DCHECK. Providing a NULL pointer -// to either variant results in NULL being returned without -// triggering any DCHECK. -// -// Example usage: -// CFNumberRef some_number = base::mac::CFCast( -// CFArrayGetValueAtIndex(array, index)); -// -// CFTypeRef hello = CFSTR("hello world"); -// CFStringRef some_string = base::mac::CFCastStrict(hello); - -template -T CFCast(const CFTypeRef& cf_val); - -template -T CFCastStrict(const CFTypeRef& cf_val); - -#define CF_CAST_DECL(TypeCF) \ -template<> BASE_EXPORT TypeCF##Ref \ -CFCast(const CFTypeRef& cf_val);\ -\ -template<> BASE_EXPORT TypeCF##Ref \ -CFCastStrict(const CFTypeRef& cf_val); - -CF_CAST_DECL(CFArray); -CF_CAST_DECL(CFBag); -CF_CAST_DECL(CFBoolean); -CF_CAST_DECL(CFData); -CF_CAST_DECL(CFDate); -CF_CAST_DECL(CFDictionary); -CF_CAST_DECL(CFNull); -CF_CAST_DECL(CFNumber); -CF_CAST_DECL(CFSet); -CF_CAST_DECL(CFString); -CF_CAST_DECL(CFURL); -CF_CAST_DECL(CFUUID); - -CF_CAST_DECL(CGColor); - -CF_CAST_DECL(CTFont); -CF_CAST_DECL(CTFontDescriptor); -CF_CAST_DECL(CTRun); - -CF_CAST_DECL(SecKey); -CF_CAST_DECL(SecPolicy); - -#undef CF_CAST_DECL - -#if defined(__OBJC__) - -// ObjCCast<>() and ObjCCastStrict<>() cast a basic id to a more -// specific (NSObject-derived) type. The compatibility of the passed -// object is found by checking if it's a kind of the requested type -// identifier. If the supplied object is not compatible with the -// requested return type, ObjCCast<>() returns nil and -// ObjCCastStrict<>() will DCHECK. Providing a nil pointer to either -// variant results in nil being returned without triggering any DCHECK. -// -// The strict variant is useful when retrieving a value from a -// collection which only has values of a specific type, e.g. an -// NSArray of NSStrings. The non-strict variant is useful when -// retrieving values from data that you can't fully control. For -// example, a plist read from disk may be beyond your exclusive -// control, so you'd only want to check that the values you retrieve -// from it are of the expected types, but not crash if they're not. -// -// Example usage: -// NSString* version = base::mac::ObjCCast( -// [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]); -// -// NSString* str = base::mac::ObjCCastStrict( -// [ns_arr_of_ns_strs objectAtIndex:0]); -template -T* ObjCCast(id objc_val) { - if ([objc_val isKindOfClass:[T class]]) { - return reinterpret_cast(objc_val); - } - return nil; -} - -template -T* ObjCCastStrict(id objc_val) { - T* rv = ObjCCast(objc_val); - DCHECK(objc_val == nil || rv); - return rv; -} - -#endif // defined(__OBJC__) - -// Helper function for GetValueFromDictionary to create the error message -// that appears when a type mismatch is encountered. -BASE_EXPORT std::string GetValueFromDictionaryErrorMessage( - CFStringRef key, const std::string& expected_type, CFTypeRef value); - -// Utility function to pull out a value from a dictionary, check its type, and -// return it. Returns NULL if the key is not present or of the wrong type. -template -T GetValueFromDictionary(CFDictionaryRef dict, CFStringRef key) { - CFTypeRef value = CFDictionaryGetValue(dict, key); - T value_specific = CFCast(value); - - if (value && !value_specific) { - std::string expected_type = TypeNameForCFType(value_specific); - DLOG(WARNING) << GetValueFromDictionaryErrorMessage(key, - expected_type, - value); - } - - return value_specific; -} - -// Converts |path| to an autoreleased NSString. Returns nil if |path| is empty. -BASE_EXPORT NSString* FilePathToNSString(const FilePath& path); - -// Converts |str| to a FilePath. Returns an empty path if |str| is nil. -BASE_EXPORT FilePath NSStringToFilePath(NSString* str); - -#if defined(__OBJC__) -// Converts |range| to an NSRange, returning the new range in |range_out|. -// Returns true if conversion was successful, false if the values of |range| -// could not be converted to NSUIntegers. -BASE_EXPORT bool CFRangeToNSRange(CFRange range, - NSRange* range_out) WARN_UNUSED_RESULT; -#endif // defined(__OBJC__) - -} // namespace mac -} // namespace base - -// Stream operations for CFTypes. They can be used with NSTypes as well -// by using the NSToCFCast methods above. -// e.g. LOG(INFO) << base::mac::NSToCFCast(@"foo"); -// Operator << can not be overloaded for ObjectiveC types as the compiler -// can not distinguish between overloads for id with overloads for void*. -BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, - const CFErrorRef err); -BASE_EXPORT extern std::ostream& operator<<(std::ostream& o, - const CFStringRef str); - -#endif // BASE_MAC_FOUNDATION_UTIL_H_ diff --git a/mac/foundation_util.mm b/mac/foundation_util.mm deleted file mode 100644 index 15fc15ba3..000000000 --- a/mac/foundation_util.mm +++ /dev/null @@ -1,484 +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/mac/foundation_util.h" - -#include -#include -#include - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/mac/bundle_locations.h" -#include "base/mac/mac_logging.h" -#include "base/macros.h" -#include "base/numerics/safe_conversions.h" -#include "base/strings/sys_string_conversions.h" -#include "build/build_config.h" - -#if !defined(OS_IOS) -#import -#endif - -extern "C" { -CFTypeID SecKeyGetTypeID(); -#if !defined(OS_IOS) -CFTypeID SecACLGetTypeID(); -CFTypeID SecTrustedApplicationGetTypeID(); -Boolean _CFIsObjC(CFTypeID typeID, CFTypeRef obj); -#endif -} // extern "C" - -namespace base { -namespace mac { - -namespace { - -bool g_cached_am_i_bundled_called = false; -bool g_cached_am_i_bundled_value = false; -bool g_override_am_i_bundled = false; -bool g_override_am_i_bundled_value = false; - -bool UncachedAmIBundled() { -#if defined(OS_IOS) - // All apps are bundled on iOS. - return true; -#else - if (g_override_am_i_bundled) - return g_override_am_i_bundled_value; - - // Yes, this is cheap. - return [[base::mac::OuterBundle() bundlePath] hasSuffix:@".app"]; -#endif -} - -} // namespace - -bool AmIBundled() { - // If the return value is not cached, this function will return different - // values depending on when it's called. This confuses some client code, see - // http://crbug.com/63183 . - if (!g_cached_am_i_bundled_called) { - g_cached_am_i_bundled_called = true; - g_cached_am_i_bundled_value = UncachedAmIBundled(); - } - DCHECK_EQ(g_cached_am_i_bundled_value, UncachedAmIBundled()) - << "The return value of AmIBundled() changed. This will confuse tests. " - << "Call SetAmIBundled() override manually if your test binary " - << "delay-loads the framework."; - return g_cached_am_i_bundled_value; -} - -void SetOverrideAmIBundled(bool value) { -#if defined(OS_IOS) - // It doesn't make sense not to be bundled on iOS. - if (!value) - NOTREACHED(); -#endif - g_override_am_i_bundled = true; - g_override_am_i_bundled_value = value; -} - -BASE_EXPORT void ClearAmIBundledCache() { - g_cached_am_i_bundled_called = false; -} - -bool IsBackgroundOnlyProcess() { - // This function really does want to examine NSBundle's idea of the main - // bundle dictionary. It needs to look at the actual running .app's - // Info.plist to access its LSUIElement property. - NSDictionary* info_dictionary = [base::mac::MainBundle() infoDictionary]; - return [info_dictionary[@"LSUIElement"] boolValue] != NO; -} - -FilePath PathForFrameworkBundleResource(CFStringRef resourceName) { - NSBundle* bundle = base::mac::FrameworkBundle(); - NSString* resourcePath = [bundle pathForResource:(NSString*)resourceName - ofType:nil]; - return NSStringToFilePath(resourcePath); -} - -OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) { - OSType creator = kUnknownType; - CFBundleGetPackageInfo(bundle, NULL, &creator); - return creator; -} - -OSType CreatorCodeForApplication() { - CFBundleRef bundle = CFBundleGetMainBundle(); - if (!bundle) - return kUnknownType; - - return CreatorCodeForCFBundleRef(bundle); -} - -bool GetSearchPathDirectory(NSSearchPathDirectory directory, - NSSearchPathDomainMask domain_mask, - FilePath* result) { - DCHECK(result); - NSArray* dirs = - NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES); - if ([dirs count] < 1) { - return false; - } - *result = NSStringToFilePath(dirs[0]); - return true; -} - -bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) { - return GetSearchPathDirectory(directory, NSLocalDomainMask, result); -} - -bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) { - return GetSearchPathDirectory(directory, NSUserDomainMask, result); -} - -FilePath GetUserLibraryPath() { - FilePath user_library_path; - if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) { - DLOG(WARNING) << "Could not get user library path"; - } - return user_library_path; -} - -// Takes a path to an (executable) binary and tries to provide the path to an -// application bundle containing it. It takes the outermost bundle that it can -// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). -// |exec_name| - path to the binary -// returns - path to the application bundle, or empty on error -FilePath GetAppBundlePath(const FilePath& exec_name) { - const char kExt[] = ".app"; - const size_t kExtLength = arraysize(kExt) - 1; - - // Split the path into components. - std::vector components; - exec_name.GetComponents(&components); - - // It's an error if we don't get any components. - if (components.empty()) - return FilePath(); - - // Don't prepend '/' to the first component. - std::vector::const_iterator it = components.begin(); - std::string bundle_name = *it; - DCHECK_GT(it->length(), 0U); - // If the first component ends in ".app", we're already done. - if (it->length() > kExtLength && - !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) - return FilePath(bundle_name); - - // The first component may be "/" or "//", etc. Only append '/' if it doesn't - // already end in '/'. - if (bundle_name.back() != '/') - bundle_name += '/'; - - // Go through the remaining components. - for (++it; it != components.end(); ++it) { - DCHECK_GT(it->length(), 0U); - - bundle_name += *it; - - // If the current component ends in ".app", we're done. - if (it->length() > kExtLength && - !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) - return FilePath(bundle_name); - - // Separate this component from the next one. - bundle_name += '/'; - } - - return FilePath(); -} - -#define TYPE_NAME_FOR_CF_TYPE_DEFN(TypeCF) \ -std::string TypeNameForCFType(TypeCF##Ref) { \ - return #TypeCF; \ -} - -TYPE_NAME_FOR_CF_TYPE_DEFN(CFArray); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFBag); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFBoolean); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFData); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFDate); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFDictionary); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFNull); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFNumber); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFSet); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFString); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFURL); -TYPE_NAME_FOR_CF_TYPE_DEFN(CFUUID); - -TYPE_NAME_FOR_CF_TYPE_DEFN(CGColor); - -TYPE_NAME_FOR_CF_TYPE_DEFN(CTFont); -TYPE_NAME_FOR_CF_TYPE_DEFN(CTRun); - -#if !defined(OS_IOS) -TYPE_NAME_FOR_CF_TYPE_DEFN(SecKey); -TYPE_NAME_FOR_CF_TYPE_DEFN(SecPolicy); -#endif - -#undef TYPE_NAME_FOR_CF_TYPE_DEFN - -void NSObjectRetain(void* obj) { - id nsobj = static_cast >(obj); - [nsobj retain]; -} - -void NSObjectRelease(void* obj) { - id nsobj = static_cast >(obj); - [nsobj release]; -} - -void* CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object) { - // When GC is on, NSMakeCollectable marks cf_object for GC and autorelease - // is a no-op. - // - // In the traditional GC-less environment, NSMakeCollectable is a no-op, - // and cf_object is autoreleased, balancing out the caller's ownership claim. - // - // NSMakeCollectable returns nil when used on a NULL object. - return [NSMakeCollectable(cf_object) autorelease]; -} - -static const char* base_bundle_id; - -const char* BaseBundleID() { - if (base_bundle_id) { - return base_bundle_id; - } - -#if defined(GOOGLE_CHROME_BUILD) - return "com.google.Chrome"; -#else - return "org.chromium.Chromium"; -#endif -} - -void SetBaseBundleID(const char* new_base_bundle_id) { - if (new_base_bundle_id != base_bundle_id) { - free((void*)base_bundle_id); - base_bundle_id = new_base_bundle_id ? strdup(new_base_bundle_id) : NULL; - } -} - -// Definitions for the corresponding CF_TO_NS_CAST_DECL macros in -// foundation_util.h. -#define CF_TO_NS_CAST_DEFN(TypeCF, TypeNS) \ -\ -TypeNS* CFToNSCast(TypeCF##Ref cf_val) { \ - DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ - TypeNS* ns_val = \ - const_cast(reinterpret_cast(cf_val)); \ - return ns_val; \ -} \ -\ -TypeCF##Ref NSToCFCast(TypeNS* ns_val) { \ - TypeCF##Ref cf_val = reinterpret_cast(ns_val); \ - DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ - return cf_val; \ -} - -#define CF_TO_NS_MUTABLE_CAST_DEFN(name) \ -CF_TO_NS_CAST_DEFN(CF##name, NS##name) \ -\ -NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val) { \ - DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ - NSMutable##name* ns_val = reinterpret_cast(cf_val); \ - return ns_val; \ -} \ -\ -CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val) { \ - CFMutable##name##Ref cf_val = \ - reinterpret_cast(ns_val); \ - DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ - return cf_val; \ -} - -CF_TO_NS_MUTABLE_CAST_DEFN(Array); -CF_TO_NS_MUTABLE_CAST_DEFN(AttributedString); -CF_TO_NS_CAST_DEFN(CFCalendar, NSCalendar); -CF_TO_NS_MUTABLE_CAST_DEFN(CharacterSet); -CF_TO_NS_MUTABLE_CAST_DEFN(Data); -CF_TO_NS_CAST_DEFN(CFDate, NSDate); -CF_TO_NS_MUTABLE_CAST_DEFN(Dictionary); -CF_TO_NS_CAST_DEFN(CFError, NSError); -CF_TO_NS_CAST_DEFN(CFLocale, NSLocale); -CF_TO_NS_CAST_DEFN(CFNumber, NSNumber); -CF_TO_NS_CAST_DEFN(CFRunLoopTimer, NSTimer); -CF_TO_NS_CAST_DEFN(CFTimeZone, NSTimeZone); -CF_TO_NS_MUTABLE_CAST_DEFN(Set); -CF_TO_NS_CAST_DEFN(CFReadStream, NSInputStream); -CF_TO_NS_CAST_DEFN(CFWriteStream, NSOutputStream); -CF_TO_NS_MUTABLE_CAST_DEFN(String); -CF_TO_NS_CAST_DEFN(CFURL, NSURL); - -#if defined(OS_IOS) -CF_TO_NS_CAST_DEFN(CTFont, UIFont); -#else -// The NSFont/CTFont toll-free bridging is broken when it comes to type -// checking, so do some special-casing. -// http://www.openradar.me/15341349 rdar://15341349 -NSFont* CFToNSCast(CTFontRef cf_val) { - NSFont* ns_val = - const_cast(reinterpret_cast(cf_val)); - DCHECK(!cf_val || - CTFontGetTypeID() == CFGetTypeID(cf_val) || - (_CFIsObjC(CTFontGetTypeID(), cf_val) && - [ns_val isKindOfClass:[NSFont class]])); - return ns_val; -} - -CTFontRef NSToCFCast(NSFont* ns_val) { - CTFontRef cf_val = reinterpret_cast(ns_val); - DCHECK(!cf_val || - CTFontGetTypeID() == CFGetTypeID(cf_val) || - [ns_val isKindOfClass:[NSFont class]]); - return cf_val; -} -#endif - -#undef CF_TO_NS_CAST_DEFN -#undef CF_TO_NS_MUTABLE_CAST_DEFN - -#define CF_CAST_DEFN(TypeCF) \ -template<> TypeCF##Ref \ -CFCast(const CFTypeRef& cf_val) { \ - if (cf_val == NULL) { \ - return NULL; \ - } \ - if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \ - return (TypeCF##Ref)(cf_val); \ - } \ - return NULL; \ -} \ -\ -template<> TypeCF##Ref \ -CFCastStrict(const CFTypeRef& cf_val) { \ - TypeCF##Ref rv = CFCast(cf_val); \ - DCHECK(cf_val == NULL || rv); \ - return rv; \ -} - -CF_CAST_DEFN(CFArray); -CF_CAST_DEFN(CFBag); -CF_CAST_DEFN(CFBoolean); -CF_CAST_DEFN(CFData); -CF_CAST_DEFN(CFDate); -CF_CAST_DEFN(CFDictionary); -CF_CAST_DEFN(CFNull); -CF_CAST_DEFN(CFNumber); -CF_CAST_DEFN(CFSet); -CF_CAST_DEFN(CFString); -CF_CAST_DEFN(CFURL); -CF_CAST_DEFN(CFUUID); - -CF_CAST_DEFN(CGColor); - -CF_CAST_DEFN(CTFontDescriptor); -CF_CAST_DEFN(CTRun); - -#if defined(OS_IOS) -CF_CAST_DEFN(CTFont); -#else -// The NSFont/CTFont toll-free bridging is broken when it comes to type -// checking, so do some special-casing. -// http://www.openradar.me/15341349 rdar://15341349 -template<> CTFontRef -CFCast(const CFTypeRef& cf_val) { - if (cf_val == NULL) { - return NULL; - } - if (CFGetTypeID(cf_val) == CTFontGetTypeID()) { - return (CTFontRef)(cf_val); - } - - if (!_CFIsObjC(CTFontGetTypeID(), cf_val)) - return NULL; - - id ns_val = reinterpret_cast(const_cast(cf_val)); - if ([ns_val isKindOfClass:[NSFont class]]) { - return (CTFontRef)(cf_val); - } - return NULL; -} - -template<> CTFontRef -CFCastStrict(const CFTypeRef& cf_val) { - CTFontRef rv = CFCast(cf_val); - DCHECK(cf_val == NULL || rv); - return rv; -} -#endif - -#if !defined(OS_IOS) -CF_CAST_DEFN(SecACL); -CF_CAST_DEFN(SecKey); -CF_CAST_DEFN(SecPolicy); -CF_CAST_DEFN(SecTrustedApplication); -#endif - -#undef CF_CAST_DEFN - -std::string GetValueFromDictionaryErrorMessage( - CFStringRef key, const std::string& expected_type, CFTypeRef value) { - ScopedCFTypeRef actual_type_ref( - CFCopyTypeIDDescription(CFGetTypeID(value))); - return "Expected value for key " + - base::SysCFStringRefToUTF8(key) + - " to be " + - expected_type + - " but it was " + - base::SysCFStringRefToUTF8(actual_type_ref) + - " instead"; -} - -NSString* FilePathToNSString(const FilePath& path) { - if (path.empty()) - return nil; - return @(path.value().c_str()); // @() does UTF8 conversion. -} - -FilePath NSStringToFilePath(NSString* str) { - if (![str length]) - return FilePath(); - return FilePath([str fileSystemRepresentation]); -} - -bool CFRangeToNSRange(CFRange range, NSRange* range_out) { - if (base::IsValueInRangeForNumericTypelocation)>( - range.location) && - base::IsValueInRangeForNumericTypelength)>( - range.length) && - base::IsValueInRangeForNumericTypelocation)>( - range.location + range.length)) { - *range_out = NSMakeRange(range.location, range.length); - return true; - } - return false; -} - -} // namespace mac -} // namespace base - -std::ostream& operator<<(std::ostream& o, const CFStringRef string) { - return o << base::SysCFStringRefToUTF8(string); -} - -std::ostream& operator<<(std::ostream& o, const CFErrorRef err) { - base::ScopedCFTypeRef desc(CFErrorCopyDescription(err)); - base::ScopedCFTypeRef user_info(CFErrorCopyUserInfo(err)); - CFStringRef errorDesc = NULL; - if (user_info.get()) { - errorDesc = reinterpret_cast( - CFDictionaryGetValue(user_info.get(), kCFErrorDescriptionKey)); - } - o << "Code: " << CFErrorGetCode(err) - << " Domain: " << CFErrorGetDomain(err) - << " Desc: " << desc.get(); - if(errorDesc) { - o << "(" << errorDesc << ")"; - } - return o; -} diff --git a/mac/foundation_util_unittest.mm b/mac/foundation_util_unittest.mm deleted file mode 100644 index a584094dd..000000000 --- a/mac/foundation_util_unittest.mm +++ /dev/null @@ -1,405 +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/mac/foundation_util.h" - -#include -#include - -#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/macros.h" -#include "base/strings/stringprintf.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" - -namespace base { -namespace mac { - -TEST(FoundationUtilTest, CFCast) { - // Build out the CF types to be tested as empty containers. - ScopedCFTypeRef test_array( - CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks)); - ScopedCFTypeRef test_array_mutable( - CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); - ScopedCFTypeRef test_bag( - CFBagCreate(NULL, NULL, 0, &kCFTypeBagCallBacks)); - ScopedCFTypeRef test_bag_mutable( - CFBagCreateMutable(NULL, 0, &kCFTypeBagCallBacks)); - CFTypeRef test_bool = kCFBooleanTrue; - ScopedCFTypeRef test_data( - CFDataCreate(NULL, NULL, 0)); - ScopedCFTypeRef test_data_mutable( - CFDataCreateMutable(NULL, 0)); - ScopedCFTypeRef test_date( - CFDateCreate(NULL, 0)); - ScopedCFTypeRef test_dict( - CFDictionaryCreate(NULL, NULL, NULL, 0, - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - ScopedCFTypeRef test_dict_mutable( - CFDictionaryCreateMutable(NULL, 0, - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - int int_val = 256; - ScopedCFTypeRef test_number( - CFNumberCreate(NULL, kCFNumberIntType, &int_val)); - CFTypeRef test_null = kCFNull; - ScopedCFTypeRef test_set( - CFSetCreate(NULL, NULL, 0, &kCFTypeSetCallBacks)); - ScopedCFTypeRef test_set_mutable( - CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks)); - ScopedCFTypeRef test_str( - CFStringCreateWithBytes(NULL, NULL, 0, kCFStringEncodingASCII, false)); - CFTypeRef test_str_const = CFSTR("hello"); - ScopedCFTypeRef test_str_mutable(CFStringCreateMutable(NULL, 0)); - - // Make sure the allocations of CF types are good. - EXPECT_TRUE(test_array); - EXPECT_TRUE(test_array_mutable); - EXPECT_TRUE(test_bag); - EXPECT_TRUE(test_bag_mutable); - EXPECT_TRUE(test_bool); - EXPECT_TRUE(test_data); - EXPECT_TRUE(test_data_mutable); - EXPECT_TRUE(test_date); - EXPECT_TRUE(test_dict); - EXPECT_TRUE(test_dict_mutable); - EXPECT_TRUE(test_number); - EXPECT_TRUE(test_null); - EXPECT_TRUE(test_set); - EXPECT_TRUE(test_set_mutable); - EXPECT_TRUE(test_str); - EXPECT_TRUE(test_str_const); - EXPECT_TRUE(test_str_mutable); - - // Casting the CFTypeRef objects correctly provides the same pointer. - EXPECT_EQ(test_array, CFCast(test_array)); - EXPECT_EQ(test_array_mutable, CFCast(test_array_mutable)); - EXPECT_EQ(test_bag, CFCast(test_bag)); - EXPECT_EQ(test_bag_mutable, CFCast(test_bag_mutable)); - EXPECT_EQ(test_bool, CFCast(test_bool)); - EXPECT_EQ(test_data, CFCast(test_data)); - EXPECT_EQ(test_data_mutable, CFCast(test_data_mutable)); - EXPECT_EQ(test_date, CFCast(test_date)); - EXPECT_EQ(test_dict, CFCast(test_dict)); - EXPECT_EQ(test_dict_mutable, CFCast(test_dict_mutable)); - EXPECT_EQ(test_number, CFCast(test_number)); - EXPECT_EQ(test_null, CFCast(test_null)); - EXPECT_EQ(test_set, CFCast(test_set)); - EXPECT_EQ(test_set_mutable, CFCast(test_set_mutable)); - EXPECT_EQ(test_str, CFCast(test_str)); - EXPECT_EQ(test_str_const, CFCast(test_str_const)); - EXPECT_EQ(test_str_mutable, CFCast(test_str_mutable)); - - // When given an incorrect CF cast, provide NULL. - EXPECT_FALSE(CFCast(test_array)); - EXPECT_FALSE(CFCast(test_array_mutable)); - EXPECT_FALSE(CFCast(test_bag)); - EXPECT_FALSE(CFCast(test_bag_mutable)); - EXPECT_FALSE(CFCast(test_bool)); - EXPECT_FALSE(CFCast(test_data)); - EXPECT_FALSE(CFCast(test_data_mutable)); - EXPECT_FALSE(CFCast(test_date)); - EXPECT_FALSE(CFCast(test_dict)); - EXPECT_FALSE(CFCast(test_dict_mutable)); - EXPECT_FALSE(CFCast(test_number)); - EXPECT_FALSE(CFCast(test_null)); - EXPECT_FALSE(CFCast(test_set)); - EXPECT_FALSE(CFCast(test_set_mutable)); - EXPECT_FALSE(CFCast(test_str)); - EXPECT_FALSE(CFCast(test_str_const)); - EXPECT_FALSE(CFCast(test_str_mutable)); - - // Giving a NULL provides a NULL. - EXPECT_FALSE(CFCast(NULL)); - EXPECT_FALSE(CFCast(NULL)); - EXPECT_FALSE(CFCast(NULL)); - EXPECT_FALSE(CFCast(NULL)); - EXPECT_FALSE(CFCast(NULL)); - EXPECT_FALSE(CFCast(NULL)); - EXPECT_FALSE(CFCast(NULL)); - EXPECT_FALSE(CFCast(NULL)); - EXPECT_FALSE(CFCast(NULL)); - EXPECT_FALSE(CFCast(NULL)); - - // CFCastStrict: correct cast results in correct pointer being returned. - EXPECT_EQ(test_array, CFCastStrict(test_array)); - EXPECT_EQ(test_array_mutable, CFCastStrict(test_array_mutable)); - EXPECT_EQ(test_bag, CFCastStrict(test_bag)); - EXPECT_EQ(test_bag_mutable, CFCastStrict(test_bag_mutable)); - EXPECT_EQ(test_bool, CFCastStrict(test_bool)); - EXPECT_EQ(test_data, CFCastStrict(test_data)); - EXPECT_EQ(test_data_mutable, CFCastStrict(test_data_mutable)); - EXPECT_EQ(test_date, CFCastStrict(test_date)); - EXPECT_EQ(test_dict, CFCastStrict(test_dict)); - EXPECT_EQ(test_dict_mutable, - CFCastStrict(test_dict_mutable)); - EXPECT_EQ(test_number, CFCastStrict(test_number)); - EXPECT_EQ(test_null, CFCastStrict(test_null)); - EXPECT_EQ(test_set, CFCastStrict(test_set)); - EXPECT_EQ(test_set_mutable, CFCastStrict(test_set_mutable)); - EXPECT_EQ(test_str, CFCastStrict(test_str)); - EXPECT_EQ(test_str_const, CFCastStrict(test_str_const)); - EXPECT_EQ(test_str_mutable, CFCastStrict(test_str_mutable)); - - // CFCastStrict: Giving a NULL provides a NULL. - EXPECT_FALSE(CFCastStrict(NULL)); - EXPECT_FALSE(CFCastStrict(NULL)); - EXPECT_FALSE(CFCastStrict(NULL)); - EXPECT_FALSE(CFCastStrict(NULL)); - EXPECT_FALSE(CFCastStrict(NULL)); - EXPECT_FALSE(CFCastStrict(NULL)); - EXPECT_FALSE(CFCastStrict(NULL)); - EXPECT_FALSE(CFCastStrict(NULL)); - EXPECT_FALSE(CFCastStrict(NULL)); - EXPECT_FALSE(CFCastStrict(NULL)); -} - -TEST(FoundationUtilTest, ObjCCast) { - ScopedNSAutoreleasePool pool; - - id test_array = @[]; - id test_array_mutable = [NSMutableArray array]; - id test_data = [NSData data]; - id test_data_mutable = [NSMutableData dataWithCapacity:10]; - id test_date = [NSDate date]; - id test_dict = @{ @"meaning" : @42 }; - id test_dict_mutable = [NSMutableDictionary dictionaryWithCapacity:10]; - id test_number = @42; - id test_null = [NSNull null]; - id test_set = [NSSet setWithObject:@"string object"]; - id test_set_mutable = [NSMutableSet setWithCapacity:10]; - id test_str = [NSString string]; - id test_str_const = @"bonjour"; - id test_str_mutable = [NSMutableString stringWithCapacity:10]; - - // Make sure the allocations of NS types are good. - EXPECT_TRUE(test_array); - EXPECT_TRUE(test_array_mutable); - EXPECT_TRUE(test_data); - EXPECT_TRUE(test_data_mutable); - EXPECT_TRUE(test_date); - EXPECT_TRUE(test_dict); - EXPECT_TRUE(test_dict_mutable); - EXPECT_TRUE(test_number); - EXPECT_TRUE(test_null); - EXPECT_TRUE(test_set); - EXPECT_TRUE(test_set_mutable); - EXPECT_TRUE(test_str); - EXPECT_TRUE(test_str_const); - EXPECT_TRUE(test_str_mutable); - - // Casting the id correctly provides the same pointer. - EXPECT_EQ(test_array, ObjCCast(test_array)); - EXPECT_EQ(test_array_mutable, ObjCCast(test_array_mutable)); - EXPECT_EQ(test_data, ObjCCast(test_data)); - EXPECT_EQ(test_data_mutable, ObjCCast(test_data_mutable)); - EXPECT_EQ(test_date, ObjCCast(test_date)); - EXPECT_EQ(test_dict, ObjCCast(test_dict)); - EXPECT_EQ(test_dict_mutable, ObjCCast(test_dict_mutable)); - EXPECT_EQ(test_number, ObjCCast(test_number)); - EXPECT_EQ(test_null, ObjCCast(test_null)); - EXPECT_EQ(test_set, ObjCCast(test_set)); - EXPECT_EQ(test_set_mutable, ObjCCast(test_set_mutable)); - EXPECT_EQ(test_str, ObjCCast(test_str)); - EXPECT_EQ(test_str_const, ObjCCast(test_str_const)); - EXPECT_EQ(test_str_mutable, ObjCCast(test_str_mutable)); - - // When given an incorrect ObjC cast, provide nil. - EXPECT_FALSE(ObjCCast(test_array)); - EXPECT_FALSE(ObjCCast(test_array_mutable)); - EXPECT_FALSE(ObjCCast(test_data)); - EXPECT_FALSE(ObjCCast(test_data_mutable)); - EXPECT_FALSE(ObjCCast(test_date)); - EXPECT_FALSE(ObjCCast(test_dict)); - EXPECT_FALSE(ObjCCast(test_dict_mutable)); - EXPECT_FALSE(ObjCCast(test_number)); - EXPECT_FALSE(ObjCCast(test_null)); - EXPECT_FALSE(ObjCCast(test_set)); - EXPECT_FALSE(ObjCCast(test_set_mutable)); - EXPECT_FALSE(ObjCCast(test_str)); - EXPECT_FALSE(ObjCCast(test_str_const)); - EXPECT_FALSE(ObjCCast(test_str_mutable)); - - // Giving a nil provides a nil. - EXPECT_FALSE(ObjCCast(nil)); - EXPECT_FALSE(ObjCCast(nil)); - EXPECT_FALSE(ObjCCast(nil)); - EXPECT_FALSE(ObjCCast(nil)); - EXPECT_FALSE(ObjCCast(nil)); - EXPECT_FALSE(ObjCCast(nil)); - EXPECT_FALSE(ObjCCast(nil)); - EXPECT_FALSE(ObjCCast(nil)); - - // ObjCCastStrict: correct cast results in correct pointer being returned. - EXPECT_EQ(test_array, ObjCCastStrict(test_array)); - EXPECT_EQ(test_array_mutable, - ObjCCastStrict(test_array_mutable)); - EXPECT_EQ(test_data, ObjCCastStrict(test_data)); - EXPECT_EQ(test_data_mutable, - ObjCCastStrict(test_data_mutable)); - EXPECT_EQ(test_date, ObjCCastStrict(test_date)); - EXPECT_EQ(test_dict, ObjCCastStrict(test_dict)); - EXPECT_EQ(test_dict_mutable, - ObjCCastStrict(test_dict_mutable)); - EXPECT_EQ(test_number, ObjCCastStrict(test_number)); - EXPECT_EQ(test_null, ObjCCastStrict(test_null)); - EXPECT_EQ(test_set, ObjCCastStrict(test_set)); - EXPECT_EQ(test_set_mutable, - ObjCCastStrict(test_set_mutable)); - EXPECT_EQ(test_str, ObjCCastStrict(test_str)); - EXPECT_EQ(test_str_const, - ObjCCastStrict(test_str_const)); - EXPECT_EQ(test_str_mutable, - ObjCCastStrict(test_str_mutable)); - - // ObjCCastStrict: Giving a nil provides a nil. - EXPECT_FALSE(ObjCCastStrict(nil)); - EXPECT_FALSE(ObjCCastStrict(nil)); - EXPECT_FALSE(ObjCCastStrict(nil)); - EXPECT_FALSE(ObjCCastStrict(nil)); - EXPECT_FALSE(ObjCCastStrict(nil)); - EXPECT_FALSE(ObjCCastStrict(nil)); - EXPECT_FALSE(ObjCCastStrict(nil)); - EXPECT_FALSE(ObjCCastStrict(nil)); -} - -TEST(FoundationUtilTest, GetValueFromDictionary) { - int one = 1, two = 2, three = 3; - - ScopedCFTypeRef cf_one( - CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &one)); - ScopedCFTypeRef cf_two( - CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &two)); - ScopedCFTypeRef cf_three( - CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &three)); - - CFStringRef keys[] = { CFSTR("one"), CFSTR("two"), CFSTR("three") }; - CFNumberRef values[] = { cf_one, cf_two, cf_three }; - - static_assert(arraysize(keys) == arraysize(values), - "keys and values arrays must have the same size"); - - ScopedCFTypeRef test_dict( - CFDictionaryCreate(kCFAllocatorDefault, - reinterpret_cast(keys), - reinterpret_cast(values), - arraysize(values), - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks)); - - // GetValueFromDictionary<>(_, _) should produce the correct - // expected output. - EXPECT_EQ(values[0], - GetValueFromDictionary(test_dict, CFSTR("one"))); - EXPECT_EQ(values[1], - GetValueFromDictionary(test_dict, CFSTR("two"))); - EXPECT_EQ(values[2], - GetValueFromDictionary(test_dict, CFSTR("three"))); - - // Bad input should produce bad output. - EXPECT_FALSE(GetValueFromDictionary(test_dict, CFSTR("four"))); - EXPECT_FALSE(GetValueFromDictionary(test_dict, CFSTR("one"))); -} - -TEST(FoundationUtilTest, FilePathToNSString) { - EXPECT_NSEQ(nil, FilePathToNSString(FilePath())); - EXPECT_NSEQ(@"/a/b", FilePathToNSString(FilePath("/a/b"))); -} - -TEST(FoundationUtilTest, NSStringToFilePath) { - EXPECT_EQ(FilePath(), NSStringToFilePath(nil)); - EXPECT_EQ(FilePath(), NSStringToFilePath(@"")); - EXPECT_EQ(FilePath("/a/b"), NSStringToFilePath(@"/a/b")); -} - -TEST(FoundationUtilTest, CFRangeToNSRange) { - NSRange range_out; - EXPECT_TRUE(CFRangeToNSRange(CFRangeMake(10, 5), &range_out)); - EXPECT_EQ(10UL, range_out.location); - EXPECT_EQ(5UL, range_out.length); - EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(-1, 5), &range_out)); - EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(5, -1), &range_out)); - EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(-1, -1), &range_out)); - EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(LONG_MAX, LONG_MAX), &range_out)); - EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(LONG_MIN, LONG_MAX), &range_out)); -} - -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 = &some_nsinteger; - ALLOW_UNUSED_LOCAL(pointer_to_some_nsinteger); - - NSUInteger some_nsuinteger; - FormatNSUIntegerAsType* pointer_to_some_nsuinteger = &some_nsuinteger; - ALLOW_UNUSED_LOCAL(pointer_to_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/mac/launch_services_util.h b/mac/launch_services_util.h deleted file mode 100644 index 30d1eec12..000000000 --- a/mac/launch_services_util.h +++ /dev/null @@ -1,31 +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_MAC_LAUNCH_SERVICES_UTIL_H_ -#define BASE_MAC_LAUNCH_SERVICES_UTIL_H_ - -#import - -#include "base/base_export.h" -#include "base/command_line.h" -#include "base/files/file_path.h" -#include "base/process/process.h" - -namespace base { -namespace mac { - -// Launches the application bundle at |bundle_path|, passing argv[1..] from -// |command_line| as command line arguments if the app isn't already running. -// |launch_options| are passed directly to -// -[NSWorkspace launchApplicationAtURL:options:configuration:error:]. -// Returns a valid process if the app was successfully launched. -BASE_EXPORT Process -OpenApplicationWithPath(const FilePath& bundle_path, - const CommandLine& command_line, - NSWorkspaceLaunchOptions launch_options); - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_LAUNCH_SERVICES_UTIL_H_ diff --git a/mac/launch_services_util.mm b/mac/launch_services_util.mm deleted file mode 100644 index fa6e8087e..000000000 --- a/mac/launch_services_util.mm +++ /dev/null @@ -1,52 +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. - -#import "base/mac/launch_services_util.h" - -#include "base/logging.h" -#include "base/strings/sys_string_conversions.h" - -namespace base { -namespace mac { - -Process OpenApplicationWithPath(const base::FilePath& bundle_path, - const CommandLine& command_line, - NSWorkspaceLaunchOptions launch_options) { - NSString* bundle_url_spec = base::SysUTF8ToNSString(bundle_path.value()); - NSURL* bundle_url = [NSURL fileURLWithPath:bundle_url_spec isDirectory:YES]; - DCHECK(bundle_url); - if (!bundle_url) { - return Process(); - } - - // NSWorkspace automatically adds the binary path as the first argument and - // it should not be included into the list. - std::vector argv = command_line.argv(); - int argc = argv.size(); - NSMutableArray* launch_args = [NSMutableArray arrayWithCapacity:argc - 1]; - for (int i = 1; i < argc; ++i) { - [launch_args addObject:base::SysUTF8ToNSString(argv[i])]; - } - - NSDictionary* configuration = @{ - NSWorkspaceLaunchConfigurationArguments : launch_args, - }; - NSError* launch_error = nil; - // TODO(jeremya): this opens a new browser window if Chrome is already - // running without any windows open. - NSRunningApplication* app = - [[NSWorkspace sharedWorkspace] launchApplicationAtURL:bundle_url - options:launch_options - configuration:configuration - error:&launch_error]; - if (launch_error) { - LOG(ERROR) << base::SysNSStringToUTF8([launch_error localizedDescription]); - return Process(); - } - DCHECK(app); - return Process([app processIdentifier]); -} - -} // namespace mac -} // namespace base diff --git a/mac/launchd.cc b/mac/launchd.cc deleted file mode 100644 index 0337d2e60..000000000 --- a/mac/launchd.cc +++ /dev/null @@ -1,75 +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/mac/launchd.h" - -#include "base/logging.h" -#include "base/mac/scoped_launch_data.h" - -namespace base { -namespace mac { - -// MessageForJob sends a single message to launchd with a simple dictionary -// mapping |operation| to |job_label|, and returns the result of calling -// launch_msg to send that message. On failure, returns NULL. The caller -// assumes ownership of the returned launch_data_t object. -launch_data_t MessageForJob(const std::string& job_label, - const char* operation) { - // launch_data_alloc returns something that needs to be freed. - ScopedLaunchData message(launch_data_alloc(LAUNCH_DATA_DICTIONARY)); - if (!message.is_valid()) { - LOG(ERROR) << "launch_data_alloc"; - return NULL; - } - - // launch_data_new_string returns something that needs to be freed, but - // the dictionary will assume ownership when launch_data_dict_insert is - // called, so put it in a scoper and .release() it when given to the - // dictionary. - ScopedLaunchData job_label_launchd(launch_data_new_string(job_label.c_str())); - if (!job_label_launchd.is_valid()) { - LOG(ERROR) << "launch_data_new_string"; - return NULL; - } - - if (!launch_data_dict_insert(message.get(), job_label_launchd.release(), - operation)) { - return NULL; - } - - return launch_msg(message.get()); -} - -pid_t PIDForJob(const std::string& job_label) { - ScopedLaunchData response(MessageForJob(job_label, LAUNCH_KEY_GETJOB)); - if (!response.is_valid()) { - return -1; - } - - launch_data_type_t response_type = launch_data_get_type(response.get()); - if (response_type != LAUNCH_DATA_DICTIONARY) { - if (response_type == LAUNCH_DATA_ERRNO) { - LOG(ERROR) << "PIDForJob: error " - << launch_data_get_errno(response.get()); - } else { - LOG(ERROR) << "PIDForJob: expected dictionary, got " << response_type; - } - return -1; - } - - launch_data_t pid_data = - launch_data_dict_lookup(response.get(), LAUNCH_JOBKEY_PID); - if (!pid_data) - return 0; - - if (launch_data_get_type(pid_data) != LAUNCH_DATA_INTEGER) { - LOG(ERROR) << "PIDForJob: expected integer"; - return -1; - } - - return launch_data_get_integer(pid_data); -} - -} // namespace mac -} // namespace base diff --git a/mac/launchd.h b/mac/launchd.h deleted file mode 100644 index 9e4514e83..000000000 --- a/mac/launchd.h +++ /dev/null @@ -1,34 +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_MAC_LAUNCHD_H_ -#define BASE_MAC_LAUNCHD_H_ - -#include -#include - -#include - -#include "base/base_export.h" - -namespace base { -namespace mac { - -// MessageForJob sends a single message to launchd with a simple dictionary -// mapping |operation| to |job_label|, and returns the result of calling -// launch_msg to send that message. On failure, returns NULL. The caller -// assumes ownership of the returned launch_data_t object. -BASE_EXPORT -launch_data_t MessageForJob(const std::string& job_label, - const char* operation); - -// Returns the process ID for |job_label| if the job is running, 0 if the job -// is loaded but not running, or -1 on error. -BASE_EXPORT -pid_t PIDForJob(const std::string& job_label); - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_LAUNCHD_H_ diff --git a/mac/mac_logging.h b/mac/mac_logging.h deleted file mode 100644 index 30e43ead2..000000000 --- a/mac/mac_logging.h +++ /dev/null @@ -1,98 +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_MAC_MAC_LOGGING_H_ -#define BASE_MAC_MAC_LOGGING_H_ - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/macros.h" -#include "build/build_config.h" - -#if defined(OS_IOS) -#include -#else -#include -#endif - -// Use the OSSTATUS_LOG family to log messages related to errors in Mac OS X -// system routines that report status via an OSStatus or OSErr value. It is -// similar to the PLOG family which operates on errno, but because there is no -// global (or thread-local) OSStatus or OSErr value, the specific error must -// be supplied as an argument to the OSSTATUS_LOG macro. The message logged -// will contain the symbolic constant name corresponding to the status value, -// along with the value itself. -// -// OSErr is just an older 16-bit form of the newer 32-bit OSStatus. Despite -// the name, OSSTATUS_LOG can be used equally well for OSStatus and OSErr. - -namespace logging { - -// Returns a UTF8 description from an OS X Status error. -BASE_EXPORT std::string DescriptionFromOSStatus(OSStatus err); - -class BASE_EXPORT OSStatusLogMessage : public logging::LogMessage { - public: - OSStatusLogMessage(const char* file_path, - int line, - LogSeverity severity, - OSStatus status); - ~OSStatusLogMessage(); - - private: - OSStatus status_; - - DISALLOW_COPY_AND_ASSIGN(OSStatusLogMessage); -}; - -} // 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) \ - logging::OSStatusLogMessage(__FILE__, __LINE__, \ - -verbose_level, status).stream() - -#define OSSTATUS_LOG(severity, status) \ - LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), LOG_IS_ON(severity)) -#define OSSTATUS_LOG_IF(severity, condition, status) \ - LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \ - LOG_IS_ON(severity) && (condition)) - -#define OSSTATUS_VLOG(verbose_level, status) \ - LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \ - VLOG_IS_ON(verbose_level)) -#define OSSTATUS_VLOG_IF(verbose_level, condition, status) \ - LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \ - VLOG_IS_ON(verbose_level) && (condition)) - -#define OSSTATUS_CHECK(condition, status) \ - LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), !(condition)) \ - << "Check failed: " # condition << ". " - -#define OSSTATUS_DLOG(severity, status) \ - LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), DLOG_IS_ON(severity)) -#define OSSTATUS_DLOG_IF(severity, condition, status) \ - LAZY_STREAM(OSSTATUS_LOG_STREAM(severity, status), \ - DLOG_IS_ON(severity) && (condition)) - -#define OSSTATUS_DVLOG(verbose_level, status) \ - 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_VLOG_STREAM(verbose_level, status), \ - MAC_DVLOG_IS_ON(verbose_level) && (condition)) - -#define OSSTATUS_DCHECK(condition, status) \ - LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), \ - DCHECK_IS_ON() && !(condition)) \ - << "Check failed: " #condition << ". " - -#endif // BASE_MAC_MAC_LOGGING_H_ diff --git a/mac/mac_logging.mm b/mac/mac_logging.mm deleted file mode 100644 index f0d3c07da..000000000 --- a/mac/mac_logging.mm +++ /dev/null @@ -1,47 +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/mac/mac_logging.h" - -#import - -#include - -#include "build/build_config.h" - -#if !defined(OS_IOS) -#include -#endif - -namespace logging { - -std::string DescriptionFromOSStatus(OSStatus err) { - NSError* error = - [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]; - return error.description.UTF8String; -} - -OSStatusLogMessage::OSStatusLogMessage(const char* file_path, - int line, - LogSeverity severity, - OSStatus status) - : LogMessage(file_path, line, severity), - status_(status) { -} - -OSStatusLogMessage::~OSStatusLogMessage() { -#if defined(OS_IOS) - // TODO(crbug.com/546375): Consider using NSError with NSOSStatusErrorDomain - // to try to get a description of the failure. - stream() << ": " << status_; -#else - stream() << ": " - << DescriptionFromOSStatus(status_) - << " (" - << status_ - << ")"; -#endif -} - -} // namespace logging diff --git a/mac/mac_util.h b/mac/mac_util.h deleted file mode 100644 index f596efb1e..000000000 --- a/mac/mac_util.h +++ /dev/null @@ -1,188 +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_MAC_MAC_UTIL_H_ -#define BASE_MAC_MAC_UTIL_H_ - -#include -#include - -#import - -#include "base/base_export.h" - -namespace base { - -class FilePath; - -namespace mac { - -// Full screen modes, in increasing order of priority. More permissive modes -// take predecence. -enum FullScreenMode { - kFullScreenModeHideAll = 0, - kFullScreenModeHideDock = 1, - kFullScreenModeAutoHideAll = 2, - kNumFullScreenModes = 3, - - // kFullScreenModeNormal is not a valid FullScreenMode, but it is useful to - // other classes, so we include it here. - kFullScreenModeNormal = 10, -}; - -// Returns an sRGB color space. The return value is a static value; do not -// release it! -BASE_EXPORT CGColorSpaceRef GetSRGBColorSpace(); - -// Returns the generic RGB color space. The return value is a static value; do -// not release it! -BASE_EXPORT CGColorSpaceRef GetGenericRGBColorSpace(); - -// Returns the color space being used by the main display. The return value -// is a static value; do not release it! -BASE_EXPORT CGColorSpaceRef GetSystemColorSpace(); - -// Add a full screen request for the given |mode|. Must be paired with a -// ReleaseFullScreen() call for the same |mode|. This does not by itself create -// a fullscreen window; rather, it manages per-application state related to -// hiding the dock and menubar. Must be called on the main thread. -BASE_EXPORT void RequestFullScreen(FullScreenMode mode); - -// Release a request for full screen mode. Must be matched with a -// RequestFullScreen() call for the same |mode|. As with RequestFullScreen(), -// this does not affect windows directly, but rather manages per-application -// state. For example, if there are no other outstanding -// |kFullScreenModeAutoHideAll| requests, this will reshow the menu bar. Must -// be called on main thread. -BASE_EXPORT void ReleaseFullScreen(FullScreenMode mode); - -// Convenience method to switch the current fullscreen mode. This has the same -// net effect as a ReleaseFullScreen(from_mode) call followed immediately by a -// RequestFullScreen(to_mode). Must be called on the main thread. -BASE_EXPORT void SwitchFullScreenModes(FullScreenMode from_mode, - FullScreenMode to_mode); - -// Excludes the file given by |file_path| from being backed up by Time Machine. -BASE_EXPORT bool SetFileBackupExclusion(const FilePath& file_path); - -// Checks if the current application is set as a Login Item, so it will launch -// on Login. If a non-NULL pointer to is_hidden is passed, the Login Item also -// is queried for the 'hide on launch' flag. -BASE_EXPORT bool CheckLoginItemStatus(bool* is_hidden); - -// Adds current application to the set of Login Items with specified "hide" -// flag. This has the same effect as adding/removing the application in -// SystemPreferences->Accounts->LoginItems or marking Application in the Dock -// as "Options->Open on Login". -// Does nothing if the application is already set up as Login Item with -// specified hide flag. -BASE_EXPORT void AddToLoginItems(bool hide_on_startup); - -// Removes the current application from the list Of Login Items. -BASE_EXPORT void RemoveFromLoginItems(); - -// Returns true if the current process was automatically launched as a -// 'Login Item' or via Lion's Resume. Used to suppress opening windows. -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(); - -// Remove the quarantine xattr from the given file. Returns false if there was -// an error, or true otherwise. -BASE_EXPORT bool RemoveQuarantineAttribute(const FilePath& file_path); - -namespace internal { - -// Returns the system's Mac OS X minor version. This is the |y| value -// in 10.y or 10.y.z. -BASE_EXPORT int MacOSXMinorVersion(); - -} // namespace internal - -// Run-time OS version checks. Use these instead of -// base::SysInfo::OperatingSystemVersionNumbers. Prefer the "AtLeast" and -// "AtMost" variants to those that check for a specific version, unless you -// know for sure that you need to check for a specific version. - -#define DEFINE_IS_OS_FUNCS(V, TEST_DEPLOYMENT_TARGET) \ - inline bool IsOS10_##V() { \ - TEST_DEPLOYMENT_TARGET(>, V, false) \ - return internal::MacOSXMinorVersion() == V; \ - } \ - inline bool IsAtLeastOS10_##V() { \ - TEST_DEPLOYMENT_TARGET(>=, V, true) \ - return internal::MacOSXMinorVersion() >= V; \ - } \ - inline bool IsAtMostOS10_##V() { \ - TEST_DEPLOYMENT_TARGET(>, V, false) \ - return internal::MacOSXMinorVersion() <= V; \ - } - -#define TEST_DEPLOYMENT_TARGET(OP, V, RET) \ - if (MAC_OS_X_VERSION_MIN_REQUIRED OP MAC_OS_X_VERSION_10_##V) \ - return RET; -#define IGNORE_DEPLOYMENT_TARGET(OP, V, RET) - -DEFINE_IS_OS_FUNCS(9, TEST_DEPLOYMENT_TARGET) -DEFINE_IS_OS_FUNCS(10, TEST_DEPLOYMENT_TARGET) - -#ifdef MAC_OS_X_VERSION_10_11 -DEFINE_IS_OS_FUNCS(11, TEST_DEPLOYMENT_TARGET) -#else -DEFINE_IS_OS_FUNCS(11, IGNORE_DEPLOYMENT_TARGET) -#endif - -#ifdef MAC_OS_X_VERSION_10_12 -DEFINE_IS_OS_FUNCS(12, TEST_DEPLOYMENT_TARGET) -#else -DEFINE_IS_OS_FUNCS(12, IGNORE_DEPLOYMENT_TARGET) -#endif - -#ifdef MAC_OS_X_VERSION_10_13 -DEFINE_IS_OS_FUNCS(13, TEST_DEPLOYMENT_TARGET) -#else -DEFINE_IS_OS_FUNCS(13, IGNORE_DEPLOYMENT_TARGET) -#endif - -#ifdef MAC_OS_X_VERSION_10_14 -DEFINE_IS_OS_FUNCS(14, TEST_DEPLOYMENT_TARGET) -#else -DEFINE_IS_OS_FUNCS(14, IGNORE_DEPLOYMENT_TARGET) -#endif - -#undef IGNORE_DEPLOYMENT_TARGET -#undef TEST_DEPLOYMENT_TARGET -#undef DEFINE_IS_OS_FUNCS - -// 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. -inline bool IsOSLaterThan10_14_DontCallThis() { - return !IsAtMostOS10_14(); -} - -// Retrieve the system's model identifier string from the IOKit registry: -// for example, "MacPro4,1", "MacBookPro6,1". Returns empty string upon -// failure. -BASE_EXPORT std::string GetModelIdentifier(); - -// Parse a model identifier string; for example, into ("MacBookPro", 6, 1). -// If any error occurs, none of the input pointers are touched. -BASE_EXPORT bool ParseModelIdentifier(const std::string& ident, - std::string* type, - int32_t* major, - int32_t* minor); - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_MAC_UTIL_H_ diff --git a/mac/mac_util.mm b/mac/mac_util.mm deleted file mode 100644 index 82b904701..000000000 --- a/mac/mac_util.mm +++ /dev/null @@ -1,485 +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/mac/mac_util.h" - -#import -#import -#include -#include -#include -#include -#include - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/mac/bundle_locations.h" -#include "base/mac/foundation_util.h" -#include "base/mac/mac_logging.h" -#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" - -namespace base { -namespace mac { - -namespace { - -// The current count of outstanding requests for full screen mode from browser -// windows, plugins, etc. -int g_full_screen_requests[kNumFullScreenModes] = { 0 }; - -// Sets the appropriate application presentation option based on the current -// full screen requests. Since only one presentation option can be active at a -// given time, full screen requests are ordered by priority. If there are no -// outstanding full screen requests, reverts to normal mode. If the correct -// presentation option is already set, does nothing. -void SetUIMode() { - NSApplicationPresentationOptions current_options = - [NSApp presentationOptions]; - - // Determine which mode should be active, based on which requests are - // currently outstanding. More permissive requests take precedence. For - // example, plugins request |kFullScreenModeAutoHideAll|, while browser - // windows request |kFullScreenModeHideDock| when the fullscreen overlay is - // down. Precedence goes to plugins in this case, so AutoHideAll wins over - // HideDock. - NSApplicationPresentationOptions desired_options = - NSApplicationPresentationDefault; - if (g_full_screen_requests[kFullScreenModeAutoHideAll] > 0) { - desired_options = NSApplicationPresentationHideDock | - NSApplicationPresentationAutoHideMenuBar; - } else if (g_full_screen_requests[kFullScreenModeHideDock] > 0) { - desired_options = NSApplicationPresentationHideDock; - } else if (g_full_screen_requests[kFullScreenModeHideAll] > 0) { - desired_options = NSApplicationPresentationHideDock | - NSApplicationPresentationHideMenuBar; - } - - // Mac OS X bug: if the window is fullscreened (Lion-style) and - // NSApplicationPresentationDefault is requested, the result is that the menu - // bar doesn't auto-hide. rdar://13576498 http://www.openradar.me/13576498 - // - // As a workaround, in that case, explicitly set the presentation options to - // the ones that are set by the system as it fullscreens a window. - if (desired_options == NSApplicationPresentationDefault && - current_options & NSApplicationPresentationFullScreen) { - desired_options |= NSApplicationPresentationFullScreen | - NSApplicationPresentationAutoHideMenuBar | - NSApplicationPresentationAutoHideDock; - } - - if (current_options != desired_options) - [NSApp setPresentationOptions:desired_options]; -} - -// Looks into Shared File Lists corresponding to Login Items for the item -// representing the current application. If such an item is found, returns a -// retained reference to it. Caller is responsible for releasing the reference. -LSSharedFileListItemRef GetLoginItemForApp() { - ScopedCFTypeRef login_items(LSSharedFileListCreate( - NULL, kLSSharedFileListSessionLoginItems, NULL)); - - if (!login_items.get()) { - DLOG(ERROR) << "Couldn't get a Login Items list."; - return NULL; - } - - base::scoped_nsobject login_items_array( - CFToNSCast(LSSharedFileListCopySnapshot(login_items, NULL))); - - NSURL* url = [NSURL fileURLWithPath:[base::mac::MainBundle() bundlePath]]; - - for(NSUInteger i = 0; i < [login_items_array count]; ++i) { - LSSharedFileListItemRef item = - reinterpret_cast(login_items_array[i]); - CFURLRef item_url_ref = NULL; - - // It seems that LSSharedFileListItemResolve() can return NULL in - // item_url_ref even if the function itself returns noErr. See - // https://crbug.com/760989 - if (LSSharedFileListItemResolve(item, 0, &item_url_ref, NULL) == noErr && - item_url_ref) { - ScopedCFTypeRef item_url(item_url_ref); - if (CFEqual(item_url, url)) { - CFRetain(item); - return item; - } - } - } - - return NULL; -} - -bool IsHiddenLoginItem(LSSharedFileListItemRef item) { - ScopedCFTypeRef hidden(reinterpret_cast( - LSSharedFileListItemCopyProperty(item, - reinterpret_cast(kLSSharedFileListLoginItemHidden)))); - - return hidden && hidden == kCFBooleanTrue; -} - -} // namespace - -CGColorSpaceRef GetGenericRGBColorSpace() { - // Leaked. That's OK, it's scoped to the lifetime of the application. - static CGColorSpaceRef g_color_space_generic_rgb( - CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); - DLOG_IF(ERROR, !g_color_space_generic_rgb) << - "Couldn't get the generic RGB color space"; - return g_color_space_generic_rgb; -} - -CGColorSpaceRef GetSRGBColorSpace() { - // Leaked. That's OK, it's scoped to the lifetime of the application. - static CGColorSpaceRef g_color_space_sRGB = - CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - DLOG_IF(ERROR, !g_color_space_sRGB) << "Couldn't get the sRGB color space"; - return g_color_space_sRGB; -} - -CGColorSpaceRef GetSystemColorSpace() { - // Leaked. That's OK, it's scoped to the lifetime of the application. - // Try to get the main display's color space. - static CGColorSpaceRef g_system_color_space = - CGDisplayCopyColorSpace(CGMainDisplayID()); - - if (!g_system_color_space) { - // Use a generic RGB color space. This is better than nothing. - g_system_color_space = CGColorSpaceCreateDeviceRGB(); - - if (g_system_color_space) { - DLOG(WARNING) << - "Couldn't get the main display's color space, using generic"; - } else { - DLOG(ERROR) << "Couldn't get any color space"; - } - } - - return g_system_color_space; -} - -// Add a request for full screen mode. Must be called on the main thread. -void RequestFullScreen(FullScreenMode mode) { - DCHECK_LT(mode, kNumFullScreenModes); - if (mode >= kNumFullScreenModes) - return; - - DCHECK_GE(g_full_screen_requests[mode], 0); - if (mode < 0) - return; - - g_full_screen_requests[mode] = std::max(g_full_screen_requests[mode] + 1, 1); - SetUIMode(); -} - -// Release a request for full screen mode. Must be called on the main thread. -void ReleaseFullScreen(FullScreenMode mode) { - DCHECK_LT(mode, kNumFullScreenModes); - if (mode >= kNumFullScreenModes) - return; - - DCHECK_GE(g_full_screen_requests[mode], 0); - if (mode < 0) - return; - - g_full_screen_requests[mode] = std::max(g_full_screen_requests[mode] - 1, 0); - SetUIMode(); -} - -// Switches full screen modes. Releases a request for |from_mode| and adds a -// new request for |to_mode|. Must be called on the main thread. -void SwitchFullScreenModes(FullScreenMode from_mode, FullScreenMode to_mode) { - DCHECK_LT(from_mode, kNumFullScreenModes); - DCHECK_LT(to_mode, kNumFullScreenModes); - if (from_mode >= kNumFullScreenModes || to_mode >= kNumFullScreenModes) - return; - - DCHECK_GT(g_full_screen_requests[from_mode], 0); - DCHECK_GE(g_full_screen_requests[to_mode], 0); - g_full_screen_requests[from_mode] = - std::max(g_full_screen_requests[from_mode] - 1, 0); - g_full_screen_requests[to_mode] = - std::max(g_full_screen_requests[to_mode] + 1, 1); - SetUIMode(); -} - -bool SetFileBackupExclusion(const FilePath& file_path) { - NSString* file_path_ns = - [NSString stringWithUTF8String:file_path.value().c_str()]; - NSURL* file_url = [NSURL fileURLWithPath:file_path_ns]; - - // When excludeByPath is true the application must be running with root - // privileges (admin for 10.6 and earlier) but the URL does not have to - // already exist. When excludeByPath is false the URL must already exist but - // can be used in non-root (or admin as above) mode. We use false so that - // non-root (or admin) users don't get their TimeMachine drive filled up with - // unnecessary backups. - OSStatus os_err = - CSBackupSetItemExcluded(base::mac::NSToCFCast(file_url), TRUE, FALSE); - if (os_err != noErr) { - OSSTATUS_DLOG(WARNING, os_err) - << "Failed to set backup exclusion for file '" - << file_path.value().c_str() << "'"; - } - return os_err == noErr; -} - -bool CheckLoginItemStatus(bool* is_hidden) { - ScopedCFTypeRef item(GetLoginItemForApp()); - if (!item.get()) - return false; - - if (is_hidden) - *is_hidden = IsHiddenLoginItem(item); - - return true; -} - -void AddToLoginItems(bool hide_on_startup) { - ScopedCFTypeRef item(GetLoginItemForApp()); - if (item.get() && (IsHiddenLoginItem(item) == hide_on_startup)) { - return; // Already is a login item with required hide flag. - } - - ScopedCFTypeRef login_items(LSSharedFileListCreate( - NULL, kLSSharedFileListSessionLoginItems, NULL)); - - if (!login_items.get()) { - DLOG(ERROR) << "Couldn't get a Login Items list."; - return; - } - - // Remove the old item, it has wrong hide flag, we'll create a new one. - if (item.get()) { - LSSharedFileListItemRemove(login_items, item); - } - - NSURL* url = [NSURL fileURLWithPath:[base::mac::MainBundle() bundlePath]]; - - BOOL hide = hide_on_startup ? YES : NO; - NSDictionary* properties = - [NSDictionary - dictionaryWithObject:[NSNumber numberWithBool:hide] - forKey:(NSString*)kLSSharedFileListLoginItemHidden]; - - ScopedCFTypeRef new_item; - new_item.reset(LSSharedFileListInsertItemURL( - login_items, kLSSharedFileListItemLast, NULL, NULL, - reinterpret_cast(url), - reinterpret_cast(properties), NULL)); - - if (!new_item.get()) { - DLOG(ERROR) << "Couldn't insert current app into Login Items list."; - } -} - -void RemoveFromLoginItems() { - ScopedCFTypeRef item(GetLoginItemForApp()); - if (!item.get()) - return; - - ScopedCFTypeRef login_items(LSSharedFileListCreate( - NULL, kLSSharedFileListSessionLoginItems, NULL)); - - if (!login_items.get()) { - DLOG(ERROR) << "Couldn't get a Login Items list."; - return; - } - - LSSharedFileListItemRemove(login_items, item); -} - -bool WasLaunchedAsLoginOrResumeItem() { - ProcessSerialNumber psn = { 0, kCurrentProcess }; - ProcessInfoRec info = {}; - info.processInfoLength = sizeof(info); - -// GetProcessInformation has been deprecated since macOS 10.9, but there is no -// replacement that provides the information we need. See -// https://crbug.com/650854. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - if (GetProcessInformation(&psn, &info) == noErr) { -#pragma clang diagnostic pop - ProcessInfoRec parent_info = {}; - parent_info.processInfoLength = sizeof(parent_info); -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - if (GetProcessInformation(&info.processLauncher, &parent_info) == noErr) { -#pragma clang diagnostic pop - return parent_info.processSignature == 'lgnw'; - } - } - return false; -} - -bool WasLaunchedAsLoginItemRestoreState() { - // "Reopen windows..." option was added for Lion. Prior OS versions should - // not have this behavior. - if (!WasLaunchedAsLoginOrResumeItem()) - return false; - - CFStringRef app = CFSTR("com.apple.loginwindow"); - CFStringRef save_state = CFSTR("TALLogoutSavesState"); - ScopedCFTypeRef 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(plist)) - return CFBooleanGetValue(restore_state); - - return false; -} - -bool WasLaunchedAsHiddenLoginItem() { - if (!WasLaunchedAsLoginOrResumeItem()) - return false; - - ScopedCFTypeRef item(GetLoginItemForApp()); - if (!item.get()) { - // OS X can launch items for the resume feature. - return false; - } - return IsHiddenLoginItem(item); -} - -bool RemoveQuarantineAttribute(const FilePath& file_path) { - const char kQuarantineAttrName[] = "com.apple.quarantine"; - int status = removexattr(file_path.value().c_str(), kQuarantineAttrName, 0); - return status == 0 || errno == ENOATTR; -} - -namespace { - -// Returns the running system's Darwin major version. Don't call this, it's -// an implementation detail and its result is meant to be cached by -// MacOSXMinorVersion. -int DarwinMajorVersionInternal() { - // base::OperatingSystemVersionNumbers calls Gestalt, which is a - // higher-level operation than is needed. It might perform unnecessary - // operations. On 10.6, it was observed to be able to spawn threads (see - // http://crbug.com/53200). It might also read files or perform other - // blocking operations. Actually, nobody really knows for sure just what - // Gestalt might do, or what it might be taught to do in the future. - // - // uname, on the other hand, is implemented as a simple series of sysctl - // system calls to obtain the relevant data from the kernel. The data is - // compiled right into the kernel, so no threads or blocking or other - // funny business is necessary. - - struct utsname uname_info; - if (uname(&uname_info) != 0) { - DPLOG(ERROR) << "uname"; - return 0; - } - - if (strcmp(uname_info.sysname, "Darwin") != 0) { - DLOG(ERROR) << "unexpected uname sysname " << uname_info.sysname; - return 0; - } - - int darwin_major_version = 0; - char* dot = strchr(uname_info.release, '.'); - if (dot) { - if (!base::StringToInt(base::StringPiece(uname_info.release, - dot - uname_info.release), - &darwin_major_version)) { - dot = NULL; - } - } - - if (!dot) { - DLOG(ERROR) << "could not parse uname release " << uname_info.release; - return 0; - } - - return darwin_major_version; -} - -// Returns the running system's Mac OS X minor version. This is the |y| value -// in 10.y or 10.y.z. Don't call this, it's an implementation detail and the -// result is meant to be cached by MacOSXMinorVersion. -int MacOSXMinorVersionInternal() { - int darwin_major_version = DarwinMajorVersionInternal(); - - // The Darwin major version is always 4 greater than the Mac OS X minor - // version for Darwin versions beginning with 6, corresponding to Mac OS X - // 10.2. Since this correspondence may change in the future, warn when - // encountering a version higher than anything seen before. Older Darwin - // versions, or versions that can't be determined, result in - // immediate death. - CHECK(darwin_major_version >= 6); - int mac_os_x_minor_version = darwin_major_version - 4; - DLOG_IF(WARNING, darwin_major_version > 18) - << "Assuming Darwin " << base::IntToString(darwin_major_version) - << " is macOS 10." << base::IntToString(mac_os_x_minor_version); - - return mac_os_x_minor_version; -} - -} // namespace - -namespace internal { -int MacOSXMinorVersion() { - static int mac_os_x_minor_version = MacOSXMinorVersionInternal(); - return mac_os_x_minor_version; -} -} // namespace internal - -std::string GetModelIdentifier() { - std::string return_string; - ScopedIOObject platform_expert( - IOServiceGetMatchingService(kIOMasterPortDefault, - IOServiceMatching("IOPlatformExpertDevice"))); - if (platform_expert) { - ScopedCFTypeRef model_data( - static_cast(IORegistryEntryCreateCFProperty( - platform_expert, - CFSTR("model"), - kCFAllocatorDefault, - 0))); - if (model_data) { - return_string = - reinterpret_cast(CFDataGetBytePtr(model_data)); - } - } - return return_string; -} - -bool ParseModelIdentifier(const std::string& ident, - std::string* type, - int32_t* major, - int32_t* minor) { - size_t number_loc = ident.find_first_of("0123456789"); - if (number_loc == std::string::npos) - return false; - size_t comma_loc = ident.find(',', number_loc); - if (comma_loc == std::string::npos) - return false; - int32_t major_tmp, minor_tmp; - std::string::const_iterator begin = ident.begin(); - if (!StringToInt( - StringPiece(begin + number_loc, begin + comma_loc), &major_tmp) || - !StringToInt( - StringPiece(begin + comma_loc + 1, ident.end()), &minor_tmp)) - return false; - *type = ident.substr(0, number_loc); - *major = major_tmp; - *minor = minor_tmp; - return true; -} - -} // namespace mac -} // namespace base diff --git a/mac/mac_util_unittest.mm b/mac/mac_util_unittest.mm deleted file mode 100644 index 6b13949b5..000000000 --- a/mac/mac_util_unittest.mm +++ /dev/null @@ -1,381 +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 -#include -#include - -#include "base/mac/mac_util.h" - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/mac/foundation_util.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/mac/scoped_nsobject.h" -#include "base/macros.h" -#include "base/sys_info.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -#include -#include - -namespace base { -namespace mac { - -namespace { - -typedef PlatformTest MacUtilTest; - -TEST_F(MacUtilTest, GetUserDirectoryTest) { - // Try a few keys, make sure they come back with non-empty paths. - FilePath caches_dir; - EXPECT_TRUE(GetUserDirectory(NSCachesDirectory, &caches_dir)); - EXPECT_FALSE(caches_dir.empty()); - - FilePath application_support_dir; - EXPECT_TRUE(GetUserDirectory(NSApplicationSupportDirectory, - &application_support_dir)); - EXPECT_FALSE(application_support_dir.empty()); - - FilePath library_dir; - EXPECT_TRUE(GetUserDirectory(NSLibraryDirectory, &library_dir)); - EXPECT_FALSE(library_dir.empty()); -} - -TEST_F(MacUtilTest, TestLibraryPath) { - FilePath library_dir = GetUserLibraryPath(); - // Make sure the string isn't empty. - EXPECT_FALSE(library_dir.value().empty()); -} - -TEST_F(MacUtilTest, TestGetAppBundlePath) { - FilePath out; - - // Make sure it doesn't crash. - out = GetAppBundlePath(FilePath()); - EXPECT_TRUE(out.empty()); - - // Some more invalid inputs. - const char* const invalid_inputs[] = { - "/", "/foo", "foo", "/foo/bar.", "foo/bar.", "/foo/bar./bazquux", - "foo/bar./bazquux", "foo/.app", "//foo", - }; - for (size_t i = 0; i < arraysize(invalid_inputs); i++) { - out = GetAppBundlePath(FilePath(invalid_inputs[i])); - EXPECT_TRUE(out.empty()) << "loop: " << i; - } - - // Some valid inputs; this and |expected_outputs| should be in sync. - struct { - const char *in; - const char *expected_out; - } valid_inputs[] = { - { "FooBar.app/", "FooBar.app" }, - { "/FooBar.app", "/FooBar.app" }, - { "/FooBar.app/", "/FooBar.app" }, - { "//FooBar.app", "//FooBar.app" }, - { "/Foo/Bar.app", "/Foo/Bar.app" }, - { "/Foo/Bar.app/", "/Foo/Bar.app" }, - { "/F/B.app", "/F/B.app" }, - { "/F/B.app/", "/F/B.app" }, - { "/Foo/Bar.app/baz", "/Foo/Bar.app" }, - { "/Foo/Bar.app/baz/", "/Foo/Bar.app" }, - { "/Foo/Bar.app/baz/quux.app/quuux", "/Foo/Bar.app" }, - { "/Applications/Google Foo.app/bar/Foo Helper.app/quux/Foo Helper", - "/Applications/Google Foo.app" }, - }; - for (size_t i = 0; i < arraysize(valid_inputs); i++) { - out = GetAppBundlePath(FilePath(valid_inputs[i].in)); - EXPECT_FALSE(out.empty()) << "loop: " << i; - EXPECT_STREQ(valid_inputs[i].expected_out, - out.value().c_str()) << "loop: " << i; - } -} - -// http://crbug.com/425745 -TEST_F(MacUtilTest, DISABLED_TestExcludeFileFromBackups) { - // The file must already exist in order to set its exclusion property. - ScopedTempDir temp_dir_; - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - FilePath dummy_file_path = temp_dir_.GetPath().Append("DummyFile"); - const char dummy_data[] = "All your base are belong to us!"; - // Dump something real into the file. - ASSERT_EQ(static_cast(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]; - // Initial state should be non-excluded. - EXPECT_FALSE(CSBackupIsItemExcluded(base::mac::NSToCFCast(fileURL), NULL)); - // Exclude the file. - EXPECT_TRUE(SetFileBackupExclusion(dummy_file_path)); - // SetFileBackupExclusion never excludes by path. - Boolean excluded_by_path = FALSE; - Boolean excluded = - CSBackupIsItemExcluded(base::mac::NSToCFCast(fileURL), &excluded_by_path); - EXPECT_TRUE(excluded); - EXPECT_FALSE(excluded_by_path); -} - -TEST_F(MacUtilTest, NSObjectRetainRelease) { - base::scoped_nsobject array( - [[NSArray alloc] initWithObjects:@"foo", nil]); - EXPECT_EQ(1U, [array retainCount]); - - NSObjectRetain(array); - EXPECT_EQ(2U, [array retainCount]); - - NSObjectRelease(array); - EXPECT_EQ(1U, [array retainCount]); -} - -TEST_F(MacUtilTest, IsOSEllipsis) { - int32_t major, minor, bugfix; - base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); - - // The patterns here are: - // - FALSE/FALSE/TRUE (it is not the earlier version, it is not "at most" the - // earlier version, it is "at least" the earlier version) - // - TRUE/TRUE/TRUE (it is the same version, it is "at most" the same version, - // it is "at least" the same version) - // - FALSE/TRUE/FALSE (it is not the later version, it is "at most" the later - // version, it is not "at least" the later version) - - // TODO(avi): Is there a better way to test this? Maybe with macros? Are - // macros a better way to test this? - - if (major == 10) { - if (minor == 9) { - EXPECT_TRUE(IsOS10_9()); - EXPECT_TRUE(IsAtMostOS10_9()); - EXPECT_TRUE(IsAtLeastOS10_9()); - - EXPECT_FALSE(IsOS10_10()); - EXPECT_TRUE(IsAtMostOS10_10()); - EXPECT_FALSE(IsAtLeastOS10_10()); - - EXPECT_FALSE(IsOS10_11()); - EXPECT_TRUE(IsAtMostOS10_11()); - EXPECT_FALSE(IsAtLeastOS10_11()); - - EXPECT_FALSE(IsOS10_12()); - EXPECT_TRUE(IsAtMostOS10_12()); - EXPECT_FALSE(IsAtLeastOS10_12()); - - EXPECT_FALSE(IsOS10_13()); - EXPECT_TRUE(IsAtMostOS10_13()); - EXPECT_FALSE(IsAtLeastOS10_13()); - - EXPECT_FALSE(IsOS10_14()); - EXPECT_TRUE(IsAtMostOS10_14()); - EXPECT_FALSE(IsAtLeastOS10_14()); - - EXPECT_FALSE(IsOSLaterThan10_14_DontCallThis()); - } else if (minor == 10) { - EXPECT_FALSE(IsOS10_9()); - EXPECT_FALSE(IsAtMostOS10_9()); - EXPECT_TRUE(IsAtLeastOS10_9()); - - EXPECT_TRUE(IsOS10_10()); - EXPECT_TRUE(IsAtMostOS10_10()); - EXPECT_TRUE(IsAtLeastOS10_10()); - - EXPECT_FALSE(IsOS10_11()); - EXPECT_TRUE(IsAtMostOS10_11()); - EXPECT_FALSE(IsAtLeastOS10_11()); - - EXPECT_FALSE(IsOS10_12()); - EXPECT_TRUE(IsAtMostOS10_12()); - EXPECT_FALSE(IsAtLeastOS10_12()); - - EXPECT_FALSE(IsOS10_13()); - EXPECT_TRUE(IsAtMostOS10_13()); - EXPECT_FALSE(IsAtLeastOS10_13()); - - EXPECT_FALSE(IsOS10_14()); - EXPECT_TRUE(IsAtMostOS10_14()); - EXPECT_FALSE(IsAtLeastOS10_14()); - - EXPECT_FALSE(IsOSLaterThan10_14_DontCallThis()); - } else if (minor == 11) { - EXPECT_FALSE(IsOS10_9()); - EXPECT_FALSE(IsAtMostOS10_9()); - EXPECT_TRUE(IsAtLeastOS10_9()); - - EXPECT_FALSE(IsOS10_10()); - EXPECT_FALSE(IsAtMostOS10_10()); - EXPECT_TRUE(IsAtLeastOS10_10()); - - EXPECT_TRUE(IsOS10_11()); - EXPECT_TRUE(IsAtMostOS10_11()); - EXPECT_TRUE(IsAtLeastOS10_11()); - - EXPECT_FALSE(IsOS10_12()); - EXPECT_TRUE(IsAtMostOS10_12()); - EXPECT_FALSE(IsAtLeastOS10_12()); - - EXPECT_FALSE(IsOS10_13()); - EXPECT_TRUE(IsAtMostOS10_13()); - EXPECT_FALSE(IsAtLeastOS10_13()); - - EXPECT_FALSE(IsOS10_14()); - EXPECT_TRUE(IsAtMostOS10_14()); - EXPECT_FALSE(IsAtLeastOS10_14()); - - EXPECT_FALSE(IsOSLaterThan10_14_DontCallThis()); - } else if (minor == 12) { - EXPECT_FALSE(IsOS10_9()); - EXPECT_FALSE(IsAtMostOS10_9()); - EXPECT_TRUE(IsAtLeastOS10_9()); - - EXPECT_FALSE(IsOS10_10()); - EXPECT_FALSE(IsAtMostOS10_10()); - EXPECT_TRUE(IsAtLeastOS10_10()); - - EXPECT_FALSE(IsOS10_11()); - EXPECT_FALSE(IsAtMostOS10_11()); - EXPECT_TRUE(IsAtLeastOS10_11()); - - EXPECT_TRUE(IsOS10_12()); - EXPECT_TRUE(IsAtMostOS10_12()); - EXPECT_TRUE(IsAtLeastOS10_12()); - - EXPECT_FALSE(IsOS10_13()); - EXPECT_TRUE(IsAtMostOS10_13()); - EXPECT_FALSE(IsAtLeastOS10_13()); - - EXPECT_FALSE(IsOS10_14()); - EXPECT_TRUE(IsAtMostOS10_14()); - EXPECT_FALSE(IsAtLeastOS10_14()); - - EXPECT_FALSE(IsOSLaterThan10_14_DontCallThis()); - } else if (minor == 13) { - EXPECT_FALSE(IsOS10_9()); - EXPECT_FALSE(IsAtMostOS10_9()); - EXPECT_TRUE(IsAtLeastOS10_9()); - - EXPECT_FALSE(IsOS10_10()); - EXPECT_FALSE(IsAtMostOS10_10()); - EXPECT_TRUE(IsAtLeastOS10_10()); - - EXPECT_FALSE(IsOS10_11()); - EXPECT_FALSE(IsAtMostOS10_11()); - EXPECT_TRUE(IsAtLeastOS10_11()); - - EXPECT_FALSE(IsOS10_12()); - EXPECT_FALSE(IsAtMostOS10_12()); - EXPECT_TRUE(IsAtLeastOS10_12()); - - EXPECT_TRUE(IsOS10_13()); - EXPECT_TRUE(IsAtMostOS10_13()); - EXPECT_TRUE(IsAtLeastOS10_13()); - - EXPECT_FALSE(IsOS10_14()); - EXPECT_TRUE(IsAtMostOS10_14()); - EXPECT_FALSE(IsAtLeastOS10_14()); - - EXPECT_FALSE(IsOSLaterThan10_14_DontCallThis()); - } else if (minor == 14) { - EXPECT_FALSE(IsOS10_9()); - EXPECT_FALSE(IsAtMostOS10_9()); - EXPECT_TRUE(IsAtLeastOS10_9()); - - EXPECT_FALSE(IsOS10_10()); - EXPECT_FALSE(IsAtMostOS10_10()); - EXPECT_TRUE(IsAtLeastOS10_10()); - - EXPECT_FALSE(IsOS10_11()); - EXPECT_FALSE(IsAtMostOS10_11()); - EXPECT_TRUE(IsAtLeastOS10_11()); - - EXPECT_FALSE(IsOS10_12()); - EXPECT_FALSE(IsAtMostOS10_12()); - EXPECT_TRUE(IsAtLeastOS10_12()); - - EXPECT_FALSE(IsOS10_13()); - EXPECT_FALSE(IsAtMostOS10_13()); - EXPECT_TRUE(IsAtLeastOS10_13()); - - EXPECT_TRUE(IsOS10_14()); - EXPECT_TRUE(IsAtMostOS10_14()); - EXPECT_TRUE(IsAtLeastOS10_14()); - - EXPECT_FALSE(IsOSLaterThan10_14_DontCallThis()); - } else { - // Not nine, ten, eleven, twelve, thirteen, or fourteen. Ah, ah, ah. - EXPECT_TRUE(false); - } - } else { - // Not ten. What you gonna do? - EXPECT_FALSE(true); - } -} - -TEST_F(MacUtilTest, ParseModelIdentifier) { - std::string model; - int32_t major = 1, minor = 2; - - EXPECT_FALSE(ParseModelIdentifier("", &model, &major, &minor)); - EXPECT_EQ(0U, model.length()); - EXPECT_EQ(1, major); - EXPECT_EQ(2, minor); - EXPECT_FALSE(ParseModelIdentifier("FooBar", &model, &major, &minor)); - - EXPECT_TRUE(ParseModelIdentifier("MacPro4,1", &model, &major, &minor)); - EXPECT_EQ(model, "MacPro"); - EXPECT_EQ(4, major); - EXPECT_EQ(1, minor); - - EXPECT_TRUE(ParseModelIdentifier("MacBookPro6,2", &model, &major, &minor)); - EXPECT_EQ(model, "MacBookPro"); - EXPECT_EQ(6, major); - EXPECT_EQ(2, minor); -} - -TEST_F(MacUtilTest, TestRemoveQuarantineAttribute) { - ScopedTempDir temp_dir_; - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - FilePath dummy_folder_path = temp_dir_.GetPath().Append("DummyFolder"); - ASSERT_TRUE(base::CreateDirectory(dummy_folder_path)); - const char* quarantine_str = "0000;4b392bb2;Chromium;|org.chromium.Chromium"; - const char* file_path_str = dummy_folder_path.value().c_str(); - EXPECT_EQ(0, setxattr(file_path_str, "com.apple.quarantine", - quarantine_str, strlen(quarantine_str), 0, 0)); - EXPECT_EQ(static_cast(strlen(quarantine_str)), - getxattr(file_path_str, "com.apple.quarantine", - NULL, 0, 0, 0)); - EXPECT_TRUE(RemoveQuarantineAttribute(dummy_folder_path)); - EXPECT_EQ(-1, getxattr(file_path_str, "com.apple.quarantine", NULL, 0, 0, 0)); - EXPECT_EQ(ENOATTR, errno); -} - -TEST_F(MacUtilTest, TestRemoveQuarantineAttributeTwice) { - ScopedTempDir temp_dir_; - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - FilePath dummy_folder_path = temp_dir_.GetPath().Append("DummyFolder"); - const char* file_path_str = dummy_folder_path.value().c_str(); - ASSERT_TRUE(base::CreateDirectory(dummy_folder_path)); - EXPECT_EQ(-1, getxattr(file_path_str, "com.apple.quarantine", NULL, 0, 0, 0)); - // No quarantine attribute to begin with, but RemoveQuarantineAttribute still - // succeeds because in the end the folder still doesn't have the quarantine - // attribute set. - EXPECT_TRUE(RemoveQuarantineAttribute(dummy_folder_path)); - EXPECT_TRUE(RemoveQuarantineAttribute(dummy_folder_path)); - EXPECT_EQ(ENOATTR, errno); -} - -TEST_F(MacUtilTest, TestRemoveQuarantineAttributeNonExistentPath) { - ScopedTempDir temp_dir_; - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - FilePath non_existent_path = temp_dir_.GetPath().Append("DummyPath"); - ASSERT_FALSE(PathExists(non_existent_path)); - EXPECT_FALSE(RemoveQuarantineAttribute(non_existent_path)); -} - -} // namespace - -} // namespace mac -} // namespace base diff --git a/mac/mach_logging.cc b/mac/mach_logging.cc deleted file mode 100644 index 7b939b3dd..000000000 --- a/mac/mach_logging.cc +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/mac/mach_logging.h" - -#include -#include - -#include "base/strings/stringprintf.h" -#include "build/build_config.h" - -#if !defined(OS_IOS) -#include -#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 - // . Otherwise, give it in hexadecimal to make it easier - // to visualize the various bits. See . - 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 . - 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/mac/mach_logging.h b/mac/mach_logging.h deleted file mode 100644 index 59ab762c3..000000000 --- a/mac/mach_logging.h +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MAC_MACH_LOGGING_H_ -#define BASE_MAC_MACH_LOGGING_H_ - -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/macros.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/mac/mach_port_broker.h b/mac/mach_port_broker.h deleted file mode 100644 index 4554b6aec..000000000 --- a/mac/mach_port_broker.h +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2016 The Chromium 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_PORT_BROKER_H_ -#define BASE_MAC_MACH_PORT_BROKER_H_ - -#include - -#include -#include -#include - -#include "base/base_export.h" -#include "base/mac/dispatch_source_mach.h" -#include "base/mac/scoped_mach_port.h" -#include "base/macros.h" -#include "base/process/port_provider_mac.h" -#include "base/process/process_handle.h" -#include "base/synchronization/lock.h" - -namespace base { - -// On OS X, the task port of a process is required to collect metrics about the -// process, and to insert Mach ports into the process. Running |task_for_pid()| -// is only allowed for privileged code. However, a process has port rights to -// all its subprocesses, so let the child processes send their Mach port to the -// parent over IPC. -// -// Mach ports can only be sent over Mach IPC, not over the |socketpair()| that -// the regular IPC system uses. Hence, the child processes opens a Mach -// connection shortly after launching and ipc their mach data to the parent -// process. A single |MachPortBroker| with a given name is expected to exist in -// the parent process. -// -// Since this data arrives over a separate channel, it is not available -// immediately after a child process has been started. -class BASE_EXPORT MachPortBroker : public base::PortProvider { - public: - // For use in child processes. This will send the task port of the current - // process over Mach IPC to the port registered by name (via this class) in - // the parent process. Returns true if the message was sent successfully - // and false if otherwise. - static bool ChildSendTaskPortToParent(const std::string& name); - - // Returns the Mach port name to use when sending or receiving messages. - // Does the Right Thing in the browser and in child processes. - static std::string GetMachPortName(const std::string& name, bool is_child); - - MachPortBroker(const std::string& name); - ~MachPortBroker() override; - - // Performs any initialization work. - bool Init(); - - // Adds a placeholder to the map for the given pid with MACH_PORT_NULL. - // Callers are expected to later update the port with FinalizePid(). Callers - // MUST acquire the lock given by GetLock() before calling this method (and - // release the lock afterwards). - void AddPlaceholderForPid(base::ProcessHandle pid); - - // Removes |pid| from the task port map. Callers MUST acquire the lock given - // by GetLock() before calling this method (and release the lock afterwards). - void InvalidatePid(base::ProcessHandle pid); - - // The lock that protects this MachPortBroker object. Callers MUST acquire - // and release this lock around calls to AddPlaceholderForPid(), - // InvalidatePid(), and FinalizePid(); - base::Lock& GetLock() { return lock_; } - - // Implement |base::PortProvider|. - mach_port_t TaskForPid(base::ProcessHandle process) const override; - - private: - friend class MachPortBrokerTest; - - // Message handler that is invoked on |dispatch_source_| when an - // incoming message needs to be received. - void HandleRequest(); - - // Updates the mapping for |pid| to include the given |mach_info|. Does - // nothing if PlaceholderForPid() has not already been called for the given - // |pid|. Callers MUST acquire the lock given by GetLock() before calling - // this method (and release the lock afterwards). - void FinalizePid(base::ProcessHandle pid, mach_port_t task_port); - - // Name used to identify a particular port broker. - const std::string name_; - - // The Mach port on which the server listens. - base::mac::ScopedMachReceiveRight server_port_; - - // The dispatch source and queue on which Mach messages will be received. - std::unique_ptr dispatch_source_; - - // Stores mach info for every process in the broker. - typedef std::map MachMap; - MachMap mach_map_; - - // Mutex that guards |mach_map_|. - mutable base::Lock lock_; - - DISALLOW_COPY_AND_ASSIGN(MachPortBroker); -}; - -} // namespace base - -#endif // BASE_MAC_MACH_PORT_BROKER_H_ diff --git a/mac/mach_port_broker.mm b/mac/mach_port_broker.mm deleted file mode 100644 index 6d9fec5ab..000000000 --- a/mac/mach_port_broker.mm +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2016 The Chromium 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_port_broker.h" - -#include -#include - -#include "base/logging.h" -#include "base/mac/foundation_util.h" -#include "base/mac/mach_logging.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" - -namespace base { - -namespace { - -// Mach message structure used in the child as a sending message. -struct MachPortBroker_ChildSendMsg { - mach_msg_header_t header; - mach_msg_body_t body; - mach_msg_port_descriptor_t child_task_port; -}; - -// Complement to the ChildSendMsg, this is used in the parent for receiving -// a message. Contains a message trailer with audit information. -struct MachPortBroker_ParentRecvMsg : public MachPortBroker_ChildSendMsg { - mach_msg_audit_trailer_t trailer; -}; - -} // namespace - -// static -bool MachPortBroker::ChildSendTaskPortToParent(const std::string& name) { - // Look up the named MachPortBroker port that's been registered with the - // bootstrap server. - mach_port_t parent_port; - kern_return_t kr = bootstrap_look_up(bootstrap_port, - const_cast(GetMachPortName(name, true).c_str()), &parent_port); - if (kr != KERN_SUCCESS) { - BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up"; - return false; - } - base::mac::ScopedMachSendRight scoped_right(parent_port); - - // Create the check in message. This will copy a send right on this process' - // (the child's) task port and send it to the parent. - MachPortBroker_ChildSendMsg msg; - bzero(&msg, sizeof(msg)); - msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND) | - MACH_MSGH_BITS_COMPLEX; - msg.header.msgh_remote_port = parent_port; - msg.header.msgh_size = sizeof(msg); - msg.body.msgh_descriptor_count = 1; - msg.child_task_port.name = mach_task_self(); - msg.child_task_port.disposition = MACH_MSG_TYPE_PORT_SEND; - msg.child_task_port.type = MACH_MSG_PORT_DESCRIPTOR; - - kr = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, sizeof(msg), - 0, MACH_PORT_NULL, 100 /*milliseconds*/, MACH_PORT_NULL); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "mach_msg"; - return false; - } - - return true; -} - -// static -std::string MachPortBroker::GetMachPortName(const std::string& name, - bool is_child) { - // In child processes, use the parent's pid. - const pid_t pid = is_child ? getppid() : getpid(); - return base::StringPrintf( - "%s.%s.%d", base::mac::BaseBundleID(), name.c_str(), pid); -} - -mach_port_t MachPortBroker::TaskForPid(base::ProcessHandle pid) const { - base::AutoLock lock(lock_); - MachPortBroker::MachMap::const_iterator it = mach_map_.find(pid); - if (it == mach_map_.end()) - return MACH_PORT_NULL; - return it->second; -} - -MachPortBroker::MachPortBroker(const std::string& name) : name_(name) {} - -MachPortBroker::~MachPortBroker() {} - -bool MachPortBroker::Init() { - DCHECK(server_port_.get() == MACH_PORT_NULL); - - // Check in with launchd and publish the service name. - mach_port_t port; - kern_return_t kr = bootstrap_check_in( - bootstrap_port, GetMachPortName(name_, false).c_str(), &port); - if (kr != KERN_SUCCESS) { - BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_check_in"; - return false; - } - server_port_.reset(port); - - // Start the dispatch source. - std::string queue_name = - base::StringPrintf("%s.MachPortBroker", base::mac::BaseBundleID()); - dispatch_source_.reset(new base::DispatchSourceMach( - queue_name.c_str(), server_port_.get(), ^{ HandleRequest(); })); - dispatch_source_->Resume(); - - return true; -} - -void MachPortBroker::AddPlaceholderForPid(base::ProcessHandle pid) { - lock_.AssertAcquired(); - DCHECK_EQ(0u, mach_map_.count(pid)); - mach_map_[pid] = MACH_PORT_NULL; -} - -void MachPortBroker::InvalidatePid(base::ProcessHandle pid) { - lock_.AssertAcquired(); - - MachMap::iterator mach_it = mach_map_.find(pid); - if (mach_it != mach_map_.end()) { - kern_return_t kr = mach_port_deallocate(mach_task_self(), mach_it->second); - MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; - mach_map_.erase(mach_it); - } -} - -void MachPortBroker::HandleRequest() { - MachPortBroker_ParentRecvMsg msg; - bzero(&msg, sizeof(msg)); - msg.header.msgh_size = sizeof(msg); - msg.header.msgh_local_port = server_port_.get(); - - const mach_msg_option_t options = MACH_RCV_MSG | - MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) | - MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT); - - kern_return_t kr = mach_msg(&msg.header, - options, - 0, - sizeof(msg), - server_port_.get(), - MACH_MSG_TIMEOUT_NONE, - MACH_PORT_NULL); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "mach_msg"; - return; - } - - // Use the kernel audit information to make sure this message is from - // a task that this process spawned. The kernel audit token contains the - // unspoofable pid of the task that sent the message. - pid_t child_pid = audit_token_to_pid(msg.trailer.msgh_audit); - mach_port_t child_task_port = msg.child_task_port.name; - - // Take the lock and update the broker information. - { - base::AutoLock lock(lock_); - FinalizePid(child_pid, child_task_port); - } - NotifyObservers(child_pid); -} - -void MachPortBroker::FinalizePid(base::ProcessHandle pid, - mach_port_t task_port) { - lock_.AssertAcquired(); - - MachMap::iterator it = mach_map_.find(pid); - if (it == mach_map_.end()) { - // Do nothing for unknown pids. - LOG(ERROR) << "Unknown process " << pid << " is sending Mach IPC messages!"; - return; - } - - DCHECK(it->second == MACH_PORT_NULL); - if (it->second == MACH_PORT_NULL) - it->second = task_port; -} - -} // namespace base diff --git a/mac/mach_port_broker_unittest.cc b/mac/mach_port_broker_unittest.cc deleted file mode 100644 index bff8eb6a9..000000000 --- a/mac/mach_port_broker_unittest.cc +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2016 The Chromium 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_port_broker.h" - -#include "base/command_line.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/waitable_event.h" -#include "base/test/multiprocess_test.h" -#include "base/test/test_timeouts.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/multiprocess_func_list.h" - -namespace base { - -namespace { -const char kBootstrapPortName[] = "thisisatest"; -} - -class MachPortBrokerTest : public testing::Test, - public base::PortProvider::Observer { - public: - MachPortBrokerTest() - : broker_(kBootstrapPortName), - event_(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED), - received_process_(kNullProcessHandle) { - broker_.AddObserver(this); - } - ~MachPortBrokerTest() override { - broker_.RemoveObserver(this); - } - - // Helper function to acquire/release locks and call |PlaceholderForPid()|. - void AddPlaceholderForPid(base::ProcessHandle pid) { - base::AutoLock lock(broker_.GetLock()); - broker_.AddPlaceholderForPid(pid); - } - - // Helper function to acquire/release locks and call |FinalizePid()|. - void FinalizePid(base::ProcessHandle pid, - mach_port_t task_port) { - base::AutoLock lock(broker_.GetLock()); - broker_.FinalizePid(pid, task_port); - } - - void WaitForTaskPort() { - event_.Wait(); - } - - // base::PortProvider::Observer: - void OnReceivedTaskPort(ProcessHandle process) override { - received_process_ = process; - event_.Signal(); - } - - protected: - MachPortBroker broker_; - WaitableEvent event_; - ProcessHandle received_process_; -}; - -TEST_F(MachPortBrokerTest, Locks) { - // Acquire and release the locks. Nothing bad should happen. - base::AutoLock lock(broker_.GetLock()); -} - -TEST_F(MachPortBrokerTest, AddPlaceholderAndFinalize) { - // Add a placeholder for PID 1. - AddPlaceholderForPid(1); - EXPECT_EQ(0u, broker_.TaskForPid(1)); - - // Finalize PID 1. - FinalizePid(1, 100u); - EXPECT_EQ(100u, broker_.TaskForPid(1)); - - // Should be no entry for PID 2. - EXPECT_EQ(0u, broker_.TaskForPid(2)); -} - -TEST_F(MachPortBrokerTest, FinalizeUnknownPid) { - // Finalizing an entry for an unknown pid should not add it to the map. - FinalizePid(1u, 100u); - EXPECT_EQ(0u, broker_.TaskForPid(1u)); -} - -MULTIPROCESS_TEST_MAIN(MachPortBrokerTestChild) { - CHECK(base::MachPortBroker::ChildSendTaskPortToParent(kBootstrapPortName)); - return 0; -} - -TEST_F(MachPortBrokerTest, ReceivePortFromChild) { - ASSERT_TRUE(broker_.Init()); - CommandLine command_line( - base::GetMultiProcessTestChildBaseCommandLine()); - broker_.GetLock().Acquire(); - base::Process test_child_process = base::SpawnMultiProcessTestChild( - "MachPortBrokerTestChild", command_line, LaunchOptions()); - broker_.AddPlaceholderForPid(test_child_process.Handle()); - broker_.GetLock().Release(); - - WaitForTaskPort(); - EXPECT_EQ(test_child_process.Handle(), received_process_); - - int rv = -1; - ASSERT_TRUE(test_child_process.WaitForExitWithTimeout( - TestTimeouts::action_timeout(), &rv)); - EXPECT_EQ(0, rv); - - EXPECT_NE(static_cast(MACH_PORT_NULL), - broker_.TaskForPid(test_child_process.Handle())); -} - -TEST_F(MachPortBrokerTest, ReceivePortFromChildWithoutAdding) { - ASSERT_TRUE(broker_.Init()); - CommandLine command_line( - base::GetMultiProcessTestChildBaseCommandLine()); - broker_.GetLock().Acquire(); - base::Process test_child_process = base::SpawnMultiProcessTestChild( - "MachPortBrokerTestChild", command_line, LaunchOptions()); - broker_.GetLock().Release(); - - int rv = -1; - ASSERT_TRUE(test_child_process.WaitForExitWithTimeout( - TestTimeouts::action_timeout(), &rv)); - EXPECT_EQ(0, rv); - - EXPECT_EQ(static_cast(MACH_PORT_NULL), - broker_.TaskForPid(test_child_process.Handle())); -} - -} // namespace base diff --git a/mac/mach_port_util.cc b/mac/mach_port_util.cc deleted file mode 100644 index 0eee21085..000000000 --- a/mac/mach_port_util.cc +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2016 The Chromium 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_port_util.h" - -#include "base/logging.h" - -namespace base { - -namespace { - -// Struct for sending a complex Mach message. -struct MachSendComplexMessage { - mach_msg_header_t header; - mach_msg_body_t body; - mach_msg_port_descriptor_t data; -}; - -// Struct for receiving a complex message. -struct MachReceiveComplexMessage { - mach_msg_header_t header; - mach_msg_body_t body; - mach_msg_port_descriptor_t data; - mach_msg_trailer_t trailer; -}; - -} // namespace - -kern_return_t SendMachPort(mach_port_t endpoint, - mach_port_t port_to_send, - int disposition) { - MachSendComplexMessage send_msg; - send_msg.header.msgh_bits = - MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0) | MACH_MSGH_BITS_COMPLEX; - send_msg.header.msgh_size = sizeof(send_msg); - send_msg.header.msgh_remote_port = endpoint; - send_msg.header.msgh_local_port = MACH_PORT_NULL; - send_msg.header.msgh_reserved = 0; - send_msg.header.msgh_id = 0; - send_msg.body.msgh_descriptor_count = 1; - send_msg.data.name = port_to_send; - send_msg.data.disposition = disposition; - send_msg.data.type = MACH_MSG_PORT_DESCRIPTOR; - - kern_return_t kr = - mach_msg(&send_msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, - send_msg.header.msgh_size, - 0, // receive limit - MACH_PORT_NULL, // receive name - 0, // timeout - MACH_PORT_NULL); // notification port - - if (kr != KERN_SUCCESS) - mach_port_deallocate(mach_task_self(), endpoint); - - return kr; -} - -base::mac::ScopedMachSendRight ReceiveMachPort(mach_port_t port_to_listen_on) { - MachReceiveComplexMessage recv_msg; - mach_msg_header_t* recv_hdr = &recv_msg.header; - recv_hdr->msgh_local_port = port_to_listen_on; - recv_hdr->msgh_size = sizeof(recv_msg); - - kern_return_t kr = - mach_msg(recv_hdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, - recv_hdr->msgh_size, port_to_listen_on, 0, MACH_PORT_NULL); - if (kr != KERN_SUCCESS) - return base::mac::ScopedMachSendRight(MACH_PORT_NULL); - if (recv_msg.header.msgh_id != 0) - return base::mac::ScopedMachSendRight(MACH_PORT_NULL); - return base::mac::ScopedMachSendRight(recv_msg.data.name); -} - -mach_port_name_t CreateIntermediateMachPort( - mach_port_t task_port, - base::mac::ScopedMachSendRight port_to_insert, - MachCreateError* error_code) { - DCHECK_NE(mach_task_self(), task_port); - DCHECK_NE(static_cast(MACH_PORT_NULL), task_port); - - // Make a port with receive rights in the destination task. - mach_port_name_t endpoint; - kern_return_t kr = - mach_port_allocate(task_port, MACH_PORT_RIGHT_RECEIVE, &endpoint); - if (kr != KERN_SUCCESS) { - if (error_code) - *error_code = MachCreateError::ERROR_MAKE_RECEIVE_PORT; - return MACH_PORT_NULL; - } - - // Change its message queue limit so that it accepts one message. - mach_port_limits limits = {}; - limits.mpl_qlimit = 1; - kr = mach_port_set_attributes(task_port, endpoint, MACH_PORT_LIMITS_INFO, - reinterpret_cast(&limits), - MACH_PORT_LIMITS_INFO_COUNT); - if (kr != KERN_SUCCESS) { - if (error_code) - *error_code = MachCreateError::ERROR_SET_ATTRIBUTES; - mach_port_deallocate(task_port, endpoint); - return MACH_PORT_NULL; - } - - // Get a send right. - mach_port_t send_once_right; - mach_msg_type_name_t send_right_type; - kr = - mach_port_extract_right(task_port, endpoint, MACH_MSG_TYPE_MAKE_SEND_ONCE, - &send_once_right, &send_right_type); - if (kr != KERN_SUCCESS) { - if (error_code) - *error_code = MachCreateError::ERROR_EXTRACT_DEST_RIGHT; - mach_port_deallocate(task_port, endpoint); - return MACH_PORT_NULL; - } - DCHECK_EQ(static_cast(MACH_MSG_TYPE_PORT_SEND_ONCE), - send_right_type); - - // This call takes ownership of |send_once_right|. - kr = base::SendMachPort( - send_once_right, port_to_insert.get(), MACH_MSG_TYPE_COPY_SEND); - if (kr != KERN_SUCCESS) { - if (error_code) - *error_code = MachCreateError::ERROR_SEND_MACH_PORT; - mach_port_deallocate(task_port, endpoint); - return MACH_PORT_NULL; - } - - // Endpoint is intentionally leaked into the destination task. An IPC must be - // sent to the destination task so that it can clean up this port. - return endpoint; -} - -} // namespace base diff --git a/mac/mach_port_util.h b/mac/mach_port_util.h deleted file mode 100644 index f7a7f325f..000000000 --- a/mac/mach_port_util.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2016 The Chromium 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_PORT_UTIL_H_ -#define BASE_MAC_MACH_PORT_UTIL_H_ - -#include - -#include "base/base_export.h" -#include "base/mac/scoped_mach_port.h" - -namespace base { - -enum class MachCreateError { - ERROR_MAKE_RECEIVE_PORT, - ERROR_SET_ATTRIBUTES, - ERROR_EXTRACT_DEST_RIGHT, - ERROR_SEND_MACH_PORT, -}; - -// Sends a Mach port to |dest_port|. Assumes that |dest_port| is a send once -// right. Takes ownership of |dest_port|. -BASE_EXPORT kern_return_t SendMachPort(mach_port_t dest_port, - mach_port_t port_to_send, - int disposition); - -// Receives a Mach port from |port_to_listen_on|, which should have exactly one -// queued message. Returns |MACH_PORT_NULL| on any error. -BASE_EXPORT base::mac::ScopedMachSendRight ReceiveMachPort( - mach_port_t port_to_listen_on); - -// Creates an intermediate Mach port in |task_port| and sends |port_to_insert| -// as a mach_msg to the intermediate Mach port. -// |task_port| is the task port of another process. -// |port_to_insert| must be a send right in the current task's name space. -// Returns the intermediate port on success, and MACH_PORT_NULL on failure. -// On failure, |error_code| is set if not null. -// This method takes ownership of |port_to_insert|. On success, ownership is -// passed to the intermediate Mach port. -BASE_EXPORT mach_port_name_t CreateIntermediateMachPort( - mach_port_t task_port, - base::mac::ScopedMachSendRight port_to_insert, - MachCreateError* error_code); - -} // namespace base - -#endif // BASE_MAC_MACH_PORT_UTIL_H_ diff --git a/mac/objc_release_properties.h b/mac/objc_release_properties.h deleted file mode 100644 index d064cf96e..000000000 --- a/mac/objc_release_properties.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2016 The Chromium 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(__has_feature) && __has_feature(objc_arc) -#error "ARC manages properties, so base::mac::ReleaseProperties isn't needed." -#endif - -#ifndef BASE_MAC_OBJC_RELEASE_PROPERTIES_H_ -#define BASE_MAC_OBJC_RELEASE_PROPERTIES_H_ - -#import - -#include "base/base_export.h" - -// base::mac::ReleaseProperties(self) can be used in a class's -dealloc method -// to release all properties marked "retain" or "copy" and backed by instance -// variables. It only affects properties defined by the calling class, not -// sub/superclass properties. -// -// Example usage: -// -// @interface AllaysIBF : NSObject -// -// @property(retain, nonatomic) NSString* string; -// @property(copy, nonatomic) NSMutableDictionary* dictionary; -// @property(assign, nonatomic) IBFDelegate* delegate; -// -// @end // @interface AllaysIBF -// -// @implementation AllaysIBF -// -// - (void)dealloc { -// base::mac::ReleaseProperties(self); -// [super dealloc]; -// } -// -// @end // @implementation AllaysIBF -// -// self.string and self.dictionary will each be released, but self.delegate -// will not because it is marked "assign", not "retain" or "copy". -// -// Another approach would be to provide a base class to inherit from whose -// -dealloc walks the property lists of all subclasses to release their -// properties. Distant subclasses might not expect it and over-release their -// properties, so don't do that. - -namespace base { -namespace mac { - -namespace details { - -BASE_EXPORT void ReleaseProperties(id, Class); - -} // namespace details - -template -void ReleaseProperties(Self* self) { - details::ReleaseProperties(self, [Self class]); -} - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_OBJC_RELEASE_PROPERTIES_H_ diff --git a/mac/objc_release_properties.mm b/mac/objc_release_properties.mm deleted file mode 100644 index d0006cfe3..000000000 --- a/mac/objc_release_properties.mm +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2016 The Chromium 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/objc_release_properties.h" - -#include - -#include - -#include "base/logging.h" -#include "base/memory/free_deleter.h" - -namespace { - -bool IsRetained(objc_property_t property) { - // The format of the string returned by property_getAttributes is documented - // at - // http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW6 - const char* attribute = property_getAttributes(property); - while (attribute[0]) { - switch (attribute[0]) { - case 'C': // copy - case '&': // retain - return true; - } - do { - attribute++; - } while (attribute[0] && attribute[-1] != ','); - } - return false; -} - -id ValueOf(id obj, objc_property_t property) { - std::unique_ptr ivar_name( - property_copyAttributeValue(property, "V")); // instance variable name - if (!ivar_name) - return nil; - id ivar_value = nil; - Ivar ivar = object_getInstanceVariable(obj, &*ivar_name, - reinterpret_cast(&ivar_value)); - DCHECK(ivar); - return ivar_value; -} - -} // namespace - -namespace base { -namespace mac { -namespace details { - -void ReleaseProperties(id self, Class cls) { - unsigned int property_count; - std::unique_ptr properties( - class_copyPropertyList(cls, &property_count)); - for (size_t i = 0; i < property_count; ++i) { - objc_property_t property = properties[i]; - if (!IsRetained(property)) - continue; - [ValueOf(self, property) release]; - } -} - -} // namespace details -} // namespace mac -} // namespace base diff --git a/mac/objc_release_properties_unittest.mm b/mac/objc_release_properties_unittest.mm deleted file mode 100644 index 2d9012792..000000000 --- a/mac/objc_release_properties_unittest.mm +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright 2016 The Chromium 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/objc_release_properties.h" -#include "base/stl_util.h" - -#import "base/mac/scoped_nsautorelease_pool.h" -#include "testing/gtest/include/gtest/gtest.h" - -#import - -// "When I'm alone, I count myself." -// --Count von Count, http://www.youtube.com/watch?v=FKzszqa9WA4 - -namespace { - -// The number of CountVonCounts outstanding. -int ah_ah_ah; - -// NumberHolder exists to exercise the property attribute string parser by -// providing a named struct and an anonymous union. -struct NumberHolder { - union { - long long sixty_four; - int thirty_two; - short sixteen; - char eight; - } what; - enum { SIXTY_FOUR, THIRTY_TWO, SIXTEEN, EIGHT } how; -}; - -} // namespace - -@interface CountVonCount : NSObject - -+ (CountVonCount*)countVonCount; - -@end // @interface CountVonCount - -@implementation CountVonCount - -+ (CountVonCount*)countVonCount { - return [[[CountVonCount alloc] init] autorelease]; -} - -- (id)init { - ++ah_ah_ah; - return [super init]; -} - -- (void)dealloc { - --ah_ah_ah; - [super dealloc]; -} - -- (id)copyWithZone:(NSZone*)zone { - return [[CountVonCount allocWithZone:zone] init]; -} - -@end // @implementation CountVonCount - -@interface ObjCPropertyTestBase : NSObject { - @private - CountVonCount* baseCvcRetain_; - CountVonCount* baseCvcCopy_; - CountVonCount* baseCvcAssign_; - CountVonCount* baseCvcNotProperty_; - CountVonCount* baseCvcNil_; - CountVonCount* baseCvcCustom_; - int baseInt_; - double baseDouble_; - void* basePointer_; - NumberHolder baseStruct_; -} - -@property(retain, nonatomic) CountVonCount* baseCvcRetain; -@property(copy, nonatomic) CountVonCount* baseCvcCopy; -@property(assign, nonatomic) CountVonCount* baseCvcAssign; -@property(retain, nonatomic) CountVonCount* baseCvcNil; -@property(retain, nonatomic, getter=baseCustom, setter=setBaseCustom:) - CountVonCount* baseCvcCustom; -@property(readonly, retain, nonatomic) CountVonCount* baseCvcReadOnly; -@property(retain, nonatomic) CountVonCount* baseCvcDynamic; -@property(assign, nonatomic) int baseInt; -@property(assign, nonatomic) double baseDouble; -@property(assign, nonatomic) void* basePointer; -@property(assign, nonatomic) NumberHolder baseStruct; - -- (void)setBaseCvcNotProperty:(CountVonCount*)cvc; - -@end // @interface ObjCPropertyTestBase - -@implementation ObjCPropertyTestBase - -@synthesize baseCvcRetain = baseCvcRetain_; -@synthesize baseCvcCopy = baseCvcCopy_; -@synthesize baseCvcAssign = baseCvcAssign_; -@synthesize baseCvcNil = baseCvcNil_; -@synthesize baseCvcCustom = baseCvcCustom_; -@synthesize baseCvcReadOnly = baseCvcReadOnly_; -@dynamic baseCvcDynamic; -@synthesize baseInt = baseInt_; -@synthesize baseDouble = baseDouble_; -@synthesize basePointer = basePointer_; -@synthesize baseStruct = baseStruct_; - -- (void)dealloc { - [baseCvcNotProperty_ release]; - base::mac::ReleaseProperties(self); - [super dealloc]; -} - -- (void)setBaseCvcNotProperty:(CountVonCount*)cvc { - if (cvc != baseCvcNotProperty_) { - [baseCvcNotProperty_ release]; - baseCvcNotProperty_ = [cvc retain]; - } -} - -- (void)setBaseCvcReadOnlyProperty:(CountVonCount*)cvc { - if (cvc != baseCvcReadOnly_) { - [baseCvcReadOnly_ release]; - baseCvcReadOnly_ = [cvc retain]; - } -} - -@end // @implementation ObjCPropertyTestBase - -@protocol ObjCPropertyTestProtocol - -@property(retain, nonatomic) CountVonCount* protoCvcRetain; -@property(copy, nonatomic) CountVonCount* protoCvcCopy; -@property(assign, nonatomic) CountVonCount* protoCvcAssign; -@property(retain, nonatomic) CountVonCount* protoCvcNil; -@property(retain, nonatomic, getter=protoCustom, setter=setProtoCustom:) - CountVonCount* protoCvcCustom; -@property(retain, nonatomic) CountVonCount* protoCvcDynamic; -@property(assign, nonatomic) int protoInt; -@property(assign, nonatomic) double protoDouble; -@property(assign, nonatomic) void* protoPointer; -@property(assign, nonatomic) NumberHolder protoStruct; - -@end // @protocol ObjCPropertyTestProtocol - -// @protocol(NSObject) declares some (copy, readonly) properties (superclass, -// description, debugDescription, and hash), but we're not expected to release -// them. The current implementation only releases properties backed by instance -// variables, and this makes sure that doesn't change in a breaking way. -@interface ObjCPropertyTestDerived - : ObjCPropertyTestBase { - @private - CountVonCount* derivedCvcRetain_; - CountVonCount* derivedCvcCopy_; - CountVonCount* derivedCvcAssign_; - CountVonCount* derivedCvcNotProperty_; - CountVonCount* derivedCvcNil_; - CountVonCount* derivedCvcCustom_; - int derivedInt_; - double derivedDouble_; - void* derivedPointer_; - NumberHolder derivedStruct_; - - CountVonCount* protoCvcRetain_; - CountVonCount* protoCvcCopy_; - CountVonCount* protoCvcAssign_; - CountVonCount* protoCvcNil_; - CountVonCount* protoCvcCustom_; - int protoInt_; - double protoDouble_; - void* protoPointer_; - NumberHolder protoStruct_; -} - -@property(retain, nonatomic) CountVonCount* derivedCvcRetain; -@property(copy, nonatomic) CountVonCount* derivedCvcCopy; -@property(assign, nonatomic) CountVonCount* derivedCvcAssign; -@property(retain, nonatomic) CountVonCount* derivedCvcNil; -@property(retain, nonatomic, getter=derivedCustom, setter=setDerivedCustom:) - CountVonCount* derivedCvcCustom; -@property(retain, nonatomic) CountVonCount* derivedCvcDynamic; -@property(assign, nonatomic) int derivedInt; -@property(assign, nonatomic) double derivedDouble; -@property(assign, nonatomic) void* derivedPointer; -@property(assign, nonatomic) NumberHolder derivedStruct; - -- (void)setDerivedCvcNotProperty:(CountVonCount*)cvc; - -@end // @interface ObjCPropertyTestDerived - -@implementation ObjCPropertyTestDerived - -@synthesize derivedCvcRetain = derivedCvcRetain_; -@synthesize derivedCvcCopy = derivedCvcCopy_; -@synthesize derivedCvcAssign = derivedCvcAssign_; -@synthesize derivedCvcNil = derivedCvcNil_; -@synthesize derivedCvcCustom = derivedCvcCustom_; -@dynamic derivedCvcDynamic; -@synthesize derivedInt = derivedInt_; -@synthesize derivedDouble = derivedDouble_; -@synthesize derivedPointer = derivedPointer_; -@synthesize derivedStruct = derivedStruct_; - -@synthesize protoCvcRetain = protoCvcRetain_; -@synthesize protoCvcCopy = protoCvcCopy_; -@synthesize protoCvcAssign = protoCvcAssign_; -@synthesize protoCvcNil = protoCvcNil_; -@synthesize protoCvcCustom = protoCvcCustom_; -@dynamic protoCvcDynamic; -@synthesize protoInt = protoInt_; -@synthesize protoDouble = protoDouble_; -@synthesize protoPointer = protoPointer_; -@synthesize protoStruct = protoStruct_; - -+ (BOOL)resolveInstanceMethod:(SEL)sel { - static const std::vector dynamicMethods { - @selector(baseCvcDynamic), @selector(derivedCvcDynamic), - @selector(protoCvcDynamic), - }; - if (!base::ContainsValue(dynamicMethods, sel)) { - return NO; - } - id (*imp)() = []() -> id { return nil; }; - class_addMethod([self class], sel, reinterpret_cast(imp), "@@:"); - return YES; -} - -- (void)dealloc { - base::mac::ReleaseProperties(self); - [derivedCvcNotProperty_ release]; - [super dealloc]; -} - -- (void)setDerivedCvcNotProperty:(CountVonCount*)cvc { - if (cvc != derivedCvcNotProperty_) { - [derivedCvcNotProperty_ release]; - derivedCvcNotProperty_ = [cvc retain]; - } -} - -@end // @implementation ObjCPropertyTestDerived - -@interface ObjcPropertyTestEmpty : NSObject -@end - -@implementation ObjcPropertyTestEmpty - -- (void)dealloc { - base::mac::ReleaseProperties(self); - [super dealloc]; -} - -@end // @implementation ObjcPropertyTestEmpty - -namespace { - -TEST(ObjCReleasePropertiesTest, SesameStreet) { - ObjCPropertyTestDerived* test_object = [[ObjCPropertyTestDerived alloc] init]; - - // Assure a clean slate. - EXPECT_EQ(0, ah_ah_ah); - EXPECT_EQ(1U, [test_object retainCount]); - - CountVonCount* baseAssign = [[CountVonCount alloc] init]; - CountVonCount* derivedAssign = [[CountVonCount alloc] init]; - CountVonCount* protoAssign = [[CountVonCount alloc] init]; - - // Make sure that worked before things get more involved. - EXPECT_EQ(3, ah_ah_ah); - - { - base::mac::ScopedNSAutoreleasePool pool; - - test_object.baseCvcRetain = [CountVonCount countVonCount]; - test_object.baseCvcCopy = [CountVonCount countVonCount]; - test_object.baseCvcAssign = baseAssign; - test_object.baseCvcCustom = [CountVonCount countVonCount]; - [test_object setBaseCvcReadOnlyProperty:[CountVonCount countVonCount]]; - [test_object setBaseCvcNotProperty:[CountVonCount countVonCount]]; - - // That added 5 objects, plus 1 more that was copied. - EXPECT_EQ(9, ah_ah_ah); - - test_object.derivedCvcRetain = [CountVonCount countVonCount]; - test_object.derivedCvcCopy = [CountVonCount countVonCount]; - test_object.derivedCvcAssign = derivedAssign; - test_object.derivedCvcCustom = [CountVonCount countVonCount]; - [test_object setDerivedCvcNotProperty:[CountVonCount countVonCount]]; - - // That added 4 objects, plus 1 more that was copied. - EXPECT_EQ(14, ah_ah_ah); - - test_object.protoCvcRetain = [CountVonCount countVonCount]; - test_object.protoCvcCopy = [CountVonCount countVonCount]; - test_object.protoCvcAssign = protoAssign; - test_object.protoCvcCustom = [CountVonCount countVonCount]; - - // That added 3 objects, plus 1 more that was copied. - EXPECT_EQ(18, ah_ah_ah); - } - - // Now that the autorelease pool has been popped, the 3 objects that were - // copied when placed into the test object will have been deallocated. - EXPECT_EQ(15, ah_ah_ah); - - // Make sure that the setters wo/rk and have the expected semantics. - test_object.baseCvcRetain = nil; - test_object.baseCvcCopy = nil; - test_object.baseCvcAssign = nil; - test_object.baseCvcCustom = nil; - test_object.derivedCvcRetain = nil; - test_object.derivedCvcCopy = nil; - test_object.derivedCvcAssign = nil; - test_object.derivedCvcCustom = nil; - test_object.protoCvcRetain = nil; - test_object.protoCvcCopy = nil; - test_object.protoCvcAssign = nil; - test_object.protoCvcCustom = nil; - - // The CountVonCounts marked "retain" and "copy" should have been - // deallocated. Those marked assign should not have been. The only ones that - // should exist now are the ones marked "assign", the ones held in - // non-property instance variables, and the ones held in properties marked - // readonly. - EXPECT_EQ(6, ah_ah_ah); - - { - base::mac::ScopedNSAutoreleasePool pool; - - // Put things back to how they were. - test_object.baseCvcRetain = [CountVonCount countVonCount]; - test_object.baseCvcCopy = [CountVonCount countVonCount]; - test_object.baseCvcAssign = baseAssign; - test_object.baseCvcCustom = [CountVonCount countVonCount]; - test_object.derivedCvcRetain = [CountVonCount countVonCount]; - test_object.derivedCvcCopy = [CountVonCount countVonCount]; - test_object.derivedCvcAssign = derivedAssign; - test_object.derivedCvcCustom = [CountVonCount countVonCount]; - test_object.protoCvcRetain = [CountVonCount countVonCount]; - test_object.protoCvcCopy = [CountVonCount countVonCount]; - test_object.protoCvcAssign = protoAssign; - test_object.protoCvcCustom = [CountVonCount countVonCount]; - - // 9 more CountVonCounts, 3 of which were copied. - EXPECT_EQ(18, ah_ah_ah); - } - - // Now that the autorelease pool has been popped, the 3 copies are gone. - EXPECT_EQ(15, ah_ah_ah); - - // Releasing the test object should get rid of everything that it owns. - [test_object release]; - - // base::mac::ReleaseProperties(self) should have released all of the - // CountVonCounts associated with properties marked "retain" or "copy". The - // -dealloc methods in each should have released the single non-property - // objects in each. Only the CountVonCounts assigned to the properties marked - // "assign" should remain. - EXPECT_EQ(3, ah_ah_ah); - - [baseAssign release]; - [derivedAssign release]; - [protoAssign release]; - - // Zero! Zero counts! Ah, ah, ah. - EXPECT_EQ(0, ah_ah_ah); -} - -TEST(ObjCReleasePropertiesTest, EmptyObject) { - // Test that ReleaseProperties doesn't do anything unexpected to a class - // with no properties. - [[[ObjcPropertyTestEmpty alloc] init] release]; -} - -} // namespace diff --git a/mac/os_crash_dumps.cc b/mac/os_crash_dumps.cc deleted file mode 100644 index 95af00979..000000000 --- a/mac/os_crash_dumps.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/mac/os_crash_dumps.h" - -#include -#include -#include - -#include "base/logging.h" -#include "base/macros.h" - -namespace base { -namespace mac { - -namespace { - -void ExitSignalHandler(int sig) { - // A call to exit() can call atexit() handlers. If we SIGSEGV due - // to a corrupt heap, and if we have an atexit handler that - // allocates or frees memory, we are in trouble if we do not _exit. - _exit(128 + sig); -} - -} // namespace - -void DisableOSCrashDumps() { - // These are the POSIX signals corresponding to the Mach exceptions that - // Apple Crash Reporter handles. See ux_exception() in xnu's - // 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 - // Not a hardware fault - SIGABRT - }; - - // For all these signals, just wire things up so we exit immediately. - for (size_t i = 0; i < arraysize(signals_to_intercept); ++i) { - struct sigaction act = {}; - act.sa_handler = ExitSignalHandler; - - // It is better to allow the signal handler to run on the stack - // registered with sigaltstack(), if one is present. - act.sa_flags = SA_ONSTACK; - - if (sigemptyset(&act.sa_mask) != 0) - DPLOG(FATAL) << "sigemptyset() failed"; - if (sigaction(signals_to_intercept[i], &act, NULL) != 0) - DPLOG(FATAL) << "sigaction() failed"; - } -} - -} // namespace mac -} // namespace base diff --git a/mac/os_crash_dumps.h b/mac/os_crash_dumps.h deleted file mode 100644 index 31d90fb24..000000000 --- a/mac/os_crash_dumps.h +++ /dev/null @@ -1,22 +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_MAC_OS_CRASH_DUMPS_H_ -#define BASE_MAC_OS_CRASH_DUMPS_H_ - -#include "base/base_export.h" - -namespace base { -namespace mac { - -// On Mac OS X, it can take a really long time for the OS crash handler to -// process a Chrome crash when debugging symbols are available. This -// translates into a long wait until the process actually dies. This call -// disables Apple Crash Reporter entirely. -BASE_EXPORT void DisableOSCrashDumps(); - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_OS_CRASH_DUMPS_H_ diff --git a/mac/scoped_aedesc.h b/mac/scoped_aedesc.h deleted file mode 100644 index 73270920d..000000000 --- a/mac/scoped_aedesc.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MAC_SCOPED_AEDESC_H_ -#define BASE_MAC_SCOPED_AEDESC_H_ - -#import - -#include "base/macros.h" - -namespace base { -namespace mac { - -// The ScopedAEDesc is used to scope AppleEvent descriptors. On creation, -// it will store a NULL descriptor. On destruction, it will dispose of the -// descriptor. -// -// This class is parameterized for additional type safety checks. You can use -// the generic AEDesc type by not providing a template parameter: -// ScopedAEDesc<> desc; -template -class ScopedAEDesc { - public: - ScopedAEDesc() { - AECreateDesc(typeNull, NULL, 0, &desc_); - } - - ~ScopedAEDesc() { - AEDisposeDesc(&desc_); - } - - // Used for in parameters. - operator const AEDescType*() { - return &desc_; - } - - // Used for out parameters. - AEDescType* OutPointer() { - return &desc_; - } - - private: - AEDescType desc_; - - DISALLOW_COPY_AND_ASSIGN(ScopedAEDesc); -}; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_AEDESC_H_ diff --git a/mac/scoped_authorizationref.h b/mac/scoped_authorizationref.h deleted file mode 100644 index b83f8dfb3..000000000 --- a/mac/scoped_authorizationref.h +++ /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. - -#ifndef BASE_MAC_SCOPED_AUTHORIZATIONREF_H_ -#define BASE_MAC_SCOPED_AUTHORIZATIONREF_H_ - -#include - -#include "base/compiler_specific.h" -#include "base/macros.h" - -// ScopedAuthorizationRef maintains ownership of an AuthorizationRef. It is -// patterned after the unique_ptr interface. - -namespace base { -namespace mac { - -class ScopedAuthorizationRef { - public: - explicit ScopedAuthorizationRef(AuthorizationRef authorization = NULL) - : authorization_(authorization) { - } - - ~ScopedAuthorizationRef() { - if (authorization_) { - AuthorizationFree(authorization_, kAuthorizationFlagDestroyRights); - } - } - - void reset(AuthorizationRef authorization = NULL) { - if (authorization_ != authorization) { - if (authorization_) { - AuthorizationFree(authorization_, kAuthorizationFlagDestroyRights); - } - authorization_ = authorization; - } - } - - bool operator==(AuthorizationRef that) const { - return authorization_ == that; - } - - bool operator!=(AuthorizationRef that) const { - return authorization_ != that; - } - - operator AuthorizationRef() const { - return authorization_; - } - - AuthorizationRef* get_pointer() { return &authorization_; } - - AuthorizationRef get() const { - return authorization_; - } - - void swap(ScopedAuthorizationRef& that) { - AuthorizationRef temp = that.authorization_; - that.authorization_ = authorization_; - authorization_ = temp; - } - - // ScopedAuthorizationRef::release() is like std::unique_ptr<>::release. It is - // NOT a wrapper for AuthorizationFree(). To force a ScopedAuthorizationRef - // object to call AuthorizationFree(), use ScopedAuthorizationRef::reset(). - AuthorizationRef release() WARN_UNUSED_RESULT { - AuthorizationRef temp = authorization_; - authorization_ = NULL; - return temp; - } - - private: - AuthorizationRef authorization_; - - DISALLOW_COPY_AND_ASSIGN(ScopedAuthorizationRef); -}; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_AUTHORIZATIONREF_H_ diff --git a/mac/scoped_block.h b/mac/scoped_block.h deleted file mode 100644 index 10ab4b4e8..000000000 --- a/mac/scoped_block.h +++ /dev/null @@ -1,72 +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_MAC_SCOPED_BLOCK_H_ -#define BASE_MAC_SCOPED_BLOCK_H_ - -#include - -#include "base/mac/scoped_typeref.h" - -#if defined(__has_feature) && __has_feature(objc_arc) -#define BASE_MAC_BRIDGE_CAST(TYPE, VALUE) (__bridge TYPE)(VALUE) -#else -#define BASE_MAC_BRIDGE_CAST(TYPE, VALUE) VALUE -#endif - -namespace base { -namespace mac { - -namespace internal { - -template -struct ScopedBlockTraits { - static B InvalidValue() { return nullptr; } - static B Retain(B block) { - return BASE_MAC_BRIDGE_CAST( - B, Block_copy(BASE_MAC_BRIDGE_CAST(const void*, block))); - } - static void Release(B block) { - Block_release(BASE_MAC_BRIDGE_CAST(const void*, block)); - } -}; - -} // namespace internal - -// ScopedBlock<> is patterned after ScopedCFTypeRef<>, but uses Block_copy() and -// Block_release() instead of CFRetain() and CFRelease(). -template -class ScopedBlock : public ScopedTypeRef> { - public: - using Traits = internal::ScopedBlockTraits; - -#if !defined(__has_feature) || !__has_feature(objc_arc) - explicit ScopedBlock( - B block = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) - : ScopedTypeRef(block, policy) {} -#else - explicit ScopedBlock(B block = Traits::InvalidValue()) - : ScopedTypeRef(block, base::scoped_policy::RETAIN) {} -#endif - -#if !defined(__has_feature) || !__has_feature(objc_arc) - void reset(B block = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = - base::scoped_policy::ASSUME) { - ScopedTypeRef::reset(block, policy); - } -#else - void reset(B block = Traits::InvalidValue()) { - ScopedTypeRef::reset(block, base::scoped_policy::RETAIN); - } -#endif -}; - -} // namespace mac -} // namespace base - -#undef BASE_MAC_BRIDGE_CAST - -#endif // BASE_MAC_SCOPED_BLOCK_H_ diff --git a/mac/scoped_cffiledescriptorref.h b/mac/scoped_cffiledescriptorref.h deleted file mode 100644 index 923a159c7..000000000 --- a/mac/scoped_cffiledescriptorref.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 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_MAC_SCOPED_CFFILEDESCRIPTORREF_H_ -#define BASE_MAC_SCOPED_CFFILEDESCRIPTORREF_H_ - -#include - -#include "base/scoped_generic.h" - -namespace base { -namespace mac { - -namespace internal { - -struct ScopedCFFileDescriptorRefTraits { - static CFFileDescriptorRef InvalidValue() { return nullptr; } - static void Free(CFFileDescriptorRef ref) { - CFFileDescriptorInvalidate(ref); - CFRelease(ref); - } -}; - -} // namespace internal - -// ScopedCFFileDescriptorRef is designed after ScopedCFTypeRef<>. On -// destruction, it will invalidate the file descriptor. -// ScopedCFFileDescriptorRef (unlike ScopedCFTypeRef<>) does not support RETAIN -// semantics, copying, or assignment, as doing so would increase the chances -// that a file descriptor is invalidated while still in use. -using ScopedCFFileDescriptorRef = - ScopedGeneric; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_CFFILEDESCRIPTORREF_H_ diff --git a/mac/scoped_cftyperef.h b/mac/scoped_cftyperef.h deleted file mode 100644 index ccbc5cfbe..000000000 --- a/mac/scoped_cftyperef.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MAC_SCOPED_CFTYPEREF_H_ -#define BASE_MAC_SCOPED_CFTYPEREF_H_ - -#include - -#include "base/mac/scoped_typeref.h" - -namespace base { - -// ScopedCFTypeRef<> is patterned after std::unique_ptr<>, but maintains -// ownership of a CoreFoundation object: any object that can be represented -// as a CFTypeRef. Style deviations here are solely for compatibility with -// std::unique_ptr<>'s interface, with which everyone is already familiar. -// -// By default, ScopedCFTypeRef<> takes ownership of an object (in the -// constructor or in reset()) by taking over the caller's existing ownership -// claim. The caller must own the object it gives to ScopedCFTypeRef<>, and -// relinquishes an ownership claim to that object. ScopedCFTypeRef<> does not -// call CFRetain(). This behavior is parameterized by the |OwnershipPolicy| -// enum. If the value |RETAIN| is passed (in the constructor or in reset()), -// then ScopedCFTypeRef<> will call CFRetain() on the object, and the initial -// ownership is not changed. - -namespace internal { - -template -struct ScopedCFTypeRefTraits { - static CFT InvalidValue() { return nullptr; } - static CFT Retain(CFT object) { - CFRetain(object); - return object; - } - static void Release(CFT object) { - CFRelease(object); - } -}; - -} // namespace internal - -template -using ScopedCFTypeRef = - ScopedTypeRef>; - -} // namespace base - -#endif // BASE_MAC_SCOPED_CFTYPEREF_H_ diff --git a/mac/scoped_dispatch_object.h b/mac/scoped_dispatch_object.h deleted file mode 100644 index cd2daf233..000000000 --- a/mac/scoped_dispatch_object.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 The Chromium 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_DISPATCH_OBJECT_H_ -#define BASE_MAC_SCOPED_DISPATCH_OBJECT_H_ - -#include - -#include "base/mac/scoped_typeref.h" - -namespace base { - -namespace internal { - -template -struct ScopedDispatchObjectTraits { - static constexpr T InvalidValue() { return nullptr; } - static T Retain(T object) { - dispatch_retain(object); - return object; - } - static void Release(T object) { - dispatch_release(object); - } -}; - -} // namepsace internal - -template -using ScopedDispatchObject = - ScopedTypeRef>; - -} // namespace base - -#endif // BASE_MAC_SCOPED_DISPATCH_OBJECT_H_ diff --git a/mac/scoped_ionotificationportref.h b/mac/scoped_ionotificationportref.h deleted file mode 100644 index 93ebc98b6..000000000 --- a/mac/scoped_ionotificationportref.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2016 The Chromium 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_IONOTIFICATIONPORTREF_H_ -#define BASE_MAC_SCOPED_IONOTIFICATIONPORTREF_H_ - -#include - -#include "base/scoped_generic.h" - -namespace base { -namespace mac { - -namespace internal { - -struct ScopedIONotificationPortRefTraits { - static IONotificationPortRef InvalidValue() { return nullptr; } - static void Free(IONotificationPortRef object) { - IONotificationPortDestroy(object); - } -}; - -} // namepsace internal - -using ScopedIONotificationPortRef = - ScopedGeneric; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_IONOTIFICATIONPORTREF_H_ diff --git a/mac/scoped_ioobject.h b/mac/scoped_ioobject.h deleted file mode 100644 index c948cb554..000000000 --- a/mac/scoped_ioobject.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MAC_SCOPED_IOOBJECT_H_ -#define BASE_MAC_SCOPED_IOOBJECT_H_ - -#include - -#include "base/mac/scoped_typeref.h" - -namespace base { -namespace mac { - -namespace internal { - -template -struct ScopedIOObjectTraits { - static IOT InvalidValue() { return IO_OBJECT_NULL; } - static IOT Retain(IOT iot) { - IOObjectRetain(iot); - return iot; - } - static void Release(IOT iot) { IOObjectRelease(iot); } -}; - -} // namespce internal - -// Just like ScopedCFTypeRef but for io_object_t and subclasses. -template -using ScopedIOObject = ScopedTypeRef>; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_IOOBJECT_H_ diff --git a/mac/scoped_ioplugininterface.h b/mac/scoped_ioplugininterface.h deleted file mode 100644 index 872da8eaa..000000000 --- a/mac/scoped_ioplugininterface.h +++ /dev/null @@ -1,38 +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_MAC_SCOPED_IOPLUGININTERFACE_H_ -#define BASE_MAC_SCOPED_IOPLUGININTERFACE_H_ - -#include - -#include "base/mac/scoped_typeref.h" - -namespace base { -namespace mac { - -namespace internal { - -template -struct ScopedIOPluginInterfaceTraits { - static T InvalidValue() { return nullptr; } - static T Retain(T t) { - (*t)->AddRef(t); - return t; - } - static void Release(T t) { (*t)->Release(t); } -}; - -} // namespace internal - -// Just like ScopedCFTypeRef but for IOCFPlugInInterface and friends -// (IOUSBInterfaceStruct and IOUSBDeviceStruct320 in particular). -template -using ScopedIOPluginInterface = - ScopedTypeRef>; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_IOPLUGININTERFACE_H_ diff --git a/mac/scoped_launch_data.h b/mac/scoped_launch_data.h deleted file mode 100644 index f4db3306d..000000000 --- a/mac/scoped_launch_data.h +++ /dev/null @@ -1,31 +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_MAC_SCOPED_LAUNCH_DATA_H_ -#define BASE_MAC_SCOPED_LAUNCH_DATA_H_ - -#include - -#include "base/scoped_generic.h" - -namespace base { -namespace mac { - -namespace internal { - -struct ScopedLaunchDataTraits { - static launch_data_t InvalidValue() { return nullptr; } - static void Free(launch_data_t ldt) { launch_data_free(ldt); } -}; - -} // namespace internal - -// Just like std::unique_ptr<> but for launch_data_t. -using ScopedLaunchData = - ScopedGeneric; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_LAUNCH_DATA_H_ diff --git a/mac/scoped_mach_port.cc b/mac/scoped_mach_port.cc deleted file mode 100644 index 13307f2c9..000000000 --- a/mac/scoped_mach_port.cc +++ /dev/null @@ -1,38 +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/mac/scoped_mach_port.h" - -#include "base/mac/mach_logging.h" - -namespace base { -namespace mac { -namespace internal { - -// 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"; -} - -// 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"; -} - -// static -void PortSetTraits::Free(mach_port_t port) { - kern_return_t kr = - mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_PORT_SET, -1); - MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) - << "ScopedMachPortSet mach_port_mod_refs"; -} - -} // namespace internal -} // namespace mac -} // namespace base diff --git a/mac/scoped_mach_port.h b/mac/scoped_mach_port.h deleted file mode 100644 index 67fed6bcf..000000000 --- a/mac/scoped_mach_port.h +++ /dev/null @@ -1,67 +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_MAC_SCOPED_MACH_PORT_H_ -#define BASE_MAC_SCOPED_MACH_PORT_H_ - -#include - -#include "base/base_export.h" -#include "base/scoped_generic.h" - -namespace base { -namespace mac { - -namespace internal { - -struct BASE_EXPORT SendRightTraits { - static mach_port_t InvalidValue() { - return MACH_PORT_NULL; - } - - BASE_EXPORT static void Free(mach_port_t port); -}; - -struct BASE_EXPORT ReceiveRightTraits { - static mach_port_t InvalidValue() { - return MACH_PORT_NULL; - } - - BASE_EXPORT static void Free(mach_port_t port); -}; - -struct PortSetTraits { - static mach_port_t InvalidValue() { - return MACH_PORT_NULL; - } - - BASE_EXPORT static void Free(mach_port_t port); -}; - -} // namespace internal - -// 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. -using ScopedMachSendRight = - ScopedGeneric; - -// 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. -using ScopedMachReceiveRight = - ScopedGeneric; - -// A scoper for handling a Mach port set. A port set can have only one -// reference. This takes ownership of that single reference on construction and -// destroys the port set on destruction. Destroying a port set does not destroy -// the receive rights that are members of the port set. -using ScopedMachPortSet = ScopedGeneric; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_MACH_PORT_H_ diff --git a/mac/scoped_mach_vm.cc b/mac/scoped_mach_vm.cc deleted file mode 100644 index d52c77f63..000000000 --- a/mac/scoped_mach_vm.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/mac/scoped_mach_vm.h" - -namespace base { -namespace mac { - -void ScopedMachVM::reset(vm_address_t address, vm_size_t size) { - DCHECK_EQ(address % PAGE_SIZE, 0u); - DCHECK_EQ(size % PAGE_SIZE, 0u); - - 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/mac/scoped_mach_vm.h b/mac/scoped_mach_vm.h deleted file mode 100644 index 58a13f664..000000000 --- a/mac/scoped_mach_vm.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MAC_SCOPED_MACH_VM_H_ -#define BASE_MAC_SCOPED_MACH_VM_H_ - -#include -#include - -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/macros.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_EQ(address % PAGE_SIZE, 0u); - DCHECK_EQ(size % PAGE_SIZE, 0u); - } - - ~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/mac/scoped_nsautorelease_pool.h b/mac/scoped_nsautorelease_pool.h deleted file mode 100644 index 4d15e6da4..000000000 --- a/mac/scoped_nsautorelease_pool.h +++ /dev/null @@ -1,45 +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_MAC_SCOPED_NSAUTORELEASE_POOL_H_ -#define BASE_MAC_SCOPED_NSAUTORELEASE_POOL_H_ - -#include "base/base_export.h" -#include "base/macros.h" - -#if defined(__OBJC__) -@class NSAutoreleasePool; -#else // __OBJC__ -class NSAutoreleasePool; -#endif // __OBJC__ - -namespace base { -namespace mac { - -// ScopedNSAutoreleasePool allocates an NSAutoreleasePool when instantiated and -// sends it a -drain message when destroyed. This allows an autorelease pool to -// be maintained in ordinary C++ code without bringing in any direct Objective-C -// dependency. - -class BASE_EXPORT ScopedNSAutoreleasePool { - public: - ScopedNSAutoreleasePool(); - ~ScopedNSAutoreleasePool(); - - // Clear out the pool in case its position on the stack causes it to be - // alive for long periods of time (such as the entire length of the app). - // Only use then when you're certain the items currently in the pool are - // no longer needed. - void Recycle(); - private: - NSAutoreleasePool* autorelease_pool_; - - private: - DISALLOW_COPY_AND_ASSIGN(ScopedNSAutoreleasePool); -}; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_NSAUTORELEASE_POOL_H_ diff --git a/mac/scoped_nsautorelease_pool.mm b/mac/scoped_nsautorelease_pool.mm deleted file mode 100644 index e542ca86b..000000000 --- a/mac/scoped_nsautorelease_pool.mm +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/mac/scoped_nsautorelease_pool.h" - -#import - -#include "base/logging.h" - -namespace base { -namespace mac { - -ScopedNSAutoreleasePool::ScopedNSAutoreleasePool() - : autorelease_pool_([[NSAutoreleasePool alloc] init]) { - DCHECK(autorelease_pool_); -} - -ScopedNSAutoreleasePool::~ScopedNSAutoreleasePool() { - [autorelease_pool_ drain]; -} - -// Cycle the internal pool, allowing everything there to get cleaned up and -// start anew. -void ScopedNSAutoreleasePool::Recycle() { - [autorelease_pool_ drain]; - autorelease_pool_ = [[NSAutoreleasePool alloc] init]; - DCHECK(autorelease_pool_); -} - -} // namespace mac -} // namespace base diff --git a/mac/scoped_nsobject.h b/mac/scoped_nsobject.h deleted file mode 100644 index d970d03e8..000000000 --- a/mac/scoped_nsobject.h +++ /dev/null @@ -1,240 +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_MAC_SCOPED_NSOBJECT_H_ -#define BASE_MAC_SCOPED_NSOBJECT_H_ - -#include - -// Include NSObject.h directly because Foundation.h pulls in many dependencies. -// (Approx 100k lines of code versus 1.5k for NSObject.h). scoped_nsobject gets -// singled out because it is most typically included from other header files. -#import - -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "base/mac/scoped_typeref.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -@class NSAutoreleasePool; -#endif - -namespace base { - -// scoped_nsobject<> is patterned after std::unique_ptr<>, but maintains -// ownership of an NSObject subclass object. Style deviations here are solely -// for compatibility with std::unique_ptr<>'s interface, with which everyone is -// already familiar. -// -// scoped_nsobject<> takes ownership of an object (in the constructor or in -// reset()) by taking over the caller's existing ownership claim. The caller -// must own the object it gives to scoped_nsobject<>, and relinquishes an -// ownership claim to that object. scoped_nsobject<> does not call -retain, -// callers have to call this manually if appropriate. -// -// scoped_nsprotocol<> has the same behavior as scoped_nsobject, but can be used -// with protocols. -// -// scoped_nsobject<> is not to be used for NSAutoreleasePools. For -// NSAutoreleasePools use ScopedNSAutoreleasePool from -// scoped_nsautorelease_pool.h instead. -// We check for bad uses of scoped_nsobject and NSAutoreleasePool at compile -// time with a template specialization (see below). -// -// If Automatic Reference Counting (aka ARC) is enabled then the ownership -// policy is not controllable by the user as ARC make it really difficult to -// transfer ownership (the reference passed to scoped_nsobject constructor is -// sunk by ARC and __attribute((ns_consumed)) appears to not work correctly -// with Objective-C++ see https://llvm.org/bugs/show_bug.cgi?id=27887). Due to -// that, the policy is always to |RETAIN| when using ARC. - -namespace internal { - -BASE_EXPORT id ScopedNSProtocolTraitsRetain(__unsafe_unretained id obj) - __attribute((ns_returns_not_retained)); -BASE_EXPORT id ScopedNSProtocolTraitsAutoRelease(__unsafe_unretained id obj) - __attribute((ns_returns_not_retained)); -BASE_EXPORT void ScopedNSProtocolTraitsRelease(__unsafe_unretained id obj); - -// Traits for ScopedTypeRef<>. As this class may be compiled from file with -// Automatic Reference Counting enable or not all methods have annotation to -// enforce the same code generation in both case (in particular, the Retain -// method uses ns_returns_not_retained to prevent ARC to insert a -release -// call on the returned value and thus defeating the -retain). -template -struct ScopedNSProtocolTraits { - static NST InvalidValue() __attribute((ns_returns_not_retained)) { - return nil; - } - static NST Retain(__unsafe_unretained NST nst) - __attribute((ns_returns_not_retained)) { - return ScopedNSProtocolTraitsRetain(nst); - } - static void Release(__unsafe_unretained NST nst) { - ScopedNSProtocolTraitsRelease(nst); - } -}; - -} // namespace internal - -template -class scoped_nsprotocol - : public ScopedTypeRef> { - public: - using Traits = internal::ScopedNSProtocolTraits; - -#if !defined(__has_feature) || !__has_feature(objc_arc) - explicit constexpr scoped_nsprotocol( - NST object = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) - : ScopedTypeRef(object, policy) {} -#else - explicit constexpr scoped_nsprotocol(NST object = Traits::InvalidValue()) - : ScopedTypeRef(object, base::scoped_policy::RETAIN) {} -#endif - - scoped_nsprotocol(const scoped_nsprotocol& that) - : ScopedTypeRef(that) {} - - template - explicit scoped_nsprotocol(const scoped_nsprotocol& that_as_subclass) - : ScopedTypeRef(that_as_subclass) {} - - scoped_nsprotocol(scoped_nsprotocol&& that) - : ScopedTypeRef(std::move(that)) {} - - scoped_nsprotocol& operator=(const scoped_nsprotocol& that) { - ScopedTypeRef::operator=(that); - return *this; - } - -#if !defined(__has_feature) || !__has_feature(objc_arc) - void reset(NST object = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = - base::scoped_policy::ASSUME) { - ScopedTypeRef::reset(object, policy); - } -#else - void reset(NST object = Traits::InvalidValue()) { - ScopedTypeRef::reset(object, base::scoped_policy::RETAIN); - } -#endif - - // Shift reference to the autorelease pool to be released later. - NST autorelease() __attribute((ns_returns_not_retained)) { - return internal::ScopedNSProtocolTraitsAutoRelease(this->release()); - } -}; - -// Free functions -template -void swap(scoped_nsprotocol& p1, scoped_nsprotocol& p2) { - p1.swap(p2); -} - -template -bool operator==(C p1, const scoped_nsprotocol& p2) { - return p1 == p2.get(); -} - -template -bool operator!=(C p1, const scoped_nsprotocol& p2) { - return p1 != p2.get(); -} - -template -class scoped_nsobject : public scoped_nsprotocol { - public: - using Traits = typename scoped_nsprotocol::Traits; - -#if !defined(__has_feature) || !__has_feature(objc_arc) - explicit constexpr scoped_nsobject( - NST* object = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) - : scoped_nsprotocol(object, policy) {} -#else - explicit constexpr scoped_nsobject(NST* object = Traits::InvalidValue()) - : scoped_nsprotocol(object) {} -#endif - - scoped_nsobject(const scoped_nsobject& that) - : scoped_nsprotocol(that) {} - - template - explicit scoped_nsobject(const scoped_nsobject& that_as_subclass) - : scoped_nsprotocol(that_as_subclass) {} - - scoped_nsobject(scoped_nsobject&& that) - : scoped_nsprotocol(std::move(that)) {} - - scoped_nsobject& operator=(const scoped_nsobject& that) { - scoped_nsprotocol::operator=(that); - return *this; - } - -#if !defined(__has_feature) || !__has_feature(objc_arc) - void reset(NST* object = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = - base::scoped_policy::ASSUME) { - scoped_nsprotocol::reset(object, policy); - } -#else - void reset(NST* object = Traits::InvalidValue()) { - scoped_nsprotocol::reset(object); - } -#endif - -#if !defined(__has_feature) || !__has_feature(objc_arc) - static_assert(std::is_same::value == false, - "Use ScopedNSAutoreleasePool instead"); -#endif -}; - -// Specialization to make scoped_nsobject work. -template<> -class scoped_nsobject : public scoped_nsprotocol { - public: - using Traits = typename scoped_nsprotocol::Traits; - -#if !defined(__has_feature) || !__has_feature(objc_arc) - explicit constexpr scoped_nsobject( - id object = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) - : scoped_nsprotocol(object, policy) {} -#else - explicit constexpr scoped_nsobject(id object = Traits::InvalidValue()) - : scoped_nsprotocol(object) {} -#endif - - scoped_nsobject(const scoped_nsobject& that) - : scoped_nsprotocol(that) {} - - template - explicit scoped_nsobject(const scoped_nsobject& that_as_subclass) - : scoped_nsprotocol(that_as_subclass) {} - - scoped_nsobject(scoped_nsobject&& that) - : scoped_nsprotocol(std::move(that)) {} - - scoped_nsobject& operator=(const scoped_nsobject& that) { - scoped_nsprotocol::operator=(that); - return *this; - } - -#if !defined(__has_feature) || !__has_feature(objc_arc) - void reset(id object = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = - base::scoped_policy::ASSUME) { - scoped_nsprotocol::reset(object, policy); - } -#else - void reset(id object = Traits::InvalidValue()) { - scoped_nsprotocol::reset(object); - } -#endif -}; - -} // namespace base - -#endif // BASE_MAC_SCOPED_NSOBJECT_H_ diff --git a/mac/scoped_nsobject.mm b/mac/scoped_nsobject.mm deleted file mode 100644 index 65b40317d..000000000 --- a/mac/scoped_nsobject.mm +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "base/mac/scoped_nsobject.h" - -namespace base { -namespace internal { - -id ScopedNSProtocolTraitsRetain(id obj) { - return [obj retain]; -} - -id ScopedNSProtocolTraitsAutoRelease(id obj) { - return [obj autorelease]; -} - -void ScopedNSProtocolTraitsRelease(id obj) { - return [obj release]; -} - -} // namespace internal -} // namespace base diff --git a/mac/scoped_nsobject_unittest.mm b/mac/scoped_nsobject_unittest.mm deleted file mode 100644 index 72d524225..000000000 --- a/mac/scoped_nsobject_unittest.mm +++ /dev/null @@ -1,102 +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 - -#include "base/mac/scoped_nsautorelease_pool.h" -#include "base/mac/scoped_nsobject.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -TEST(ScopedNSObjectTest, ScopedNSObject) { - base::scoped_nsobject p1([[NSObject alloc] init]); - ASSERT_TRUE(p1.get()); - ASSERT_EQ(1u, [p1 retainCount]); - base::scoped_nsobject p2(p1); - ASSERT_EQ(p1.get(), p2.get()); - ASSERT_EQ(2u, [p1 retainCount]); - p2.reset(); - ASSERT_EQ(nil, p2.get()); - ASSERT_EQ(1u, [p1 retainCount]); - { - base::scoped_nsobject p3 = p1; - ASSERT_EQ(p1.get(), p3.get()); - ASSERT_EQ(2u, [p1 retainCount]); - { - base::mac::ScopedNSAutoreleasePool pool; - p3 = p1; - } - ASSERT_EQ(p1.get(), p3.get()); - ASSERT_EQ(2u, [p1 retainCount]); - } - ASSERT_EQ(1u, [p1 retainCount]); - base::scoped_nsobject p4([p1.get() retain]); - ASSERT_EQ(2u, [p1 retainCount]); - ASSERT_TRUE(p1 == p1.get()); - ASSERT_TRUE(p1 == p1); - ASSERT_FALSE(p1 != p1); - ASSERT_FALSE(p1 != p1.get()); - base::scoped_nsobject p5([[NSObject alloc] init]); - ASSERT_TRUE(p1 != p5); - ASSERT_TRUE(p1 != p5.get()); - ASSERT_FALSE(p1 == p5); - ASSERT_FALSE(p1 == p5.get()); - - base::scoped_nsobject p6 = p1; - ASSERT_EQ(3u, [p6 retainCount]); - { - base::mac::ScopedNSAutoreleasePool pool; - p6.autorelease(); - ASSERT_EQ(nil, p6.get()); - ASSERT_EQ(3u, [p1 retainCount]); - } - ASSERT_EQ(2u, [p1 retainCount]); - - base::scoped_nsobject p7([NSObject new]); - base::scoped_nsobject p8(std::move(p7)); - ASSERT_TRUE(p8); - ASSERT_EQ(1u, [p8 retainCount]); - ASSERT_FALSE(p7.get()); -} - -// Instantiating scoped_nsobject<> with T=NSAutoreleasePool should trip a -// static_assert. -#if 0 -TEST(ScopedNSObjectTest, FailToCreateScopedNSObjectAutoreleasePool) { - base::scoped_nsobject pool; -} -#endif - -TEST(ScopedNSObjectTest, ScopedNSObjectInContainer) { - base::scoped_nsobject p([[NSObject alloc] init]); - ASSERT_TRUE(p.get()); - ASSERT_EQ(1u, [p retainCount]); - { - std::vector> objects; - objects.push_back(p); - ASSERT_EQ(2u, [p retainCount]); - ASSERT_EQ(p.get(), objects[0].get()); - objects.push_back(base::scoped_nsobject([[NSObject alloc] init])); - ASSERT_TRUE(objects[1].get()); - ASSERT_EQ(1u, [objects[1] retainCount]); - } - ASSERT_EQ(1u, [p retainCount]); -} - -TEST(ScopedNSObjectTest, ScopedNSObjectFreeFunctions) { - base::scoped_nsobject p1([[NSObject alloc] init]); - id o1 = p1.get(); - ASSERT_TRUE(o1 == p1); - ASSERT_FALSE(o1 != p1); - base::scoped_nsobject p2([[NSObject alloc] init]); - ASSERT_TRUE(o1 != p2); - ASSERT_FALSE(o1 == p2); - id o2 = p2.get(); - swap(p1, p2); - ASSERT_EQ(o2, p1.get()); - ASSERT_EQ(o1, p2.get()); -} - -} // namespace diff --git a/mac/scoped_nsobject_unittest_arc.mm b/mac/scoped_nsobject_unittest_arc.mm deleted file mode 100644 index 5cbf3f838..000000000 --- a/mac/scoped_nsobject_unittest_arc.mm +++ /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. - -#include - -#import - -#include "base/logging.h" -#import "base/mac/scoped_nsobject.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -namespace { - -template -CFIndex GetRetainCount(const base::scoped_nsobject& nst) { - @autoreleasepool { - return CFGetRetainCount((__bridge CFTypeRef)nst.get()) - 1; - } -} - -#if __has_feature(objc_arc_weak) -TEST(ScopedNSObjectTestARC, DefaultPolicyIsRetain) { - __weak id o; - @autoreleasepool { - base::scoped_nsprotocol p([[NSObject alloc] init]); - o = p.get(); - DCHECK_EQ(o, p.get()); - } - DCHECK_EQ(o, nil); -} -#endif - -TEST(ScopedNSObjectTestARC, ScopedNSObject) { - base::scoped_nsobject p1([[NSObject alloc] init]); - @autoreleasepool { - EXPECT_TRUE(p1.get()); - EXPECT_TRUE(p1.get()); - } - EXPECT_EQ(1, GetRetainCount(p1)); - EXPECT_EQ(1, GetRetainCount(p1)); - base::scoped_nsobject p2(p1); - @autoreleasepool { - EXPECT_EQ(p1.get(), p2.get()); - } - EXPECT_EQ(2, GetRetainCount(p1)); - p2.reset(); - EXPECT_EQ(nil, p2.get()); - EXPECT_EQ(1, GetRetainCount(p1)); - { - base::scoped_nsobject p3 = p1; - @autoreleasepool { - EXPECT_EQ(p1.get(), p3.get()); - } - EXPECT_EQ(2, GetRetainCount(p1)); - @autoreleasepool { - p3 = p1; - EXPECT_EQ(p1.get(), p3.get()); - } - EXPECT_EQ(2, GetRetainCount(p1)); - } - EXPECT_EQ(1, GetRetainCount(p1)); - base::scoped_nsobject p4; - @autoreleasepool { - p4 = base::scoped_nsobject(p1.get()); - } - EXPECT_EQ(2, GetRetainCount(p1)); - @autoreleasepool { - EXPECT_TRUE(p1 == p1.get()); - EXPECT_TRUE(p1 == p1); - EXPECT_FALSE(p1 != p1); - EXPECT_FALSE(p1 != p1.get()); - } - base::scoped_nsobject p5([[NSObject alloc] init]); - @autoreleasepool { - EXPECT_TRUE(p1 != p5); - EXPECT_TRUE(p1 != p5.get()); - EXPECT_FALSE(p1 == p5); - EXPECT_FALSE(p1 == p5.get()); - } - - base::scoped_nsobject p6 = p1; - EXPECT_EQ(3, GetRetainCount(p6)); - @autoreleasepool { - p6.autorelease(); - EXPECT_EQ(nil, p6.get()); - } - EXPECT_EQ(2, GetRetainCount(p1)); -} - -TEST(ScopedNSObjectTestARC, ScopedNSObjectInContainer) { - base::scoped_nsobject p([[NSObject alloc] init]); - @autoreleasepool { - EXPECT_TRUE(p.get()); - } - EXPECT_EQ(1, GetRetainCount(p)); - @autoreleasepool { - std::vector> objects; - objects.push_back(p); - EXPECT_EQ(2, GetRetainCount(p)); - @autoreleasepool { - EXPECT_EQ(p.get(), objects[0].get()); - } - objects.push_back(base::scoped_nsobject([[NSObject alloc] init])); - @autoreleasepool { - EXPECT_TRUE(objects[1].get()); - } - EXPECT_EQ(1, GetRetainCount(objects[1])); - } - EXPECT_EQ(1, GetRetainCount(p)); -} - -TEST(ScopedNSObjectTestARC, ScopedNSObjectFreeFunctions) { - base::scoped_nsobject p1([[NSObject alloc] init]); - id o1 = p1.get(); - EXPECT_TRUE(o1 == p1); - EXPECT_FALSE(o1 != p1); - base::scoped_nsobject p2([[NSObject alloc] init]); - EXPECT_TRUE(o1 != p2); - EXPECT_FALSE(o1 == p2); - id o2 = p2.get(); - swap(p1, p2); - EXPECT_EQ(o2, p1.get()); - EXPECT_EQ(o1, p2.get()); -} - -} // namespace diff --git a/mac/scoped_objc_class_swizzler.h b/mac/scoped_objc_class_swizzler.h deleted file mode 100644 index e18e4abfd..000000000 --- a/mac/scoped_objc_class_swizzler.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MAC_SCOPED_OBJC_CLASS_SWIZZLER_H_ -#define BASE_MAC_SCOPED_OBJC_CLASS_SWIZZLER_H_ - -#import - -#include "base/base_export.h" -#include "base/macros.h" - -namespace base { -namespace mac { - -// Within a given scope, swaps method implementations of a class interface, or -// between two class interfaces. The argument and return types must match. -class BASE_EXPORT ScopedObjCClassSwizzler { - public: - // Given two classes that each respond to |selector|, swap the implementations - // of those methods. - ScopedObjCClassSwizzler(Class target, Class source, SEL selector); - - // Given two selectors on the same class interface, |target| (e.g. via - // inheritance or categories), swap the implementations of methods |original| - // and |alternate|. - ScopedObjCClassSwizzler(Class target, SEL original, SEL alternate); - - ~ScopedObjCClassSwizzler(); - - // Return a callable function pointer for the replaced method. To call this - // from the replacing function, the first two arguments should be |self| and - // |_cmd|. These are followed by the (variadic) method arguments. - IMP GetOriginalImplementation(); - - private: - // Delegated constructor. - void Init(Class target, Class source, SEL original, SEL alternate); - - Method old_selector_impl_; - Method new_selector_impl_; - - DISALLOW_COPY_AND_ASSIGN(ScopedObjCClassSwizzler); -}; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_OBJC_CLASS_SWIZZLER_H_ diff --git a/mac/scoped_objc_class_swizzler.mm b/mac/scoped_objc_class_swizzler.mm deleted file mode 100644 index 20e5c569a..000000000 --- a/mac/scoped_objc_class_swizzler.mm +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "base/mac/scoped_objc_class_swizzler.h" - -#include - -#include "base/logging.h" - -namespace base { -namespace mac { - -ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target, - Class source, - SEL selector) - : old_selector_impl_(NULL), new_selector_impl_(NULL) { - Init(target, source, selector, selector); -} - -ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target, - SEL original, - SEL alternate) - : old_selector_impl_(NULL), new_selector_impl_(NULL) { - Init(target, target, original, alternate); -} - -ScopedObjCClassSwizzler::~ScopedObjCClassSwizzler() { - if (old_selector_impl_ && new_selector_impl_) - method_exchangeImplementations(old_selector_impl_, new_selector_impl_); -} - -IMP ScopedObjCClassSwizzler::GetOriginalImplementation() { - // Note that while the swizzle is in effect the "new" method is actually - // pointing to the original implementation, since they have been swapped. - return method_getImplementation(new_selector_impl_); -} - -void ScopedObjCClassSwizzler::Init(Class target, - Class source, - SEL original, - SEL alternate) { - old_selector_impl_ = class_getInstanceMethod(target, original); - new_selector_impl_ = class_getInstanceMethod(source, alternate); - if (!old_selector_impl_ && !new_selector_impl_) { - // Try class methods. - old_selector_impl_ = class_getClassMethod(target, original); - new_selector_impl_ = class_getClassMethod(source, alternate); - } - - DCHECK(old_selector_impl_); - DCHECK(new_selector_impl_); - if (!old_selector_impl_ || !new_selector_impl_) - return; - - // The argument and return types must match exactly. - const char* old_types = method_getTypeEncoding(old_selector_impl_); - const char* new_types = method_getTypeEncoding(new_selector_impl_); - DCHECK(old_types); - DCHECK(new_types); - DCHECK_EQ(0, strcmp(old_types, new_types)); - if (!old_types || !new_types || strcmp(old_types, new_types)) { - old_selector_impl_ = new_selector_impl_ = NULL; - return; - } - - method_exchangeImplementations(old_selector_impl_, new_selector_impl_); -} - -} // namespace mac -} // namespace base diff --git a/mac/scoped_objc_class_swizzler_unittest.mm b/mac/scoped_objc_class_swizzler_unittest.mm deleted file mode 100644 index 79820a3ff..000000000 --- a/mac/scoped_objc_class_swizzler_unittest.mm +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "base/mac/scoped_objc_class_swizzler.h" - -#import "base/mac/scoped_nsobject.h" -#include "testing/gtest/include/gtest/gtest.h" - -@interface ObjCClassSwizzlerTestOne : NSObject -+ (NSInteger)function; -- (NSInteger)method; -- (NSInteger)modifier; -@end - -@interface ObjCClassSwizzlerTestTwo : NSObject -+ (NSInteger)function; -- (NSInteger)method; -- (NSInteger)modifier; -@end - -@implementation ObjCClassSwizzlerTestOne : NSObject - -+ (NSInteger)function { - return 10; -} - -- (NSInteger)method { - // Multiply by a modifier to ensure |self| in a swizzled implementation - // refers to the original object. - return 1 * [self modifier]; -} - -- (NSInteger)modifier { - return 3; -} - -@end - -@implementation ObjCClassSwizzlerTestTwo : NSObject - -+ (NSInteger)function { - return 20; -} - -- (NSInteger)method { - return 2 * [self modifier]; -} - -- (NSInteger)modifier { - return 7; -} - -@end - -@interface ObjCClassSwizzlerTestOne (AlternateCategory) -- (NSInteger)alternate; -@end - -@implementation ObjCClassSwizzlerTestOne (AlternateCategory) -- (NSInteger)alternate { - return 3 * [self modifier]; -} -@end - -@interface ObjCClassSwizzlerTestOneChild : ObjCClassSwizzlerTestOne -- (NSInteger)childAlternate; -@end - -@implementation ObjCClassSwizzlerTestOneChild -- (NSInteger)childAlternate { - return 5 * [self modifier]; -} -@end - -namespace base { -namespace mac { - -TEST(ObjCClassSwizzlerTest, SwizzleInstanceMethods) { - base::scoped_nsobject object_one( - [[ObjCClassSwizzlerTestOne alloc] init]); - base::scoped_nsobject object_two( - [[ObjCClassSwizzlerTestTwo alloc] init]); - EXPECT_EQ(3, [object_one method]); - EXPECT_EQ(14, [object_two method]); - - { - base::mac::ScopedObjCClassSwizzler swizzler( - [ObjCClassSwizzlerTestOne class], - [ObjCClassSwizzlerTestTwo class], - @selector(method)); - EXPECT_EQ(6, [object_one method]); - EXPECT_EQ(7, [object_two method]); - - IMP original = swizzler.GetOriginalImplementation(); - id expected_result = reinterpret_cast(3); - EXPECT_EQ(expected_result, original(object_one, @selector(method))); - } - - EXPECT_EQ(3, [object_one method]); - EXPECT_EQ(14, [object_two method]); -} - -TEST(ObjCClassSwizzlerTest, SwizzleClassMethods) { - EXPECT_EQ(10, [ObjCClassSwizzlerTestOne function]); - EXPECT_EQ(20, [ObjCClassSwizzlerTestTwo function]); - - { - base::mac::ScopedObjCClassSwizzler swizzler( - [ObjCClassSwizzlerTestOne class], - [ObjCClassSwizzlerTestTwo class], - @selector(function)); - EXPECT_EQ(20, [ObjCClassSwizzlerTestOne function]); - EXPECT_EQ(10, [ObjCClassSwizzlerTestTwo function]); - - IMP original = swizzler.GetOriginalImplementation(); - id expected_result = reinterpret_cast(10); - EXPECT_EQ(expected_result, - original([ObjCClassSwizzlerTestOne class], @selector(function))); - } - - EXPECT_EQ(10, [ObjCClassSwizzlerTestOne function]); - EXPECT_EQ(20, [ObjCClassSwizzlerTestTwo function]); -} - -TEST(ObjCClassSwizzlerTest, SwizzleViaCategory) { - base::scoped_nsobject object_one( - [[ObjCClassSwizzlerTestOne alloc] init]); - EXPECT_EQ(3, [object_one method]); - - { - base::mac::ScopedObjCClassSwizzler swizzler( - [ObjCClassSwizzlerTestOne class], - @selector(method), - @selector(alternate)); - EXPECT_EQ(9, [object_one method]); - - IMP original = swizzler.GetOriginalImplementation(); - id expected_result = reinterpret_cast(3); - EXPECT_EQ(expected_result, original(object_one, @selector(method))); - } - - EXPECT_EQ(3, [object_one method]); -} - -TEST(ObjCClassSwizzlerTest, SwizzleViaInheritance) { - base::scoped_nsobject child( - [[ObjCClassSwizzlerTestOneChild alloc] init]); - EXPECT_EQ(3, [child method]); - - { - base::mac::ScopedObjCClassSwizzler swizzler( - [ObjCClassSwizzlerTestOneChild class], - @selector(method), - @selector(childAlternate)); - EXPECT_EQ(15, [child method]); - - IMP original = swizzler.GetOriginalImplementation(); - id expected_result = reinterpret_cast(3); - EXPECT_EQ(expected_result, original(child, @selector(method))); - } - - EXPECT_EQ(3, [child method]); -} - -} // namespace mac -} // namespace base diff --git a/mac/scoped_sending_event.h b/mac/scoped_sending_event.h deleted file mode 100644 index c579cefc6..000000000 --- a/mac/scoped_sending_event.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_MAC_SCOPED_SENDING_EVENT_H_ -#define BASE_MAC_SCOPED_SENDING_EVENT_H_ - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/message_loop/message_pump_mac.h" - -// Nested event loops can pump IPC messages, including -// script-initiated tab closes, which could release objects that the -// nested event loop might message. CrAppProtocol defines how to ask -// the embedding NSApplication subclass if an event is currently being -// handled, in which case such closes are deferred to the top-level -// event loop. -// -// ScopedSendingEvent allows script-initiated event loops to work like -// a nested event loop, as such events do not arrive via -sendEvent:. -// CrAppControlProtocol lets ScopedSendingEvent tell the embedding -// NSApplication what to return from -handlingSendEvent. - -@protocol CrAppControlProtocol -- (void)setHandlingSendEvent:(BOOL)handlingSendEvent; -@end - -namespace base { -namespace mac { - -class BASE_EXPORT ScopedSendingEvent { - public: - ScopedSendingEvent(); - ~ScopedSendingEvent(); - - private: - // The NSApp in control at the time the constructor was run, to be - // sure the |handling_| setting is restored appropriately. - NSObject* app_; - BOOL handling_; // Value of -[app_ handlingSendEvent] at construction. - - DISALLOW_COPY_AND_ASSIGN(ScopedSendingEvent); -}; - -} // namespace mac -} // namespace base - -#endif // BASE_MAC_SCOPED_SENDING_EVENT_H_ diff --git a/mac/scoped_sending_event.mm b/mac/scoped_sending_event.mm deleted file mode 100644 index c3813d8ae..000000000 --- a/mac/scoped_sending_event.mm +++ /dev/null @@ -1,24 +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. - -#import "base/mac/scoped_sending_event.h" - -#include "base/logging.h" - -namespace base { -namespace mac { - -ScopedSendingEvent::ScopedSendingEvent() - : app_(static_cast*>(NSApp)) { - DCHECK([app_ conformsToProtocol:@protocol(CrAppControlProtocol)]); - handling_ = [app_ isHandlingSendEvent]; - [app_ setHandlingSendEvent:YES]; -} - -ScopedSendingEvent::~ScopedSendingEvent() { - [app_ setHandlingSendEvent:handling_]; -} - -} // namespace mac -} // namespace base diff --git a/mac/scoped_sending_event_unittest.mm b/mac/scoped_sending_event_unittest.mm deleted file mode 100644 index 52f18c6f5..000000000 --- a/mac/scoped_sending_event_unittest.mm +++ /dev/null @@ -1,63 +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. - -#import "base/mac/scoped_sending_event.h" - -#import - -#include "base/mac/scoped_nsobject.h" -#include "testing/gtest/include/gtest/gtest.h" - -@interface ScopedSendingEventTestCrApp : NSApplication { - @private - BOOL handlingSendEvent_; -} -@property(nonatomic, assign, getter=isHandlingSendEvent) BOOL handlingSendEvent; -@end - -@implementation ScopedSendingEventTestCrApp -@synthesize handlingSendEvent = handlingSendEvent_; -@end - -namespace { - -class ScopedSendingEventTest : public testing::Test { - public: - ScopedSendingEventTest() : app_([[ScopedSendingEventTestCrApp alloc] init]) { - NSApp = app_.get(); - } - ~ScopedSendingEventTest() override { NSApp = nil; } - - private: - base::scoped_nsobject app_; -}; - -// Sets the flag within scope, resets when leaving scope. -TEST_F(ScopedSendingEventTest, SetHandlingSendEvent) { - id app = NSApp; - EXPECT_FALSE([app isHandlingSendEvent]); - { - base::mac::ScopedSendingEvent is_handling_send_event; - EXPECT_TRUE([app isHandlingSendEvent]); - } - EXPECT_FALSE([app isHandlingSendEvent]); -} - -// Nested call restores previous value rather than resetting flag. -TEST_F(ScopedSendingEventTest, NestedSetHandlingSendEvent) { - id app = NSApp; - EXPECT_FALSE([app isHandlingSendEvent]); - { - base::mac::ScopedSendingEvent is_handling_send_event; - EXPECT_TRUE([app isHandlingSendEvent]); - { - base::mac::ScopedSendingEvent nested_is_handling_send_event; - EXPECT_TRUE([app isHandlingSendEvent]); - } - EXPECT_TRUE([app isHandlingSendEvent]); - } - EXPECT_FALSE([app isHandlingSendEvent]); -} - -} // namespace diff --git a/mac/scoped_typeref.h b/mac/scoped_typeref.h deleted file mode 100644 index dd9841d73..000000000 --- a/mac/scoped_typeref.h +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MAC_SCOPED_TYPEREF_H_ -#define BASE_MAC_SCOPED_TYPEREF_H_ - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/memory/scoped_policy.h" - -namespace base { - -// ScopedTypeRef<> is patterned after std::unique_ptr<>, but maintains 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 { -// static CGLContextObj InvalidValue() { return nullptr; } -// static CGLContextObj Retain(CGLContextObj object) { -// CGLContextRetain(object); -// return object; -// } -// static 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 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 -struct ScopedTypeRefTraits; - -template> -class ScopedTypeRef { - public: - typedef T element_type; - - explicit constexpr ScopedTypeRef( - __unsafe_unretained T object = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) - : object_(object) { - if (object_ && policy == base::scoped_policy::RETAIN) - object_ = Traits::Retain(object_); - } - - ScopedTypeRef(const ScopedTypeRef& that) - : object_(that.object_) { - if (object_) - object_ = Traits::Retain(object_); - } - - // This allows passing an object to a function that takes its superclass. - template - explicit ScopedTypeRef(const ScopedTypeRef& that_as_subclass) - : object_(that_as_subclass.get()) { - if (object_) - object_ = Traits::Retain(object_); - } - - ScopedTypeRef(ScopedTypeRef&& that) : object_(that.object_) { - that.object_ = Traits::InvalidValue(); - } - - ~ScopedTypeRef() { - if (object_) - Traits::Release(object_); - } - - ScopedTypeRef& operator=(const ScopedTypeRef& 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(__unsafe_unretained T object = Traits::InvalidValue(), - base::scoped_policy::OwnershipPolicy policy = - base::scoped_policy::ASSUME) { - if (object && policy == base::scoped_policy::RETAIN) - object = Traits::Retain(object); - if (object_) - Traits::Release(object_); - object_ = object; - } - - bool operator==(__unsafe_unretained T that) const { return object_ == that; } - - bool operator!=(__unsafe_unretained T that) const { return object_ != that; } - - operator T() const __attribute((ns_returns_not_retained)) { return object_; } - - T get() const __attribute((ns_returns_not_retained)) { return object_; } - - void swap(ScopedTypeRef& that) { - __unsafe_unretained T temp = that.object_; - that.object_ = object_; - object_ = temp; - } - - // ScopedTypeRef<>::release() is like std::unique_ptr<>::release. It is NOT - // a wrapper for Release(). To force a ScopedTypeRef<> object to call - // Release(), use ScopedTypeRef<>::reset(). - T release() __attribute((ns_returns_not_retained)) WARN_UNUSED_RESULT { - __unsafe_unretained T temp = object_; - object_ = Traits::InvalidValue(); - return temp; - } - - private: - __unsafe_unretained T object_; -}; - -} // namespace base - -#endif // BASE_MAC_SCOPED_TYPEREF_H_ diff --git a/mac/sdk_forward_declarations.h b/mac/sdk_forward_declarations.h deleted file mode 100644 index 799387073..000000000 --- a/mac/sdk_forward_declarations.h +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains forward declarations for items in later SDKs than the -// default one with which Chromium is built (currently 10.10). -// If you call any function from this header, be sure to check at runtime for -// respondsToSelector: before calling these functions (else your code will crash -// on older OS X versions that chrome still supports). - -#ifndef BASE_MAC_SDK_FORWARD_DECLARATIONS_H_ -#define BASE_MAC_SDK_FORWARD_DECLARATIONS_H_ - -#import -#import -#import -#import -#import -#import -#include - -#include "base/base_export.h" -#include "base/mac/availability.h" - -// ---------------------------------------------------------------------------- -// Define typedefs, enums, and protocols not available in the version of the -// OSX SDK being compiled against. -// ---------------------------------------------------------------------------- - -#if !defined(MAC_OS_X_VERSION_10_11) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11 - -enum { - NSPressureBehaviorUnknown = -1, - NSPressureBehaviorPrimaryDefault = 0, - NSPressureBehaviorPrimaryClick = 1, - NSPressureBehaviorPrimaryGeneric = 2, - NSPressureBehaviorPrimaryAccelerator = 3, - NSPressureBehaviorPrimaryDeepClick = 5, - NSPressureBehaviorPrimaryDeepDrag = 6 -}; -typedef NSInteger NSPressureBehavior; - -@interface NSPressureConfiguration : NSObject -- (instancetype)initWithPressureBehavior:(NSPressureBehavior)pressureBehavior; -@end - -enum { - NSSpringLoadingHighlightNone = 0, - NSSpringLoadingHighlightStandard, - NSSpringLoadingHighlightEmphasized -}; -typedef NSUInteger NSSpringLoadingHighlight; - -#endif // MAC_OS_X_VERSION_10_11 - -#if !defined(MAC_OS_X_VERSION_10_12) || \ - MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 - -// The protocol was formalized by the 10.12 SDK, but it was informally used -// before. -@protocol CAAnimationDelegate -- (void)animationDidStart:(CAAnimation*)animation; -- (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished; -@end - -@protocol CALayerDelegate -@end - -#endif // MAC_OS_X_VERSION_10_12 - -// ---------------------------------------------------------------------------- -// Define NSStrings only available in newer versions of the OSX SDK to force -// them to be statically linked. -// ---------------------------------------------------------------------------- - -extern "C" { -#if !defined(MAC_OS_X_VERSION_10_10) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 -BASE_EXPORT extern NSString* const CIDetectorTypeQRCode; -BASE_EXPORT extern NSString* const NSUserActivityTypeBrowsingWeb; -BASE_EXPORT extern NSString* const NSAppearanceNameVibrantDark; -BASE_EXPORT extern NSString* const NSAppearanceNameVibrantLight; -#endif // MAC_OS_X_VERSION_10_10 -} // extern "C" - -// ---------------------------------------------------------------------------- -// If compiling against an older version of the OSX SDK, declare classes and -// functions that are available in newer versions of the OSX SDK. If compiling -// against a newer version of the OSX SDK, redeclare those same classes and -// functions to suppress -Wpartial-availability warnings. -// ---------------------------------------------------------------------------- - -// Once Chrome no longer supports OSX 10.9, everything within this preprocessor -// block can be removed. -#if !defined(MAC_OS_X_VERSION_10_10) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 - -@interface NSUserActivity (YosemiteSDK) -@property(readonly, copy) NSString* activityType; -@property(copy) NSDictionary* userInfo; -@property(copy) NSURL* webpageURL; -@property(copy) NSString* title; -- (instancetype)initWithActivityType:(NSString*)activityType; -- (void)becomeCurrent; -- (void)invalidate; -@end - -@interface CBUUID (YosemiteSDK) -- (NSString*)UUIDString; -@end - -@interface NSViewController (YosemiteSDK) -- (void)viewDidLoad; -@end - -@interface NSWindow (YosemiteSDK) -- (void)setTitlebarAppearsTransparent:(BOOL)flag; -@end - -@interface NSProcessInfo (YosemiteSDK) -@property(readonly) NSOperatingSystemVersion operatingSystemVersion; -@end - -@interface NSLayoutConstraint (YosemiteSDK) -@property(getter=isActive) BOOL active; -+ (void)activateConstraints:(NSArray*)constraints; -@end - -@interface NSVisualEffectView (YosemiteSDK) -- (void)setState:(NSVisualEffectState)state; -@end - -@class NSVisualEffectView; - -@interface CIQRCodeFeature (YosemiteSDK) -@property(readonly) CGRect bounds; -@property(readonly) CGPoint topLeft; -@property(readonly) CGPoint topRight; -@property(readonly) CGPoint bottomLeft; -@property(readonly) CGPoint bottomRight; -@property(readonly, copy) NSString* messageString; -@end - -@class CIQRCodeFeature; - -@interface NSView (YosemiteSDK) -- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector; -@property(copy) NSString* accessibilityLabel; -@end - -#endif // MAC_OS_X_VERSION_10_10 - -// Once Chrome no longer supports OSX 10.10.2, everything within this -// preprocessor block can be removed. -#if !defined(MAC_OS_X_VERSION_10_10_3) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10_3 - -@interface NSEvent (Yosemite_3_SDK) -@property(readonly) NSInteger stage; -@end - -#endif // MAC_OS_X_VERSION_10_10 - -// ---------------------------------------------------------------------------- -// Define NSStrings only available in newer versions of the OSX SDK to force -// them to be statically linked. -// ---------------------------------------------------------------------------- - -extern "C" { -#if !defined(MAC_OS_X_VERSION_10_11) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11 -BASE_EXPORT extern NSString* const CIDetectorTypeText; -#endif // MAC_OS_X_VERSION_10_11 -} // extern "C" - -// Once Chrome no longer supports OSX 10.10, everything within this -// preprocessor block can be removed. -#if !defined(MAC_OS_X_VERSION_10_11) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11 - -@class NSLayoutDimension; -@class NSLayoutXAxisAnchor; -@class NSLayoutYAxisAnchor; - -@interface NSObject (ElCapitanSDK) -- (NSLayoutConstraint*)constraintEqualToConstant:(CGFloat)c; -- (NSLayoutConstraint*)constraintGreaterThanOrEqualToConstant:(CGFloat)c; -@end - -@interface NSView (ElCapitanSDK) -- (void)setPressureConfiguration:(NSPressureConfiguration*)aConfiguration - API_AVAILABLE(macos(10.11)); -@property(readonly, strong) - NSLayoutXAxisAnchor* leftAnchor API_AVAILABLE(macos(10.11)); -@property(readonly, strong) - NSLayoutXAxisAnchor* rightAnchor API_AVAILABLE(macos(10.11)); -@property(readonly, strong) - NSLayoutYAxisAnchor* bottomAnchor API_AVAILABLE(macos(10.11)); -@property(readonly, strong) - NSLayoutDimension* widthAnchor API_AVAILABLE(macos(10.11)); -@end - -@interface NSWindow (ElCapitanSDK) -- (void)performWindowDragWithEvent:(NSEvent*)event; -@end - -@interface CIRectangleFeature (ElCapitanSDK) -@property(readonly) CGRect bounds; -@end - -@class CIRectangleFeature; - -#endif // MAC_OS_X_VERSION_10_11 - -// Once Chrome no longer supports OSX 10.11, everything within this -// preprocessor block can be removed. -#if !defined(MAC_OS_X_VERSION_10_12) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 - -@interface NSWindow (SierraSDK) -@property(class) BOOL allowsAutomaticWindowTabbing; -@end - -#endif // MAC_OS_X_VERSION_10_12 - -// Once Chrome no longer supports OSX 10.12.0, everything within this -// preprocessor block can be removed. -#if !defined(MAC_OS_X_VERSION_10_12_1) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12_1 - -@interface NSButton (SierraPointOneSDK) -@property(copy) NSColor* bezelColor; -@property BOOL imageHugsTitle; -+ (instancetype)buttonWithTitle:(NSString*)title - target:(id)target - action:(SEL)action; -+ (instancetype)buttonWithImage:(NSImage*)image - target:(id)target - action:(SEL)action; -+ (instancetype)buttonWithTitle:(NSString*)title - image:(NSImage*)image - target:(id)target - action:(SEL)action; -@end - -@interface NSSegmentedControl (SierraPointOneSDK) -+ (instancetype)segmentedControlWithImages:(NSArray*)images - trackingMode:(NSSegmentSwitchTracking)trackingMode - target:(id)target - action:(SEL)action; -@end - -@interface NSTextField (SierraPointOneSDK) -+ (instancetype)labelWithAttributedString: - (NSAttributedString*)attributedStringValue; -@end - -#endif // MAC_OS_X_VERSION_10_12_1 - -// Once Chrome no longer supports OSX 10.12, everything within this -// preprocessor block can be removed. -#if !defined(MAC_OS_X_VERSION_10_13) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_13 - -// VNRequest forward declarations. -@class VNRequest; -typedef void (^VNRequestCompletionHandler)(VNRequest* request, NSError* error); - -@interface VNRequest : NSObject -- (instancetype)initWithCompletionHandler: - (VNRequestCompletionHandler)completionHandler NS_DESIGNATED_INITIALIZER; -@property(readonly, nonatomic, copy) NSArray* results; -@end - -// VNDetectFaceLandmarksRequest forward declarations. -@interface VNImageBasedRequest : VNRequest -@end - -@protocol VNFaceObservationAccepting -@end - -@interface VNDetectFaceLandmarksRequest - : VNImageBasedRequest -@end - -// VNImageRequestHandler forward declarations. -typedef NSString* VNImageOption NS_STRING_ENUM; - -@interface VNImageRequestHandler : NSObject -- (instancetype)initWithCIImage:(CIImage*)image - options:(NSDictionary*)options; -- (BOOL)performRequests:(NSArray*)requests error:(NSError**)error; -@end - -// VNFaceLandmarks2D forward declarations. -@interface VNFaceLandmarkRegion : NSObject -@property(readonly) NSUInteger pointCount; -@end - -@interface VNFaceLandmarkRegion2D : VNFaceLandmarkRegion -@property(readonly, assign) - const CGPoint* normalizedPoints NS_RETURNS_INNER_POINTER; -@end - -@interface VNFaceLandmarks2D : NSObject -@property(readonly) VNFaceLandmarkRegion2D* leftEye; -@property(readonly) VNFaceLandmarkRegion2D* rightEye; -@property(readonly) VNFaceLandmarkRegion2D* outerLips; -@property(readonly) VNFaceLandmarkRegion2D* nose; -@end - -// VNFaceObservation forward declarations. -@interface VNObservation : NSObject -@end - -@interface VNDetectedObjectObservation : VNObservation -@property(readonly, nonatomic, assign) CGRect boundingBox; -@end - -@interface VNFaceObservation : VNDetectedObjectObservation -@property(readonly, nonatomic, strong) VNFaceLandmarks2D* landmarks; -@end - -#endif // MAC_OS_X_VERSION_10_13 -// ---------------------------------------------------------------------------- -// The symbol for kCWSSIDDidChangeNotification is available in the -// CoreWLAN.framework for OSX versions 10.6 through 10.10. The symbol is not -// declared in the OSX 10.9+ SDK, so when compiling against an OSX 10.9+ SDK, -// declare the symbol. -// ---------------------------------------------------------------------------- -BASE_EXPORT extern "C" NSString* const kCWSSIDDidChangeNotification; - -#endif // BASE_MAC_SDK_FORWARD_DECLARATIONS_H_ diff --git a/mac/sdk_forward_declarations.mm b/mac/sdk_forward_declarations.mm deleted file mode 100644 index c624daedd..000000000 --- a/mac/sdk_forward_declarations.mm +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/mac/sdk_forward_declarations.h" - -#if !defined(MAC_OS_X_VERSION_10_10) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10 -NSString* const CIDetectorTypeQRCode = @"CIDetectorTypeQRCode"; - -NSString* const NSUserActivityTypeBrowsingWeb = - @"NSUserActivityTypeBrowsingWeb"; - -NSString* const NSAppearanceNameVibrantDark = @"NSAppearanceNameVibrantDark"; -#endif // MAC_OS_X_VERSION_10_10 - -#if !defined(MAC_OS_X_VERSION_10_11) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11 -NSString* const CIDetectorTypeText = @"CIDetectorTypeText"; -#endif // MAC_OS_X_VERSION_10_11 diff --git a/macros.h b/macros.h deleted file mode 100644 index 3064a1b58..000000000 --- a/macros.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file 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 // For size_t. - -// Distinguish mips32. -#if defined(__mips__) && (_MIPS_SIM == _ABIO32) && !defined(__mips32__) -#define __mips32__ -#endif - -// Distinguish mips64. -#if defined(__mips__) && (_MIPS_SIM == _ABI64) && !defined(__mips64__) -#define __mips64__ -#endif - -// Put this in the declarations for a class to be uncopyable. -#define DISALLOW_COPY(TypeName) \ - TypeName(const TypeName&) = delete - -// Put this in the declarations for a class to be unassignable. -#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete - -// Put this in the declarations for a class to be uncopyable and unassignable. -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - DISALLOW_COPY(TypeName); \ - DISALLOW_ASSIGN(TypeName) - -// A macro to disallow all the implicit constructors, namely the -// default constructor, copy constructor and operator= functions. -// This is especially useful for classes containing only static methods. -#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName() = delete; \ - 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. For the technical details, refer to -// http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx. - -// This template function declaration is used in defining arraysize. -// Note that the function doesn't need an implementation, as we only -// use its type. -// -// DEPRECATED, please use base::size(array) instead. -// TODO(https://crbug.com/837308): Replace existing arraysize usages. -template char (&ArraySizeHelper(T (&array)[N]))[N]; -#define arraysize(array) (sizeof(ArraySizeHelper(array))) - -// 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: -// -// std::unique_ptr my_var = ...; -// if (TakeOwnership(my_var.get()) == SUCCESS) -// ignore_result(my_var.release()); -// -template -inline void ignore_result(const T&) { -} - -namespace base { - -// 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. This is -// thread-safe. -// -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DEPRECATED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// Please don't use this macro. Use a function-local static of type -// base::NoDestructor instead: -// -// Factory& Factory::GetInstance() { -// static base::NoDestructor instance; -// return *instance; -// } -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \ - static type& name = *new type arguments - -// Workaround for MSVC, which expands __VA_ARGS__ as one macro argument. To -// work around this bug, wrap the entire expression in this macro... -#define CR_EXPAND_ARG(arg) arg - -} // base - -#endif // BASE_MACROS_H_ diff --git a/md5.cc b/md5.cc deleted file mode 100644 index 72c774d35..000000000 --- a/md5.cc +++ /dev/null @@ -1,299 +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. - -// The original file was copied from sqlite, and was in the public domain. - -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ - -#include "base/md5.h" - -#include - -namespace { - -struct Context { - uint32_t buf[4]; - uint32_t bits[2]; - uint8_t in[64]; -}; - -/* - * Note: this code is harmless on little-endian machines. - */ -void byteReverse(uint8_t* buf, unsigned longs) { - do { - uint32_t temp = static_cast( - static_cast(buf[3]) << 8 | - buf[2]) << 16 | - (static_cast(buf[1]) << 8 | buf[0]); - *reinterpret_cast(buf) = temp; - buf += 4; - } while (--longs); -} - -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ - (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -void MD5Transform(uint32_t buf[4], const uint32_t in[16]) { - uint32_t a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - -} // namespace - -namespace base { - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -void MD5Init(MD5Context* context) { - struct Context* ctx = reinterpret_cast(context); - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -void MD5Update(MD5Context* context, const StringPiece& data) { - struct Context* ctx = reinterpret_cast(context); - const uint8_t* buf = reinterpret_cast(data.data()); - size_t len = data.size(); - - /* Update bitcount */ - - uint32_t t = ctx->bits[0]; - if ((ctx->bits[0] = t + (static_cast(len) << 3)) < t) - ctx->bits[1]++; /* Carry from low to high */ - ctx->bits[1] += static_cast(len >> 29); - - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - - /* Handle any leading odd-sized chunks */ - - if (t) { - uint8_t* p = static_cast(ctx->in + t); - - t = 64 - t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); - buf += t; - len -= t; - } - - /* Process data in 64-byte chunks */ - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); - buf += 64; - len -= 64; - } - - /* Handle any remaining bytes of data. */ - - memcpy(ctx->in, buf, len); -} - -/* - * Final wrapup - pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -void MD5Final(MD5Digest* digest, MD5Context* context) { - struct Context* ctx = reinterpret_cast(context); - unsigned count; - uint8_t* p; - - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; - - /* Set the first char of padding to 0x80. This is safe since there is - always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); - - /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - memset(p, 0, count - 8); - } - byteReverse(ctx->in, 14); - - /* Append length in bits and transform */ - memcpy(&ctx->in[14 * sizeof(ctx->bits[0])], &ctx->bits[0], - sizeof(ctx->bits[0])); - memcpy(&ctx->in[15 * sizeof(ctx->bits[1])], &ctx->bits[1], - sizeof(ctx->bits[1])); - - MD5Transform(ctx->buf, reinterpret_cast(ctx->in)); - byteReverse(reinterpret_cast(ctx->buf), 4); - memcpy(digest->a, ctx->buf, 16); - memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ -} - -void MD5IntermediateFinal(MD5Digest* digest, const MD5Context* context) { - /* 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"; - - std::string ret; - ret.resize(32); - - for (int i = 0, j = 0; i < 16; i++, j += 2) { - uint8_t a = digest.a[i]; - ret[j] = zEncode[(a >> 4) & 0xf]; - ret[j + 1] = zEncode[a & 0xf]; - } - return ret; -} - -void MD5Sum(const void* data, size_t length, MD5Digest* digest) { - MD5Context ctx; - MD5Init(&ctx); - MD5Update(&ctx, StringPiece(reinterpret_cast(data), length)); - MD5Final(digest, &ctx); -} - -std::string MD5String(const StringPiece& str) { - MD5Digest digest; - MD5Sum(str.data(), str.length(), &digest); - return MD5DigestToBase16(digest); -} - -} // namespace base diff --git a/md5.h b/md5.h deleted file mode 100644 index ef6417899..000000000 --- a/md5.h +++ /dev/null @@ -1,78 +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_MD5_H_ -#define BASE_MD5_H_ - -#include -#include - -#include "base/base_export.h" -#include "base/strings/string_piece.h" - -namespace base { - -// MD5 stands for Message Digest algorithm 5. -// MD5 is a robust hash function, designed for cyptography, but often used -// for file checksums. The code is complex and slow, but has few -// collisions. -// See Also: -// http://en.wikipedia.org/wiki/MD5 - -// These functions perform MD5 operations. The simplest call is MD5Sum() to -// generate the MD5 sum of the given data. -// -// You can also compute the MD5 sum of data incrementally by making multiple -// calls to MD5Update(): -// MD5Context ctx; // intermediate MD5 data: do not use -// MD5Init(&ctx); -// MD5Update(&ctx, data1, length1); -// MD5Update(&ctx, data2, length2); -// ... -// -// MD5Digest digest; // the result of the computation -// MD5Final(&digest, &ctx); -// -// You can call MD5DigestToBase16() to generate a string of the digest. - -// The output of an MD5 operation. -struct MD5Digest { - uint8_t a[16]; -}; - -// Used for storing intermediate data during an MD5 computation. Callers -// should not access the data. -typedef char MD5Context[88]; - -// Initializes the given MD5 context structure for subsequent calls to -// MD5Update(). -BASE_EXPORT void MD5Init(MD5Context* context); - -// For the given buffer of |data| as a StringPiece, updates the given MD5 -// context with the sum of the data. You can call this any number of times -// during the computation, except that MD5Init() must have been called first. -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); - -// Computes the MD5 sum of the given data buffer with the given length. -// The given 'digest' structure will be filled with the result data. -BASE_EXPORT void MD5Sum(const void* data, size_t length, MD5Digest* digest); - -// Returns the MD5 (in hexadecimal) of a string. -BASE_EXPORT std::string MD5String(const StringPiece& str); - -} // namespace base - -#endif // BASE_MD5_H_ diff --git a/md5_unittest.cc b/md5_unittest.cc deleted file mode 100644 index b27efe9f9..000000000 --- a/md5_unittest.cc +++ /dev/null @@ -1,253 +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/md5.h" - -#include - -#include -#include - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(MD5, DigestToBase16) { - MD5Digest digest; - - int data[] = { - 0xd4, 0x1d, 0x8c, 0xd9, - 0x8f, 0x00, 0xb2, 0x04, - 0xe9, 0x80, 0x09, 0x98, - 0xec, 0xf8, 0x42, 0x7e - }; - - for (int i = 0; i < 16; ++i) - digest.a[i] = data[i] & 0xff; - - std::string actual = MD5DigestToBase16(digest); - std::string expected = "d41d8cd98f00b204e9800998ecf8427e"; - - EXPECT_EQ(expected, actual); -} - -TEST(MD5, MD5SumEmtpyData) { - MD5Digest digest; - const char data[] = ""; - - MD5Sum(data, strlen(data), &digest); - - int expected[] = { - 0xd4, 0x1d, 0x8c, 0xd9, - 0x8f, 0x00, 0xb2, 0x04, - 0xe9, 0x80, 0x09, 0x98, - 0xec, 0xf8, 0x42, 0x7e - }; - - for (int i = 0; i < 16; ++i) - EXPECT_EQ(expected[i], digest.a[i] & 0xFF); -} - -TEST(MD5, MD5SumOneByteData) { - MD5Digest digest; - const char data[] = "a"; - - MD5Sum(data, strlen(data), &digest); - - int expected[] = { - 0x0c, 0xc1, 0x75, 0xb9, - 0xc0, 0xf1, 0xb6, 0xa8, - 0x31, 0xc3, 0x99, 0xe2, - 0x69, 0x77, 0x26, 0x61 - }; - - for (int i = 0; i < 16; ++i) - EXPECT_EQ(expected[i], digest.a[i] & 0xFF); -} - -TEST(MD5, MD5SumLongData) { - const int length = 10 * 1024 * 1024 + 1; - std::unique_ptr data(new char[length]); - - for (int i = 0; i < length; ++i) - data[i] = i & 0xFF; - - MD5Digest digest; - MD5Sum(data.get(), length, &digest); - - int expected[] = { - 0x90, 0xbd, 0x6a, 0xd9, - 0x0a, 0xce, 0xf5, 0xad, - 0xaa, 0x92, 0x20, 0x3e, - 0x21, 0xc7, 0xa1, 0x3e - }; - - for (int i = 0; i < 16; ++i) - EXPECT_EQ(expected[i], digest.a[i] & 0xFF); -} - -TEST(MD5, ContextWithEmptyData) { - MD5Context ctx; - MD5Init(&ctx); - - MD5Digest digest; - MD5Final(&digest, &ctx); - - int expected[] = { - 0xd4, 0x1d, 0x8c, 0xd9, - 0x8f, 0x00, 0xb2, 0x04, - 0xe9, 0x80, 0x09, 0x98, - 0xec, 0xf8, 0x42, 0x7e - }; - - for (int i = 0; i < 16; ++i) - EXPECT_EQ(expected[i], digest.a[i] & 0xFF); -} - -TEST(MD5, ContextWithLongData) { - MD5Context ctx; - MD5Init(&ctx); - - const int length = 10 * 1024 * 1024 + 1; - std::unique_ptr data(new char[length]); - - for (int i = 0; i < length; ++i) - data[i] = i & 0xFF; - - int total = 0; - while (total < length) { - int len = 4097; // intentionally not 2^k. - if (len > length - total) - len = length - total; - - MD5Update(&ctx, - StringPiece(reinterpret_cast(data.get() + total), len)); - total += len; - } - - EXPECT_EQ(length, total); - - MD5Digest digest; - MD5Final(&digest, &ctx); - - int expected[] = { - 0x90, 0xbd, 0x6a, 0xd9, - 0x0a, 0xce, 0xf5, 0xad, - 0xaa, 0x92, 0x20, 0x3e, - 0x21, 0xc7, 0xa1, 0x3e - }; - - for (int i = 0; i < 16; ++i) - EXPECT_EQ(expected[i], digest.a[i] & 0xFF); -} - -// Example data from http://www.ietf.org/rfc/rfc1321.txt A.5 Test Suite -TEST(MD5, MD5StringTestSuite1) { - std::string actual = MD5String(""); - std::string expected = "d41d8cd98f00b204e9800998ecf8427e"; - EXPECT_EQ(expected, actual); -} - -TEST(MD5, MD5StringTestSuite2) { - std::string actual = MD5String("a"); - std::string expected = "0cc175b9c0f1b6a831c399e269772661"; - EXPECT_EQ(expected, actual); -} - -TEST(MD5, MD5StringTestSuite3) { - std::string actual = MD5String("abc"); - std::string expected = "900150983cd24fb0d6963f7d28e17f72"; - EXPECT_EQ(expected, actual); -} - -TEST(MD5, MD5StringTestSuite4) { - std::string actual = MD5String("message digest"); - std::string expected = "f96b697d7cb7938d525a2f31aaf161d0"; - EXPECT_EQ(expected, actual); -} - -TEST(MD5, MD5StringTestSuite5) { - std::string actual = MD5String("abcdefghijklmnopqrstuvwxyz"); - std::string expected = "c3fcd3d76192e4007dfb496cca67e13b"; - EXPECT_EQ(expected, actual); -} - -TEST(MD5, MD5StringTestSuite6) { - std::string actual = MD5String("ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789"); - std::string expected = "d174ab98d277d9f5a5611c2c9f419d9f"; - EXPECT_EQ(expected, actual); -} - -TEST(MD5, MD5StringTestSuite7) { - std::string actual = MD5String("12345678901234567890" - "12345678901234567890" - "12345678901234567890" - "12345678901234567890"); - std::string expected = "57edf4a22be3c955ac49da2e2107b67a"; - EXPECT_EQ(expected, actual); -} - -TEST(MD5, ContextWithStringData) { - MD5Context ctx; - MD5Init(&ctx); - - MD5Update(&ctx, "abc"); - - MD5Digest digest; - MD5Final(&digest, &ctx); - - std::string actual = MD5DigestToBase16(digest); - std::string expected = "900150983cd24fb0d6963f7d28e17f72"; - - 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_TRUE(memcmp(&digest, &header_digest, sizeof(digest))); -} - -} // namespace base diff --git a/memory/OWNERS b/memory/OWNERS deleted file mode 100644 index 9b7cbb19b..000000000 --- a/memory/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -per-file *chromeos*=skuhne@chromium.org -per-file *chromeos*=oshima@chromium.org -per-file *shared_memory*=set noparent -per-file *shared_memory*=file://ipc/SECURITY_OWNERS diff --git a/memory/aligned_memory.cc b/memory/aligned_memory.cc deleted file mode 100644 index 93cbeb57f..000000000 --- a/memory/aligned_memory.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/aligned_memory.h" - -#include "base/logging.h" -#include "build/build_config.h" - -#if defined(OS_ANDROID) -#include -#endif - -namespace base { - -void* AlignedAlloc(size_t size, size_t alignment) { - DCHECK_GT(size, 0U); - DCHECK_EQ(alignment & (alignment - 1), 0U); - DCHECK_EQ(alignment % sizeof(void*), 0U); - void* ptr = nullptr; -#if defined(COMPILER_MSVC) - ptr = _aligned_malloc(size, alignment); -// Android technically supports posix_memalign(), but does not expose it in -// the current version of the library headers used by Chrome. Luckily, -// memalign() on Android returns pointers which can safely be used with -// free(), so we can use it instead. Issue filed to document this: -// http://code.google.com/p/android/issues/detail?id=35391 -#elif defined(OS_ANDROID) - ptr = memalign(alignment, size); -#else - if (posix_memalign(&ptr, alignment, size)) - ptr = nullptr; -#endif - // Since aligned allocations may fail for non-memory related reasons, force a - // crash if we encounter a failed allocation; maintaining consistent behavior - // with a normal allocation failure in Chrome. - if (!ptr) { - DLOG(ERROR) << "If you crashed here, your aligned allocation is incorrect: " - << "size=" << size << ", alignment=" << alignment; - CHECK(false); - } - // Sanity check alignment just to be safe. - DCHECK_EQ(reinterpret_cast(ptr) & (alignment - 1), 0U); - return ptr; -} - -} // namespace base diff --git a/memory/aligned_memory.h b/memory/aligned_memory.h deleted file mode 100644 index 89f95054c..000000000 --- a/memory/aligned_memory.h +++ /dev/null @@ -1,58 +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_MEMORY_ALIGNED_MEMORY_H_ -#define BASE_MEMORY_ALIGNED_MEMORY_H_ - -#include -#include - -#include - -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "build/build_config.h" - -#if defined(COMPILER_MSVC) -#include -#else -#include -#endif - -// A runtime sized aligned allocation can be created: -// -// float* my_array = static_cast(AlignedAlloc(size, alignment)); -// -// // ... later, to release the memory: -// AlignedFree(my_array); -// -// Or using unique_ptr: -// -// std::unique_ptr my_array( -// static_cast(AlignedAlloc(size, alignment))); - -namespace base { - -// This can be replaced with std::aligned_malloc when we have C++17. -BASE_EXPORT void* AlignedAlloc(size_t size, size_t alignment); - -inline void AlignedFree(void* ptr) { -#if defined(COMPILER_MSVC) - _aligned_free(ptr); -#else - free(ptr); -#endif -} - -// Deleter for use with unique_ptr. E.g., use as -// std::unique_ptr foo; -struct AlignedFreeDeleter { - inline void operator()(void* ptr) const { - AlignedFree(ptr); - } -}; - -} // namespace base - -#endif // BASE_MEMORY_ALIGNED_MEMORY_H_ diff --git a/memory/aligned_memory_unittest.cc b/memory/aligned_memory_unittest.cc deleted file mode 100644 index e354f38b7..000000000 --- a/memory/aligned_memory_unittest.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/aligned_memory.h" - -#include - -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#define EXPECT_ALIGNED(ptr, align) \ - EXPECT_EQ(0u, reinterpret_cast(ptr) & (align - 1)) - -namespace base { - -TEST(AlignedMemoryTest, DynamicAllocation) { - void* p = AlignedAlloc(8, 8); - EXPECT_TRUE(p); - EXPECT_ALIGNED(p, 8); - AlignedFree(p); - - p = AlignedAlloc(8, 16); - EXPECT_TRUE(p); - EXPECT_ALIGNED(p, 16); - AlignedFree(p); - - p = AlignedAlloc(8, 256); - EXPECT_TRUE(p); - EXPECT_ALIGNED(p, 256); - AlignedFree(p); - - p = AlignedAlloc(8, 4096); - EXPECT_TRUE(p); - EXPECT_ALIGNED(p, 4096); - AlignedFree(p); -} - -TEST(AlignedMemoryTest, ScopedDynamicAllocation) { - std::unique_ptr p( - static_cast(AlignedAlloc(8, 8))); - EXPECT_TRUE(p.get()); - EXPECT_ALIGNED(p.get(), 8); -} - -} // namespace base diff --git a/memory/discardable_memory.cc b/memory/discardable_memory.cc deleted file mode 100644 index f0730aa40..000000000 --- a/memory/discardable_memory.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/discardable_memory.h" - -namespace base { - -DiscardableMemory::DiscardableMemory() = default; - -DiscardableMemory::~DiscardableMemory() = default; - -} // namespace base diff --git a/memory/discardable_memory.h b/memory/discardable_memory.h deleted file mode 100644 index 5c632d1c9..000000000 --- a/memory/discardable_memory.h +++ /dev/null @@ -1,78 +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_MEMORY_DISCARDABLE_MEMORY_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_H_ - -#include "base/base_export.h" -#include "base/compiler_specific.h" - -namespace base { - -namespace trace_event { -class MemoryAllocatorDump; -class ProcessMemoryDump; -} - -// Discardable memory is used to cache large objects without worrying about -// blowing out memory, both on mobile devices where there is no swap, and -// desktop devices where unused free memory should be used to help the user -// experience. This is preferable to releasing memory in response to an OOM -// signal because it is simpler and provides system-wide management of -// purgable memory, though it has less flexibility as to which objects get -// discarded. -// -// Discardable memory has two states: locked and unlocked. While the memory is -// locked, it will not be discarded. Unlocking the memory allows the -// discardable memory system and the OS to reclaim it if needed. Locks do not -// nest. -// -// Notes: -// - The paging behavior of memory while it is locked is not specified. While -// mobile platforms will not swap it out, it may qualify for swapping -// on desktop platforms. It is not expected that this will matter, as the -// preferred pattern of usage for DiscardableMemory is to lock down the -// memory, use it as quickly as possible, and then unlock it. -// - Because of memory alignment, the amount of memory allocated can be -// larger than the requested memory size. It is not very efficient for -// small allocations. -// - A discardable memory instance is not thread safe. It is the -// responsibility of users of discardable memory to ensure there are no -// races. -// -class BASE_EXPORT DiscardableMemory { - public: - DiscardableMemory(); - virtual ~DiscardableMemory(); - - // Locks the memory so that it will not be purged by the system. Returns - // true on success. If the return value is false then this object should be - // discarded and a new one should be created. - virtual bool Lock() WARN_UNUSED_RESULT = 0; - - // Unlocks the memory so that it can be purged by the system. Must be called - // after every successful lock call. - virtual void Unlock() = 0; - - // Returns the memory address held by this object. The object must be locked - // before calling this. - virtual void* data() const = 0; - - // Handy method to simplify calling data() with a reinterpret_cast. - template T* data_as() const { - return reinterpret_cast(data()); - } - - // Used for dumping the statistics of discardable memory allocated in tracing. - // Returns a new MemoryAllocatorDump in the |pmd| with the size of the - // discardable memory. The MemoryAllocatorDump created is owned by |pmd|. See - // ProcessMemoryDump::CreateAllocatorDump. - virtual trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump( - const char* name, - trace_event::ProcessMemoryDump* pmd) const = 0; -}; - -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_H_ diff --git a/memory/discardable_memory_allocator.cc b/memory/discardable_memory_allocator.cc deleted file mode 100644 index 3dbb27672..000000000 --- a/memory/discardable_memory_allocator.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/discardable_memory_allocator.h" - -#include "base/logging.h" - -namespace base { -namespace { - -DiscardableMemoryAllocator* g_discardable_allocator = nullptr; - -} // namespace - -// static -void DiscardableMemoryAllocator::SetInstance( - DiscardableMemoryAllocator* allocator) { - DCHECK(!allocator || !g_discardable_allocator); - g_discardable_allocator = allocator; -} - -// static -DiscardableMemoryAllocator* DiscardableMemoryAllocator::GetInstance() { - DCHECK(g_discardable_allocator); - return g_discardable_allocator; -} - -} // namespace base diff --git a/memory/discardable_memory_allocator.h b/memory/discardable_memory_allocator.h deleted file mode 100644 index 8d74b1695..000000000 --- a/memory/discardable_memory_allocator.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_ -#define BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_ - -#include - -#include - -#include "base/base_export.h" - -namespace base { -class DiscardableMemory; - -class BASE_EXPORT DiscardableMemoryAllocator { - public: - // Returns the allocator instance. - static DiscardableMemoryAllocator* GetInstance(); - - // Sets the allocator instance. Can only be called once, e.g. on startup. - // Ownership of |instance| remains with the caller. - static void SetInstance(DiscardableMemoryAllocator* allocator); - - // Giant WARNING: Discardable[Shared]Memory is only implemented on Android. On - // non-Android platforms, it behaves exactly the same as SharedMemory. - // See LockPages() in discardable_shared_memory.cc. - virtual std::unique_ptr AllocateLockedDiscardableMemory( - size_t size) = 0; - - protected: - virtual ~DiscardableMemoryAllocator() = default; -}; - -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_ diff --git a/memory/discardable_shared_memory.cc b/memory/discardable_shared_memory.cc deleted file mode 100644 index 3b6b4dbfa..000000000 --- a/memory/discardable_shared_memory.cc +++ /dev/null @@ -1,514 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/discardable_shared_memory.h" - -#include - -#include - -#include "base/atomicops.h" -#include "base/bits.h" -#include "base/logging.h" -#include "base/memory/shared_memory_tracker.h" -#include "base/numerics/safe_math.h" -#include "base/process/process_metrics.h" -#include "base/trace_event/memory_allocator_dump.h" -#include "base/trace_event/process_memory_dump.h" -#include "build/build_config.h" - -#if defined(OS_POSIX) && !defined(OS_NACL) -// For madvise() which is available on all POSIX compatible systems. -#include -#endif - -#if defined(OS_ANDROID) -#include "third_party/ashmem/ashmem.h" -#endif - -#if defined(OS_WIN) -#include -#include "base/win/windows_version.h" -#endif - -namespace base { -namespace { - -// Use a machine-sized pointer as atomic type. It will use the Atomic32 or -// Atomic64 routines, depending on the architecture. -typedef intptr_t AtomicType; -typedef uintptr_t UAtomicType; - -// Template specialization for timestamp serialization/deserialization. This -// is used to serialize timestamps using Unix time on systems where AtomicType -// does not have enough precision to contain a timestamp in the standard -// serialized format. -template -Time TimeFromWireFormat(int64_t value); -template -int64_t TimeToWireFormat(Time time); - -// Serialize to Unix time when using 4-byte wire format. -// Note: 19 January 2038, this will cease to work. -template <> -Time ALLOW_UNUSED_TYPE TimeFromWireFormat<4>(int64_t value) { - return value ? Time::UnixEpoch() + TimeDelta::FromSeconds(value) : Time(); -} -template <> -int64_t ALLOW_UNUSED_TYPE TimeToWireFormat<4>(Time time) { - return time > Time::UnixEpoch() ? (time - Time::UnixEpoch()).InSeconds() : 0; -} - -// Standard serialization format when using 8-byte wire format. -template <> -Time ALLOW_UNUSED_TYPE TimeFromWireFormat<8>(int64_t value) { - return Time::FromInternalValue(value); -} -template <> -int64_t ALLOW_UNUSED_TYPE TimeToWireFormat<8>(Time time) { - return time.ToInternalValue(); -} - -struct SharedState { - enum LockState { UNLOCKED = 0, LOCKED = 1 }; - - explicit SharedState(AtomicType ivalue) { value.i = ivalue; } - SharedState(LockState lock_state, Time timestamp) { - int64_t wire_timestamp = TimeToWireFormat(timestamp); - DCHECK_GE(wire_timestamp, 0); - DCHECK_EQ(lock_state & ~1, 0); - value.u = (static_cast(wire_timestamp) << 1) | lock_state; - } - - LockState GetLockState() const { return static_cast(value.u & 1); } - - Time GetTimestamp() const { - return TimeFromWireFormat(value.u >> 1); - } - - // Bit 1: Lock state. Bit is set when locked. - // Bit 2..sizeof(AtomicType)*8: Usage timestamp. NULL time when locked or - // purged. - union { - AtomicType i; - UAtomicType u; - } value; -}; - -// Shared state is stored at offset 0 in shared memory segments. -SharedState* SharedStateFromSharedMemory( - const WritableSharedMemoryMapping& shared_memory) { - DCHECK(shared_memory.IsValid()); - return static_cast(shared_memory.memory()); -} - -// Round up |size| to a multiple of page size. -size_t AlignToPageSize(size_t size) { - return bits::Align(size, base::GetPageSize()); -} - -} // namespace - -DiscardableSharedMemory::DiscardableSharedMemory() - : mapped_size_(0), locked_page_count_(0) { -} - -DiscardableSharedMemory::DiscardableSharedMemory( - UnsafeSharedMemoryRegion shared_memory_region) - : shared_memory_region_(std::move(shared_memory_region)), - mapped_size_(0), - locked_page_count_(0) {} - -DiscardableSharedMemory::~DiscardableSharedMemory() = default; - -bool DiscardableSharedMemory::CreateAndMap(size_t size) { - CheckedNumeric checked_size = size; - checked_size += AlignToPageSize(sizeof(SharedState)); - if (!checked_size.IsValid()) - return false; - - shared_memory_region_ = - UnsafeSharedMemoryRegion::Create(checked_size.ValueOrDie()); - - if (!shared_memory_region_.IsValid()) - return false; - - shared_memory_mapping_ = shared_memory_region_.Map(); - if (!shared_memory_mapping_.IsValid()) - return false; - - mapped_size_ = shared_memory_mapping_.mapped_size() - - AlignToPageSize(sizeof(SharedState)); - - locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize(); -#if DCHECK_IS_ON() - for (size_t page = 0; page < locked_page_count_; ++page) - locked_pages_.insert(page); -#endif - - DCHECK(last_known_usage_.is_null()); - SharedState new_state(SharedState::LOCKED, Time()); - subtle::Release_Store( - &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i, - new_state.value.i); - return true; -} - -bool DiscardableSharedMemory::Map(size_t size) { - DCHECK(!shared_memory_mapping_.IsValid()); - if (shared_memory_mapping_.IsValid()) - return false; - - shared_memory_mapping_ = shared_memory_region_.MapAt( - 0, AlignToPageSize(sizeof(SharedState)) + size); - if (!shared_memory_mapping_.IsValid()) - return false; - - mapped_size_ = shared_memory_mapping_.mapped_size() - - AlignToPageSize(sizeof(SharedState)); - - locked_page_count_ = AlignToPageSize(mapped_size_) / base::GetPageSize(); -#if DCHECK_IS_ON() - for (size_t page = 0; page < locked_page_count_; ++page) - locked_pages_.insert(page); -#endif - - return true; -} - -bool DiscardableSharedMemory::Unmap() { - if (!shared_memory_mapping_.IsValid()) - return false; - - shared_memory_mapping_ = WritableSharedMemoryMapping(); - locked_page_count_ = 0; -#if DCHECK_IS_ON() - locked_pages_.clear(); -#endif - mapped_size_ = 0; - return true; -} - -DiscardableSharedMemory::LockResult DiscardableSharedMemory::Lock( - size_t offset, size_t length) { - DCHECK_EQ(AlignToPageSize(offset), offset); - DCHECK_EQ(AlignToPageSize(length), length); - - // Calls to this function must be synchronized properly. - DFAKE_SCOPED_LOCK(thread_collision_warner_); - - DCHECK(shared_memory_mapping_.IsValid()); - - // We need to successfully acquire the platform independent lock before - // individual pages can be locked. - if (!locked_page_count_) { - // Return false when instance has been purged or not initialized properly - // by checking if |last_known_usage_| is NULL. - if (last_known_usage_.is_null()) - return FAILED; - - SharedState old_state(SharedState::UNLOCKED, last_known_usage_); - SharedState new_state(SharedState::LOCKED, Time()); - SharedState result(subtle::Acquire_CompareAndSwap( - &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i, - old_state.value.i, new_state.value.i)); - if (result.value.u != old_state.value.u) { - // Update |last_known_usage_| in case the above CAS failed because of - // an incorrect timestamp. - last_known_usage_ = result.GetTimestamp(); - return FAILED; - } - } - - // Zero for length means "everything onward". - if (!length) - length = AlignToPageSize(mapped_size_) - offset; - - size_t start = offset / base::GetPageSize(); - size_t end = start + length / base::GetPageSize(); - DCHECK_LE(start, end); - DCHECK_LE(end, AlignToPageSize(mapped_size_) / base::GetPageSize()); - - // Add pages to |locked_page_count_|. - // Note: Locking a page that is already locked is an error. - locked_page_count_ += end - start; -#if DCHECK_IS_ON() - // Detect incorrect usage by keeping track of exactly what pages are locked. - for (auto page = start; page < end; ++page) { - auto result = locked_pages_.insert(page); - DCHECK(result.second); - } - DCHECK_EQ(locked_pages_.size(), locked_page_count_); -#endif - - // Always behave as if memory was purged when trying to lock a 0 byte segment. - if (!length) - return PURGED; - -#if defined(OS_ANDROID) - // Ensure that the platform won't discard the required pages. - return LockPages(shared_memory_region_, - AlignToPageSize(sizeof(SharedState)) + offset, length); -#elif defined(OS_MACOSX) - // On macOS, there is no mechanism to lock pages. However, we do need to call - // madvise(MADV_FREE_REUSE) in order to correctly update accounting for memory - // footprint via task_info(). - // - // Note that calling madvise(MADV_FREE_REUSE) on regions that haven't had - // madvise(MADV_FREE_REUSABLE) called on them has no effect. - // - // Note that the corresponding call to MADV_FREE_REUSABLE is in Purge(), since - // that's where the memory is actually released, rather than Unlock(), which - // is a no-op on macOS. - // - // For more information, see - // https://bugs.chromium.org/p/chromium/issues/detail?id=823915. - if (madvise(reinterpret_cast(shared_memory_mapping_.memory()) + - AlignToPageSize(sizeof(SharedState)), - AlignToPageSize(mapped_size_), MADV_FREE_REUSE)) - ; - return DiscardableSharedMemory::SUCCESS; -#else - return DiscardableSharedMemory::SUCCESS; -#endif -} - -void DiscardableSharedMemory::Unlock(size_t offset, size_t length) { - DCHECK_EQ(AlignToPageSize(offset), offset); - DCHECK_EQ(AlignToPageSize(length), length); - - // Calls to this function must be synchronized properly. - DFAKE_SCOPED_LOCK(thread_collision_warner_); - - // Passing zero for |length| means "everything onward". Note that |length| may - // still be zero after this calculation, e.g. if |mapped_size_| is zero. - if (!length) - length = AlignToPageSize(mapped_size_) - offset; - - DCHECK(shared_memory_mapping_.IsValid()); - - // Allow the pages to be discarded by the platform, if supported. - UnlockPages(shared_memory_region_, - AlignToPageSize(sizeof(SharedState)) + offset, length); - - size_t start = offset / base::GetPageSize(); - size_t end = start + length / base::GetPageSize(); - DCHECK_LE(start, end); - DCHECK_LE(end, AlignToPageSize(mapped_size_) / base::GetPageSize()); - - // Remove pages from |locked_page_count_|. - // Note: Unlocking a page that is not locked is an error. - DCHECK_GE(locked_page_count_, end - start); - locked_page_count_ -= end - start; -#if DCHECK_IS_ON() - // Detect incorrect usage by keeping track of exactly what pages are locked. - for (auto page = start; page < end; ++page) { - auto erased_count = locked_pages_.erase(page); - DCHECK_EQ(1u, erased_count); - } - DCHECK_EQ(locked_pages_.size(), locked_page_count_); -#endif - - // Early out and avoid releasing the platform independent lock if some pages - // are still locked. - if (locked_page_count_) - return; - - Time current_time = Now(); - DCHECK(!current_time.is_null()); - - SharedState old_state(SharedState::LOCKED, Time()); - SharedState new_state(SharedState::UNLOCKED, current_time); - // Note: timestamp cannot be NULL as that is a unique value used when - // locked or purged. - DCHECK(!new_state.GetTimestamp().is_null()); - // Timestamp precision should at least be accurate to the second. - DCHECK_EQ((new_state.GetTimestamp() - Time::UnixEpoch()).InSeconds(), - (current_time - Time::UnixEpoch()).InSeconds()); - SharedState result(subtle::Release_CompareAndSwap( - &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i, - old_state.value.i, new_state.value.i)); - - DCHECK_EQ(old_state.value.u, result.value.u); - - last_known_usage_ = current_time; -} - -void* DiscardableSharedMemory::memory() const { - return reinterpret_cast(shared_memory_mapping_.memory()) + - AlignToPageSize(sizeof(SharedState)); -} - -bool DiscardableSharedMemory::Purge(Time current_time) { - // Calls to this function must be synchronized properly. - DFAKE_SCOPED_LOCK(thread_collision_warner_); - DCHECK(shared_memory_mapping_.IsValid()); - - SharedState old_state(SharedState::UNLOCKED, last_known_usage_); - SharedState new_state(SharedState::UNLOCKED, Time()); - SharedState result(subtle::Acquire_CompareAndSwap( - &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i, - old_state.value.i, new_state.value.i)); - - // Update |last_known_usage_| to |current_time| if the memory is locked. This - // allows the caller to determine if purging failed because last known usage - // was incorrect or memory was locked. In the second case, the caller should - // most likely wait for some amount of time before attempting to purge the - // the memory again. - if (result.value.u != old_state.value.u) { - last_known_usage_ = result.GetLockState() == SharedState::LOCKED - ? current_time - : result.GetTimestamp(); - return false; - } - -// The next section will release as much resource as can be done -// from the purging process, until the client process notices the -// purge and releases its own references. -// Note: this memory will not be accessed again. The segment will be -// freed asynchronously at a later time, so just do the best -// immediately. -#if defined(OS_POSIX) && !defined(OS_NACL) -// Linux and Android provide MADV_REMOVE which is preferred as it has a -// behavior that can be verified in tests. Other POSIX flavors (MacOSX, BSDs), -// provide MADV_FREE which has the same result but memory is purged lazily. -#if defined(OS_LINUX) || defined(OS_ANDROID) -#define MADV_PURGE_ARGUMENT MADV_REMOVE -#elif defined(OS_MACOSX) -// MADV_FREE_REUSABLE is similar to MADV_FREE, but also marks the pages with the -// reusable bit, which allows both Activity Monitor and memory-infra to -// correctly track the pages. -#define MADV_PURGE_ARGUMENT MADV_FREE_REUSABLE -#else -#define MADV_PURGE_ARGUMENT MADV_FREE -#endif - // Advise the kernel to remove resources associated with purged pages. - // Subsequent accesses of memory pages will succeed, but might result in - // zero-fill-on-demand pages. - if (madvise(reinterpret_cast(shared_memory_mapping_.memory()) + - AlignToPageSize(sizeof(SharedState)), - AlignToPageSize(mapped_size_), MADV_PURGE_ARGUMENT)) { - DPLOG(ERROR) << "madvise() failed"; - } -#elif defined(OS_WIN) - if (base::win::GetVersion() >= base::win::VERSION_WIN8_1) { - // Discard the purged pages, which releases the physical storage (resident - // memory, compressed or swapped), but leaves them reserved & committed. - // This does not free commit for use by other applications, but allows the - // system to avoid compressing/swapping these pages to free physical memory. - static const auto discard_virtual_memory = - reinterpret_cast(GetProcAddress( - GetModuleHandle(L"kernel32.dll"), "DiscardVirtualMemory")); - if (discard_virtual_memory) { - DWORD discard_result = discard_virtual_memory( - reinterpret_cast(shared_memory_mapping_.memory()) + - AlignToPageSize(sizeof(SharedState)), - AlignToPageSize(mapped_size_)); - if (discard_result != ERROR_SUCCESS) { - DLOG(DCHECK) << "DiscardVirtualMemory() failed in Purge(): " - << logging::SystemErrorCodeToString(discard_result); - } - } - } -#endif - - last_known_usage_ = Time(); - return true; -} - -bool DiscardableSharedMemory::IsMemoryResident() const { - DCHECK(shared_memory_mapping_.IsValid()); - - SharedState result(subtle::NoBarrier_Load( - &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i)); - - return result.GetLockState() == SharedState::LOCKED || - !result.GetTimestamp().is_null(); -} - -bool DiscardableSharedMemory::IsMemoryLocked() const { - DCHECK(shared_memory_mapping_.IsValid()); - - SharedState result(subtle::NoBarrier_Load( - &SharedStateFromSharedMemory(shared_memory_mapping_)->value.i)); - - return result.GetLockState() == SharedState::LOCKED; -} - -void DiscardableSharedMemory::Close() { - shared_memory_region_ = UnsafeSharedMemoryRegion(); -} - -void DiscardableSharedMemory::CreateSharedMemoryOwnershipEdge( - trace_event::MemoryAllocatorDump* local_segment_dump, - trace_event::ProcessMemoryDump* pmd, - bool is_owned) const { - auto* shared_memory_dump = SharedMemoryTracker::GetOrCreateSharedMemoryDump( - shared_memory_mapping_, pmd); - // TODO(ssid): Clean this by a new api to inherit size of parent dump once the - // we send the full PMD and calculate sizes inside chrome, crbug.com/704203. - size_t resident_size = shared_memory_dump->GetSizeInternal(); - local_segment_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize, - trace_event::MemoryAllocatorDump::kUnitsBytes, - resident_size); - - // By creating an edge with a higher |importance| (w.r.t non-owned dumps) - // the tracing UI will account the effective size of the segment to the - // client instead of manager. - // TODO(ssid): Define better constants in MemoryAllocatorDump for importance - // values, crbug.com/754793. - const int kImportance = is_owned ? 2 : 0; - auto shared_memory_guid = shared_memory_mapping_.guid(); - local_segment_dump->AddString("id", "hash", shared_memory_guid.ToString()); - - // Owned discardable segments which are allocated by client process, could - // have been cleared by the discardable manager. So, the segment need not - // exist in memory and weak dumps are created to indicate the UI that the dump - // should exist only if the manager also created the global dump edge. - if (is_owned) { - pmd->CreateWeakSharedMemoryOwnershipEdge(local_segment_dump->guid(), - shared_memory_guid, kImportance); - } else { - pmd->CreateSharedMemoryOwnershipEdge(local_segment_dump->guid(), - shared_memory_guid, kImportance); - } -} - -// static -DiscardableSharedMemory::LockResult DiscardableSharedMemory::LockPages( - const UnsafeSharedMemoryRegion& region, - size_t offset, - size_t length) { -#if defined(OS_ANDROID) - if (region.IsValid()) { - int pin_result = - ashmem_pin_region(region.GetPlatformHandle(), offset, length); - if (pin_result == ASHMEM_WAS_PURGED) - return PURGED; - if (pin_result < 0) - return FAILED; - } -#endif - return SUCCESS; -} - -// static -void DiscardableSharedMemory::UnlockPages( - const UnsafeSharedMemoryRegion& region, - size_t offset, - size_t length) { -#if defined(OS_ANDROID) - if (region.IsValid()) { - int unpin_result = - ashmem_unpin_region(region.GetPlatformHandle(), offset, length); - DCHECK_EQ(0, unpin_result); - } -#endif -} - -Time DiscardableSharedMemory::Now() const { - return Time::Now(); -} - -} // namespace base diff --git a/memory/discardable_shared_memory.h b/memory/discardable_shared_memory.h deleted file mode 100644 index 52a78b131..000000000 --- a/memory/discardable_shared_memory.h +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_DISCARDABLE_SHARED_MEMORY_H_ -#define BASE_MEMORY_DISCARDABLE_SHARED_MEMORY_H_ - -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/shared_memory_mapping.h" -#include "base/memory/unsafe_shared_memory_region.h" -#include "base/threading/thread_collision_warner.h" -#include "base/time/time.h" -#include "build/build_config.h" - -#if DCHECK_IS_ON() -#include -#endif - -// Linux (including Android) support the MADV_REMOVE argument with madvise() -// which has the behavior of reliably causing zero-fill-on-demand pages to -// be returned after a call. Here we define -// DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE on Linux -// and Android to indicate that this type of behavior can be expected on -// those platforms. Note that madvise() will still be used on other POSIX -// platforms but doesn't provide the zero-fill-on-demand pages guarantee. -#if defined(OS_LINUX) || defined(OS_ANDROID) -#define DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE -#endif - -namespace base { - -namespace trace_event { -class MemoryAllocatorDump; -class ProcessMemoryDump; -} // namespace trace_event - -// Platform abstraction for discardable shared memory. -// -// This class is not thread-safe. Clients are responsible for synchronizing -// access to an instance of this class. -class BASE_EXPORT DiscardableSharedMemory { - public: - enum LockResult { SUCCESS, PURGED, FAILED }; - - DiscardableSharedMemory(); - - // Create a new DiscardableSharedMemory object from an existing, open shared - // memory file. Memory must be locked. - explicit DiscardableSharedMemory(UnsafeSharedMemoryRegion region); - - // Closes any open files. - virtual ~DiscardableSharedMemory(); - - // Creates and maps a locked DiscardableSharedMemory object with |size|. - // Returns true on success and false on failure. - bool CreateAndMap(size_t size); - - // Maps the locked discardable memory into the caller's address space. - // Returns true on success, false otherwise. - bool Map(size_t size); - - // Unmaps the discardable shared memory from the caller's address space. - // Unmapping won't unlock previously locked range. - // Returns true if successful; returns false on error or if the memory is - // not mapped. - bool Unmap(); - - // The actual size of the mapped memory (may be larger than requested). - size_t mapped_size() const { return mapped_size_; } - - // Returns a duplicated shared memory region for this DiscardableSharedMemory - // object. - UnsafeSharedMemoryRegion DuplicateRegion() const { - return shared_memory_region_.Duplicate(); - } - - // Returns an ID for the shared memory region. This is ID of the mapped region - // consistent across all processes and is valid as long as the region is not - // unmapped. - const UnguessableToken& mapped_id() const { - return shared_memory_mapping_.guid(); - } - - // Locks a range of memory so that it will not be purged by the system. - // The range of memory must be unlocked. The result of trying to lock an - // already locked range is undefined. |offset| and |length| must both be - // a multiple of the page size as returned by GetPageSize(). - // Passing 0 for |length| means "everything onward". - // Returns SUCCESS if range was successfully locked and the memory is still - // resident, PURGED if range was successfully locked but has been purged - // since last time it was locked and FAILED if range could not be locked. - // Locking can fail for two reasons; object might have been purged, our - // last known usage timestamp might be out of date. Last known usage time - // is updated to the actual last usage timestamp if memory is still resident - // or 0 if not. - LockResult Lock(size_t offset, size_t length); - - // Unlock a previously successfully locked range of memory. The range of - // memory must be locked. The result of trying to unlock a not - // previously locked range is undefined. - // |offset| and |length| must both be a multiple of the page size as returned - // by GetPageSize(). - // Passing 0 for |length| means "everything onward". - void Unlock(size_t offset, size_t length); - - // Gets a pointer to the opened discardable memory space. Discardable memory - // must have been mapped via Map(). - void* memory() const; - - // Returns the last known usage time for DiscardableSharedMemory object. This - // may be earlier than the "true" usage time when memory has been used by a - // different process. Returns NULL time if purged. - Time last_known_usage() const { return last_known_usage_; } - - // This returns true and sets |last_known_usage_| to 0 if - // DiscardableSharedMemory object was successfully purged. Purging can fail - // for two reasons; object might be locked or our last known usage timestamp - // might be out of date. Last known usage time is updated to |current_time| - // if locked or the actual last usage timestamp if unlocked. It is often - // necessary to call this function twice for the object to successfully be - // purged. First call, updates |last_known_usage_|. Second call, successfully - // purges the object using the updated |last_known_usage_|. - // Note: there is no guarantee that multiple calls to this function will - // successfully purge object. DiscardableSharedMemory object might be locked - // or another thread/process might be able to lock and unlock it in between - // each call. - bool Purge(Time current_time); - - // Returns true if memory is still resident. - bool IsMemoryResident() const; - - // Returns true if memory is locked. - bool IsMemoryLocked() const; - - // Closes the open discardable memory segment. - // It is safe to call Close repeatedly. - void Close(); - - // For tracing: Creates ownership edge to the underlying shared memory dump - // which is cross process in the given |pmd|. |local_segment_dump| is the dump - // associated with the local discardable shared memory segment and |is_owned| - // is true when the current process owns the segment and the effective memory - // is assigned to the current process. - void CreateSharedMemoryOwnershipEdge( - trace_event::MemoryAllocatorDump* local_segment_dump, - trace_event::ProcessMemoryDump* pmd, - bool is_owned) const; - - private: - // LockPages/UnlockPages are platform-native discardable page management - // helper functions. Both expect |offset| to be specified relative to the - // base address at which |memory| is mapped, and that |offset| and |length| - // are page-aligned by the caller. - // Returns SUCCESS on platforms which do not support discardable pages. - static LockResult LockPages(const UnsafeSharedMemoryRegion& region, - size_t offset, - size_t length); - // UnlockPages() is a no-op on platforms not supporting discardable pages. - static void UnlockPages(const UnsafeSharedMemoryRegion& region, - size_t offset, - size_t length); - - // Virtual for tests. - virtual Time Now() const; - - UnsafeSharedMemoryRegion shared_memory_region_; - WritableSharedMemoryMapping shared_memory_mapping_; - size_t mapped_size_; - size_t locked_page_count_; -#if DCHECK_IS_ON() - std::set locked_pages_; -#endif - // Implementation is not thread-safe but still usable if clients are - // synchronized somehow. Use a collision warner to detect incorrect usage. - DFAKE_MUTEX(thread_collision_warner_); - Time last_known_usage_; - - DISALLOW_COPY_AND_ASSIGN(DiscardableSharedMemory); -}; - -} // namespace base - -#endif // BASE_MEMORY_DISCARDABLE_SHARED_MEMORY_H_ diff --git a/memory/discardable_shared_memory_unittest.cc b/memory/discardable_shared_memory_unittest.cc deleted file mode 100644 index b3d21a7bd..000000000 --- a/memory/discardable_shared_memory_unittest.cc +++ /dev/null @@ -1,456 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "base/files/scoped_file.h" -#include "base/memory/discardable_shared_memory.h" -#include "base/memory/shared_memory_tracker.h" -#include "base/process/process_metrics.h" -#include "base/trace_event/memory_allocator_dump.h" -#include "base/trace_event/process_memory_dump.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -class TestDiscardableSharedMemory : public DiscardableSharedMemory { - public: - TestDiscardableSharedMemory() = default; - - explicit TestDiscardableSharedMemory(UnsafeSharedMemoryRegion region) - : DiscardableSharedMemory(std::move(region)) {} - - void SetNow(Time now) { now_ = now; } - - private: - // Overriden from DiscardableSharedMemory: - Time Now() const override { return now_; } - - Time now_; -}; - -TEST(DiscardableSharedMemoryTest, CreateAndMap) { - const uint32_t kDataSize = 1024; - - TestDiscardableSharedMemory memory; - bool rv = memory.CreateAndMap(kDataSize); - ASSERT_TRUE(rv); - EXPECT_GE(memory.mapped_size(), kDataSize); - EXPECT_TRUE(memory.IsMemoryLocked()); -} - -TEST(DiscardableSharedMemoryTest, CreateFromHandle) { - const uint32_t kDataSize = 1024; - - TestDiscardableSharedMemory memory1; - bool rv = memory1.CreateAndMap(kDataSize); - ASSERT_TRUE(rv); - - UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion(); - ASSERT_TRUE(shared_region.IsValid()); - - TestDiscardableSharedMemory memory2(std::move(shared_region)); - rv = memory2.Map(kDataSize); - ASSERT_TRUE(rv); - EXPECT_TRUE(memory2.IsMemoryLocked()); -} - -TEST(DiscardableSharedMemoryTest, LockAndUnlock) { - const uint32_t kDataSize = 1024; - - TestDiscardableSharedMemory memory1; - bool rv = memory1.CreateAndMap(kDataSize); - ASSERT_TRUE(rv); - - // Memory is initially locked. Unlock it. - memory1.SetNow(Time::FromDoubleT(1)); - memory1.Unlock(0, 0); - EXPECT_FALSE(memory1.IsMemoryLocked()); - - // Lock and unlock memory. - DiscardableSharedMemory::LockResult lock_rv = memory1.Lock(0, 0); - EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv); - memory1.SetNow(Time::FromDoubleT(2)); - memory1.Unlock(0, 0); - - // Lock again before duplicating and passing ownership to new instance. - lock_rv = memory1.Lock(0, 0); - EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv); - EXPECT_TRUE(memory1.IsMemoryLocked()); - - UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion(); - ASSERT_TRUE(shared_region.IsValid()); - - TestDiscardableSharedMemory memory2(std::move(shared_region)); - rv = memory2.Map(kDataSize); - ASSERT_TRUE(rv); - - // Unlock second instance. - memory2.SetNow(Time::FromDoubleT(3)); - memory2.Unlock(0, 0); - - // Both memory instances should be unlocked now. - EXPECT_FALSE(memory2.IsMemoryLocked()); - EXPECT_FALSE(memory1.IsMemoryLocked()); - - // Lock second instance before passing ownership back to first instance. - lock_rv = memory2.Lock(0, 0); - EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv); - - // Memory should still be resident and locked. - rv = memory1.IsMemoryResident(); - EXPECT_TRUE(rv); - EXPECT_TRUE(memory1.IsMemoryLocked()); - - // Unlock first instance. - memory1.SetNow(Time::FromDoubleT(4)); - memory1.Unlock(0, 0); -} - -TEST(DiscardableSharedMemoryTest, Purge) { - const uint32_t kDataSize = 1024; - - TestDiscardableSharedMemory memory1; - bool rv = memory1.CreateAndMap(kDataSize); - ASSERT_TRUE(rv); - - UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion(); - ASSERT_TRUE(shared_region.IsValid()); - - TestDiscardableSharedMemory memory2(std::move(shared_region)); - rv = memory2.Map(kDataSize); - ASSERT_TRUE(rv); - - // This should fail as memory is locked. - rv = memory1.Purge(Time::FromDoubleT(1)); - EXPECT_FALSE(rv); - - memory2.SetNow(Time::FromDoubleT(2)); - memory2.Unlock(0, 0); - - ASSERT_TRUE(memory2.IsMemoryResident()); - - // Memory is unlocked, but our usage timestamp is incorrect. - rv = memory1.Purge(Time::FromDoubleT(3)); - EXPECT_FALSE(rv); - - ASSERT_TRUE(memory2.IsMemoryResident()); - - // Memory is unlocked and our usage timestamp should be correct. - rv = memory1.Purge(Time::FromDoubleT(4)); - EXPECT_TRUE(rv); - - // Lock should fail as memory has been purged. - DiscardableSharedMemory::LockResult lock_rv = memory2.Lock(0, 0); - EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv); - - ASSERT_FALSE(memory2.IsMemoryResident()); -} - -TEST(DiscardableSharedMemoryTest, LastUsed) { - const uint32_t kDataSize = 1024; - - TestDiscardableSharedMemory memory1; - bool rv = memory1.CreateAndMap(kDataSize); - ASSERT_TRUE(rv); - - UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion(); - ASSERT_TRUE(shared_region.IsValid()); - - TestDiscardableSharedMemory memory2(std::move(shared_region)); - rv = memory2.Map(kDataSize); - ASSERT_TRUE(rv); - - memory2.SetNow(Time::FromDoubleT(1)); - memory2.Unlock(0, 0); - - EXPECT_EQ(memory2.last_known_usage(), Time::FromDoubleT(1)); - - DiscardableSharedMemory::LockResult lock_rv = memory2.Lock(0, 0); - EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv); - - // This should fail as memory is locked. - rv = memory1.Purge(Time::FromDoubleT(2)); - ASSERT_FALSE(rv); - - // Last usage should have been updated to timestamp passed to Purge above. - EXPECT_EQ(memory1.last_known_usage(), Time::FromDoubleT(2)); - - memory2.SetNow(Time::FromDoubleT(3)); - memory2.Unlock(0, 0); - - // Usage time should be correct for |memory2| instance. - EXPECT_EQ(memory2.last_known_usage(), Time::FromDoubleT(3)); - - // However, usage time has not changed as far as |memory1| instance knows. - EXPECT_EQ(memory1.last_known_usage(), Time::FromDoubleT(2)); - - // Memory is unlocked, but our usage timestamp is incorrect. - rv = memory1.Purge(Time::FromDoubleT(4)); - EXPECT_FALSE(rv); - - // The failed purge attempt should have updated usage time to the correct - // value. - EXPECT_EQ(memory1.last_known_usage(), Time::FromDoubleT(3)); - - // Purge memory through |memory2| instance. The last usage time should be - // set to 0 as a result of this. - rv = memory2.Purge(Time::FromDoubleT(5)); - EXPECT_TRUE(rv); - EXPECT_TRUE(memory2.last_known_usage().is_null()); - - // This should fail as memory has already been purged and |memory1|'s usage - // time is incorrect as a result. - rv = memory1.Purge(Time::FromDoubleT(6)); - EXPECT_FALSE(rv); - - // The failed purge attempt should have updated usage time to the correct - // value. - EXPECT_TRUE(memory1.last_known_usage().is_null()); - - // Purge should succeed now that usage time is correct. - rv = memory1.Purge(Time::FromDoubleT(7)); - EXPECT_TRUE(rv); -} - -TEST(DiscardableSharedMemoryTest, LockShouldAlwaysFailAfterSuccessfulPurge) { - const uint32_t kDataSize = 1024; - - TestDiscardableSharedMemory memory1; - bool rv = memory1.CreateAndMap(kDataSize); - ASSERT_TRUE(rv); - - UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion(); - ASSERT_TRUE(shared_region.IsValid()); - - TestDiscardableSharedMemory memory2(std::move(shared_region)); - rv = memory2.Map(kDataSize); - ASSERT_TRUE(rv); - - memory2.SetNow(Time::FromDoubleT(1)); - memory2.Unlock(0, 0); - - rv = memory2.Purge(Time::FromDoubleT(2)); - EXPECT_TRUE(rv); - - // Lock should fail as memory has been purged. - DiscardableSharedMemory::LockResult lock_rv = memory2.Lock(0, 0); - EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv); -} - -#if defined(OS_ANDROID) -TEST(DiscardableSharedMemoryTest, LockShouldFailIfPlatformLockPagesFails) { - const uint32_t kDataSize = 1024; - - DiscardableSharedMemory memory1; - bool rv1 = memory1.CreateAndMap(kDataSize); - ASSERT_TRUE(rv1); - - base::UnsafeSharedMemoryRegion region = memory1.DuplicateRegion(); - int fd = region.GetPlatformHandle(); - DiscardableSharedMemory memory2(std::move(region)); - bool rv2 = memory2.Map(kDataSize); - ASSERT_TRUE(rv2); - - // Unlock() the first page of memory, so we can test Lock()ing it. - memory2.Unlock(0, base::GetPageSize()); - // To cause ashmem_pin_region() to fail, we arrange for it to be called with - // an invalid file-descriptor, which requires a valid-looking fd (i.e. we - // can't just Close() |memory|), but one on which the operation is invalid. - // We can overwrite the |memory| fd with a handle to a different file using - // dup2(), which has the nice properties that |memory| still has a valid fd - // that it can close, etc without errors, but on which ashmem_pin_region() - // will fail. - base::ScopedFD null(open("/dev/null", O_RDONLY)); - ASSERT_EQ(fd, dup2(null.get(), fd)); - - // Now re-Lock()ing the first page should fail. - DiscardableSharedMemory::LockResult lock_rv = - memory2.Lock(0, base::GetPageSize()); - EXPECT_EQ(DiscardableSharedMemory::FAILED, lock_rv); -} -#endif // defined(OS_ANDROID) - -TEST(DiscardableSharedMemoryTest, LockAndUnlockRange) { - const uint32_t kDataSize = 32; - - uint32_t data_size_in_bytes = kDataSize * base::GetPageSize(); - - TestDiscardableSharedMemory memory1; - bool rv = memory1.CreateAndMap(data_size_in_bytes); - ASSERT_TRUE(rv); - - UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion(); - ASSERT_TRUE(shared_region.IsValid()); - - TestDiscardableSharedMemory memory2(std::move(shared_region)); - rv = memory2.Map(data_size_in_bytes); - ASSERT_TRUE(rv); - - // Unlock first page. - memory2.SetNow(Time::FromDoubleT(1)); - memory2.Unlock(0, base::GetPageSize()); - - rv = memory1.Purge(Time::FromDoubleT(2)); - EXPECT_FALSE(rv); - - // Lock first page again. - memory2.SetNow(Time::FromDoubleT(3)); - DiscardableSharedMemory::LockResult lock_rv = - memory2.Lock(0, base::GetPageSize()); - EXPECT_NE(DiscardableSharedMemory::FAILED, lock_rv); - - // Unlock first page. - memory2.SetNow(Time::FromDoubleT(4)); - memory2.Unlock(0, base::GetPageSize()); - - rv = memory1.Purge(Time::FromDoubleT(5)); - EXPECT_FALSE(rv); - - // Unlock second page. - memory2.SetNow(Time::FromDoubleT(6)); - memory2.Unlock(base::GetPageSize(), base::GetPageSize()); - - rv = memory1.Purge(Time::FromDoubleT(7)); - EXPECT_FALSE(rv); - - // Unlock anything onwards. - memory2.SetNow(Time::FromDoubleT(8)); - memory2.Unlock(2 * base::GetPageSize(), 0); - - // Memory is unlocked, but our usage timestamp is incorrect. - rv = memory1.Purge(Time::FromDoubleT(9)); - EXPECT_FALSE(rv); - - // The failed purge attempt should have updated usage time to the correct - // value. - EXPECT_EQ(Time::FromDoubleT(8), memory1.last_known_usage()); - - // Purge should now succeed. - rv = memory1.Purge(Time::FromDoubleT(10)); - EXPECT_TRUE(rv); -} - -TEST(DiscardableSharedMemoryTest, MappedSize) { - const uint32_t kDataSize = 1024; - - TestDiscardableSharedMemory memory; - bool rv = memory.CreateAndMap(kDataSize); - ASSERT_TRUE(rv); - - EXPECT_LE(kDataSize, memory.mapped_size()); - - // Mapped size should be 0 after memory segment has been unmapped. - rv = memory.Unmap(); - EXPECT_TRUE(rv); - EXPECT_EQ(0u, memory.mapped_size()); -} - -TEST(DiscardableSharedMemoryTest, Close) { - const uint32_t kDataSize = 1024; - - TestDiscardableSharedMemory memory; - bool rv = memory.CreateAndMap(kDataSize); - ASSERT_TRUE(rv); - - // Mapped size should be unchanged after memory segment has been closed. - memory.Close(); - EXPECT_LE(kDataSize, memory.mapped_size()); - - // Memory is initially locked. Unlock it. - memory.SetNow(Time::FromDoubleT(1)); - memory.Unlock(0, 0); - - // Lock and unlock memory. - DiscardableSharedMemory::LockResult lock_rv = memory.Lock(0, 0); - EXPECT_EQ(DiscardableSharedMemory::SUCCESS, lock_rv); - memory.SetNow(Time::FromDoubleT(2)); - memory.Unlock(0, 0); -} - -TEST(DiscardableSharedMemoryTest, ZeroSize) { - TestDiscardableSharedMemory memory; - bool rv = memory.CreateAndMap(0); - ASSERT_TRUE(rv); - - EXPECT_LE(0u, memory.mapped_size()); - - // Memory is initially locked. Unlock it. - memory.SetNow(Time::FromDoubleT(1)); - memory.Unlock(0, 0); - - // Lock and unlock memory. - DiscardableSharedMemory::LockResult lock_rv = memory.Lock(0, 0); - EXPECT_NE(DiscardableSharedMemory::FAILED, lock_rv); - memory.SetNow(Time::FromDoubleT(2)); - memory.Unlock(0, 0); -} - -// This test checks that zero-filled pages are returned after purging a segment -// when DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE is -// defined and MADV_REMOVE is supported. -#if defined(DISCARDABLE_SHARED_MEMORY_ZERO_FILL_ON_DEMAND_PAGES_AFTER_PURGE) -TEST(DiscardableSharedMemoryTest, ZeroFilledPagesAfterPurge) { - const uint32_t kDataSize = 1024; - - TestDiscardableSharedMemory memory1; - bool rv = memory1.CreateAndMap(kDataSize); - ASSERT_TRUE(rv); - - UnsafeSharedMemoryRegion shared_region = memory1.DuplicateRegion(); - ASSERT_TRUE(shared_region.IsValid()); - - TestDiscardableSharedMemory memory2(std::move(shared_region)); - rv = memory2.Map(kDataSize); - ASSERT_TRUE(rv); - - // Initialize all memory to '0xaa'. - memset(memory2.memory(), 0xaa, kDataSize); - - // Unlock memory. - memory2.SetNow(Time::FromDoubleT(1)); - memory2.Unlock(0, 0); - EXPECT_FALSE(memory1.IsMemoryLocked()); - - // Memory is unlocked, but our usage timestamp is incorrect. - rv = memory1.Purge(Time::FromDoubleT(2)); - EXPECT_FALSE(rv); - rv = memory1.Purge(Time::FromDoubleT(3)); - EXPECT_TRUE(rv); - - // Check that reading memory after it has been purged is returning - // zero-filled pages. - uint8_t expected_data[kDataSize] = {}; - EXPECT_EQ(memcmp(memory2.memory(), expected_data, kDataSize), 0); -} -#endif - -TEST(DiscardableSharedMemoryTest, TracingOwnershipEdges) { - const uint32_t kDataSize = 1024; - TestDiscardableSharedMemory memory1; - bool rv = memory1.CreateAndMap(kDataSize); - ASSERT_TRUE(rv); - - base::trace_event::MemoryDumpArgs args = { - base::trace_event::MemoryDumpLevelOfDetail::DETAILED}; - trace_event::ProcessMemoryDump pmd(args); - trace_event::MemoryAllocatorDump* client_dump = - pmd.CreateAllocatorDump("discardable_manager/map1"); - const bool is_owned = false; - memory1.CreateSharedMemoryOwnershipEdge(client_dump, &pmd, is_owned); - const auto* shm_dump = pmd.GetAllocatorDump( - SharedMemoryTracker::GetDumpNameForTracing(memory1.mapped_id())); - EXPECT_TRUE(shm_dump); - EXPECT_EQ(shm_dump->GetSizeInternal(), client_dump->GetSizeInternal()); - const auto edges = pmd.allocator_dumps_edges(); - EXPECT_EQ(2u, edges.size()); - EXPECT_NE(edges.end(), edges.find(shm_dump->guid())); - EXPECT_NE(edges.end(), edges.find(client_dump->guid())); - // TODO(ssid): test for weak global dump once the - // CreateWeakSharedMemoryOwnershipEdge() is fixed, crbug.com/661257. -} - -} // namespace base diff --git a/memory/free_deleter.h b/memory/free_deleter.h deleted file mode 100644 index 560411886..000000000 --- a/memory/free_deleter.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2016 The Chromium 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_FREE_DELETER_H_ -#define BASE_MEMORY_FREE_DELETER_H_ - -#include - -namespace base { - -// Function object which invokes 'free' on its parameter, which must be -// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr: -// -// std::unique_ptr foo_ptr( -// static_cast(malloc(sizeof(int)))); -struct FreeDeleter { - inline void operator()(void* ptr) const { - free(ptr); - } -}; - -} // namespace base - -#endif // BASE_MEMORY_FREE_DELETER_H_ diff --git a/memory/linked_ptr.h b/memory/linked_ptr.h deleted file mode 100644 index 68512864b..000000000 --- a/memory/linked_ptr.h +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// A "smart" pointer type with reference tracking. Every pointer to a -// particular object is kept on a circular linked list. When the last pointer -// to an object is destroyed or reassigned, the object is deleted. -// -// Used properly, this deletes the object when the last reference goes away. -// There are several caveats: -// - Like all reference counting schemes, cycles lead to leaks. -// - Each smart pointer is actually two pointers (8 bytes instead of 4). -// - Every time a pointer is released, the entire list of pointers to that -// object is traversed. This class is therefore NOT SUITABLE when there -// will often be more than two or three pointers to a particular object. -// - References are only tracked as long as linked_ptr<> objects are copied. -// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS -// will happen (double deletion). -// -// Note: If you use an incomplete type with linked_ptr<>, the class -// *containing* linked_ptr<> must have a constructor and destructor (even -// if they do nothing!). -// -// Thread Safety: -// A linked_ptr is NOT thread safe. Copying a linked_ptr object is -// effectively a read-write operation. -// -// Alternative: to linked_ptr is shared_ptr, which -// - is also two pointers in size (8 bytes for 32 bit addresses) -// - is thread safe for copying and deletion -// - supports weak_ptrs - -#ifndef BASE_MEMORY_LINKED_PTR_H_ -#define BASE_MEMORY_LINKED_PTR_H_ - -#include "base/logging.h" // for CHECK macros - -// This is used internally by all instances of linked_ptr<>. It needs to be -// a non-template class because different types of linked_ptr<> can refer to -// the same object (linked_ptr(obj) vs linked_ptr(obj)). -// So, it needs to be possible for different types of linked_ptr to participate -// in the same circular linked list, so we need a single class type here. -// -// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. -class linked_ptr_internal { - public: - // Create a new circle that includes only this instance. - void join_new() { - next_ = this; - } - - // Join an existing circle. - void join(linked_ptr_internal const* ptr) { - next_ = ptr->next_; - ptr->next_ = this; - } - - // Leave whatever circle we're part of. Returns true iff we were the - // last member of the circle. Once this is done, you can join() another. - bool depart() { - if (next_ == this) return true; - linked_ptr_internal const* p = next_; - while (p->next_ != this) p = p->next_; - p->next_ = next_; - return false; - } - - private: - mutable linked_ptr_internal const* next_; -}; - -// TODO(http://crbug.com/556939): DEPRECATED: Use unique_ptr instead (now that -// we have support for moveable types inside STL containers). -template -class linked_ptr { - public: - typedef T element_type; - - // Take over ownership of a raw pointer. This should happen as soon as - // possible after the object is created. - explicit linked_ptr(T* ptr = NULL) { capture(ptr); } - ~linked_ptr() { depart(); } - - // Copy an existing linked_ptr<>, adding ourselves to the list of references. - template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } - - linked_ptr(linked_ptr const& ptr) { - DCHECK_NE(&ptr, this); - copy(&ptr); - } - - // Assignment releases the old value and acquires the new. - template linked_ptr& operator=(linked_ptr const& ptr) { - depart(); - copy(&ptr); - return *this; - } - - linked_ptr& operator=(linked_ptr const& ptr) { - if (&ptr != this) { - depart(); - copy(&ptr); - } - return *this; - } - - // Smart pointer members. - void reset(T* ptr = NULL) { - depart(); - capture(ptr); - } - T* get() const { return value_; } - T* operator->() const { return value_; } - T& operator*() const { return *value_; } - // Release ownership of the pointed object and returns it. - // Sole ownership by this linked_ptr object is required. - T* release() { - bool last = link_.depart(); - CHECK(last); - T* v = value_; - value_ = NULL; - return v; - } - - bool operator==(const T* p) const { return value_ == p; } - bool operator!=(const T* p) const { return value_ != p; } - template - bool operator==(linked_ptr const& ptr) const { - return value_ == ptr.get(); - } - template - bool operator!=(linked_ptr const& ptr) const { - return value_ != ptr.get(); - } - - private: - template - friend class linked_ptr; - - T* value_; - linked_ptr_internal link_; - - void depart() { - if (link_.depart()) delete value_; - } - - void capture(T* ptr) { - value_ = ptr; - link_.join_new(); - } - - template void copy(linked_ptr const* ptr) { - value_ = ptr->get(); - if (value_) - link_.join(&ptr->link_); - else - link_.join_new(); - } -}; - -template inline -bool operator==(T* ptr, const linked_ptr& x) { - return ptr == x.get(); -} - -template inline -bool operator!=(T* ptr, const linked_ptr& x) { - return ptr != x.get(); -} - -// A function to convert T* into linked_ptr -// Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation -// for linked_ptr >(new FooBarBaz(arg)) -template -linked_ptr make_linked_ptr(T* ptr) { - return linked_ptr(ptr); -} - -#endif // BASE_MEMORY_LINKED_PTR_H_ diff --git a/memory/linked_ptr_unittest.cc b/memory/linked_ptr_unittest.cc deleted file mode 100644 index 344ffa48d..000000000 --- a/memory/linked_ptr_unittest.cc +++ /dev/null @@ -1,108 +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 - -#include "base/memory/linked_ptr.h" -#include "base/strings/stringprintf.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -int num = 0; - -std::string history; - -// Class which tracks allocation/deallocation -struct A { - A(): mynum(num++) { history += base::StringPrintf("A%d ctor\n", mynum); } - virtual ~A() { history += base::StringPrintf("A%d dtor\n", mynum); } - virtual void Use() { history += base::StringPrintf("A%d use\n", mynum); } - int mynum; -}; - -// Subclass -struct B: public A { - B() { history += base::StringPrintf("B%d ctor\n", mynum); } - ~B() override { history += base::StringPrintf("B%d dtor\n", mynum); } - void Use() override { history += base::StringPrintf("B%d use\n", mynum); } -}; - -} // namespace - -TEST(LinkedPtrTest, Test) { - { - linked_ptr a0, a1, a2; - a0 = *&a0; // The *& defeats Clang's -Wself-assign warning. - a1 = a2; - ASSERT_EQ(a0.get(), static_cast(nullptr)); - ASSERT_EQ(a1.get(), static_cast(nullptr)); - ASSERT_EQ(a2.get(), static_cast(nullptr)); - ASSERT_TRUE(a0 == nullptr); - ASSERT_TRUE(a1 == nullptr); - ASSERT_TRUE(a2 == nullptr); - - { - linked_ptr a3(new A); - a0 = a3; - ASSERT_TRUE(a0 == a3); - ASSERT_TRUE(a0 != nullptr); - ASSERT_TRUE(a0.get() == a3); - ASSERT_TRUE(a0 == a3.get()); - linked_ptr a4(a0); - a1 = a4; - linked_ptr a5(new A); - ASSERT_TRUE(a5.get() != a3); - ASSERT_TRUE(a5 != a3.get()); - a2 = a5; - linked_ptr b0(new B); - linked_ptr a6(b0); - ASSERT_TRUE(b0 == a6); - ASSERT_TRUE(a6 == b0); - ASSERT_TRUE(b0 != nullptr); - a5 = b0; - a5 = b0; - a3->Use(); - a4->Use(); - a5->Use(); - a6->Use(); - b0->Use(); - (*b0).Use(); - b0.get()->Use(); - } - - a0->Use(); - a1->Use(); - a2->Use(); - - a1 = a2; - a2.reset(new A); - a0.reset(); - - linked_ptr a7; - } - - ASSERT_EQ(history, - "A0 ctor\n" - "A1 ctor\n" - "A2 ctor\n" - "B2 ctor\n" - "A0 use\n" - "A0 use\n" - "B2 use\n" - "B2 use\n" - "B2 use\n" - "B2 use\n" - "B2 use\n" - "B2 dtor\n" - "A2 dtor\n" - "A0 use\n" - "A0 use\n" - "A1 use\n" - "A3 ctor\n" - "A0 dtor\n" - "A3 dtor\n" - "A1 dtor\n" - ); -} diff --git a/memory/memory_coordinator_client.cc b/memory/memory_coordinator_client.cc deleted file mode 100644 index 7fa623217..000000000 --- a/memory/memory_coordinator_client.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_coordinator_client.h" - -#include "base/logging.h" - -namespace base { - -const char* MemoryStateToString(MemoryState state) { - switch (state) { - case MemoryState::UNKNOWN: - return "unknown"; - case MemoryState::NORMAL: - return "normal"; - case MemoryState::THROTTLED: - return "throttled"; - case MemoryState::SUSPENDED: - return "suspended"; - default: - NOTREACHED(); - } - return ""; -} - -} // namespace base diff --git a/memory/memory_coordinator_client.h b/memory/memory_coordinator_client.h deleted file mode 100644 index 804f0a6b8..000000000 --- a/memory/memory_coordinator_client.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_MEMORY_COORDINATOR_CLIENT_H_ -#define BASE_MEMORY_MEMORY_COORDINATOR_CLIENT_H_ - -#include "base/base_export.h" - -namespace base { - -// OVERVIEW: -// -// MemoryCoordinatorClient is an interface which a component can implement to -// adjust "future allocation" and "existing allocation". For "future allocation" -// it provides a callback to observe memory state changes, and for "existing -// allocation" it provides a callback to purge memory. -// -// Unlike MemoryPressureListener, memory state changes are stateful. State -// transitions are throttled to avoid thrashing; the exact throttling period is -// platform dependent, but will be at least 5-10 seconds. When a state change -// notification is dispatched, clients are expected to update their allocation -// policies (e.g. setting cache limit) that persist for the duration of the -// memory state. Note that clients aren't expected to free up memory on memory -// state changes. Clients should wait for a separate purge request to free up -// memory. Purging requests will be throttled as well. - -// MemoryState is an indicator that processes can use to guide their memory -// allocation policies. For example, a process that receives the throttled -// state can use that as as signal to decrease memory cache limits. -// NOTE: This enum is used to back an UMA histogram, and therefore should be -// treated as append-only. -enum class MemoryState : int { - // The state is unknown. - UNKNOWN = -1, - // No memory constraints. - NORMAL = 0, - // Running and interactive but memory allocation should be throttled. - // Clients should set lower budget for any memory that is used as an - // optimization but that is not necessary for the process to run. - // (e.g. caches) - THROTTLED = 1, - // Still resident in memory but core processing logic has been suspended. - // In most cases, OnPurgeMemory() will be called before entering this state. - SUSPENDED = 2, -}; - -const int kMemoryStateMax = static_cast(MemoryState::SUSPENDED) + 1; - -// Returns a string representation of MemoryState. -BASE_EXPORT const char* MemoryStateToString(MemoryState state); - -// This is an interface for components which can respond to memory status -// changes. An initial state is NORMAL. See MemoryCoordinatorClientRegistry for -// threading guarantees and ownership management. -class BASE_EXPORT MemoryCoordinatorClient { - public: - // Called when memory state has changed. Any transition can occur except for - // UNKNOWN. General guidelines are: - // * NORMAL: Restore the default settings for memory allocation/usage if - // it has changed. - // * THROTTLED: Use smaller limits for future memory allocations. You don't - // need to take any action on existing allocations. - // * SUSPENDED: Use much smaller limits for future memory allocations. You - // don't need to take any action on existing allocations. - virtual void OnMemoryStateChange(MemoryState state) {} - - // Called to purge memory. - // This callback should free up any memory that is used as an optimization, or - // any memory whose contents can be reproduced. - virtual void OnPurgeMemory() {} - - protected: - virtual ~MemoryCoordinatorClient() = default; -}; - -} // namespace base - -#endif // BASE_MEMORY_MEMORY_COORDINATOR_CLIENT_H_ diff --git a/memory/memory_coordinator_client_registry.cc b/memory/memory_coordinator_client_registry.cc deleted file mode 100644 index 45b4a7f5b..000000000 --- a/memory/memory_coordinator_client_registry.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_coordinator_client_registry.h" - -namespace base { - -// static -MemoryCoordinatorClientRegistry* -MemoryCoordinatorClientRegistry::GetInstance() { - return Singleton< - MemoryCoordinatorClientRegistry, - LeakySingletonTraits>::get(); -} - -MemoryCoordinatorClientRegistry::MemoryCoordinatorClientRegistry() - : clients_(new ClientList) {} - -MemoryCoordinatorClientRegistry::~MemoryCoordinatorClientRegistry() = default; - -void MemoryCoordinatorClientRegistry::Register( - MemoryCoordinatorClient* client) { - clients_->AddObserver(client); -} - -void MemoryCoordinatorClientRegistry::Unregister( - MemoryCoordinatorClient* client) { - clients_->RemoveObserver(client); -} - -void MemoryCoordinatorClientRegistry::Notify(MemoryState state) { - clients_->Notify(FROM_HERE, - &base::MemoryCoordinatorClient::OnMemoryStateChange, state); -} - -void MemoryCoordinatorClientRegistry::PurgeMemory() { - clients_->Notify(FROM_HERE, &base::MemoryCoordinatorClient::OnPurgeMemory); -} - -} // namespace base diff --git a/memory/memory_coordinator_client_registry.h b/memory/memory_coordinator_client_registry.h deleted file mode 100644 index e2c81b718..000000000 --- a/memory/memory_coordinator_client_registry.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_MEMORY_CLIENT_REGISTRY_H_ -#define BASE_MEMORY_MEMORY_CLIENT_REGISTRY_H_ - -#include "base/base_export.h" -#include "base/memory/memory_coordinator_client.h" -#include "base/memory/singleton.h" -#include "base/observer_list_threadsafe.h" - -namespace base { - -// MemoryCoordinatorClientRegistry is the registry of MemoryCoordinatorClients. -// This class manages clients and provides a way to notify memory state changes -// to clients, but this isn't responsible to determine how/when to change -// memory states. -// -// Threading guarantees: -// This class uses ObserverListThreadsafe internally, which means that -// * Registering/unregistering callbacks are thread-safe. -// * Callbacks are invoked on the same thread on which they are registered. -// See base/observer_list_threadsafe.h for reference. -// -// Ownership management: -// This class doesn't take the ownership of clients. Clients must be -// unregistered before they are destroyed. -class BASE_EXPORT MemoryCoordinatorClientRegistry { - public: - static MemoryCoordinatorClientRegistry* GetInstance(); - - ~MemoryCoordinatorClientRegistry(); - - // Registers/unregisters a client. Does not take ownership of client. - void Register(MemoryCoordinatorClient* client); - void Unregister(MemoryCoordinatorClient* client); - - // Notify clients of a memory state change. - void Notify(MemoryState state); - - // Requests purging memory. - void PurgeMemory(); - - private: - friend struct DefaultSingletonTraits; - - MemoryCoordinatorClientRegistry(); - - using ClientList = ObserverListThreadSafe; - scoped_refptr clients_; -}; - -} // namespace base - -#endif // BASE_MEMORY_MEMORY_CLIENT_REGISTRY_H_ diff --git a/memory/memory_coordinator_client_registry_unittest.cc b/memory/memory_coordinator_client_registry_unittest.cc deleted file mode 100644 index 37ed7673d..000000000 --- a/memory/memory_coordinator_client_registry_unittest.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_coordinator_client_registry.h" - -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -class TestMemoryCoordinatorClient : public MemoryCoordinatorClient { - public: - void OnMemoryStateChange(MemoryState state) override { state_ = state; } - - void OnPurgeMemory() override { ++purge_count_; } - - MemoryState state() const { return state_; } - size_t purge_count() const { return purge_count_; } - - private: - MemoryState state_ = MemoryState::UNKNOWN; - size_t purge_count_ = 0; -}; - -void RunUntilIdle() { - base::RunLoop loop; - loop.RunUntilIdle(); -} - -TEST(MemoryCoordinatorClientRegistryTest, NotifyStateChange) { - MessageLoop loop; - auto* registry = MemoryCoordinatorClientRegistry::GetInstance(); - TestMemoryCoordinatorClient client; - registry->Register(&client); - registry->Notify(MemoryState::THROTTLED); - RunUntilIdle(); - ASSERT_EQ(MemoryState::THROTTLED, client.state()); - registry->Unregister(&client); -} - -TEST(MemoryCoordinatorClientRegistryTest, PurgeMemory) { - MessageLoop loop; - auto* registry = MemoryCoordinatorClientRegistry::GetInstance(); - TestMemoryCoordinatorClient client; - registry->Register(&client); - registry->PurgeMemory(); - RunUntilIdle(); - ASSERT_EQ(1u, client.purge_count()); - registry->Unregister(&client); -} - -} // namespace - -} // namespace base diff --git a/memory/memory_coordinator_proxy.cc b/memory/memory_coordinator_proxy.cc deleted file mode 100644 index 4e22fe04f..000000000 --- a/memory/memory_coordinator_proxy.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_coordinator_proxy.h" - -namespace base { - -namespace { - -MemoryCoordinator* g_memory_coordinator = nullptr; - -} // namespace - -MemoryCoordinatorProxy::MemoryCoordinatorProxy() = default; - -MemoryCoordinatorProxy::~MemoryCoordinatorProxy() = default; - -// static -MemoryCoordinatorProxy* MemoryCoordinatorProxy::GetInstance() { - return Singleton::get(); -} - -// static -void MemoryCoordinatorProxy::SetMemoryCoordinator( - MemoryCoordinator* coordinator) { - DCHECK(!g_memory_coordinator || !coordinator); - g_memory_coordinator = coordinator; -} - -MemoryState MemoryCoordinatorProxy::GetCurrentMemoryState() const { - if (!g_memory_coordinator) - return MemoryState::NORMAL; - return g_memory_coordinator->GetCurrentMemoryState(); -} - -} // namespace base diff --git a/memory/memory_coordinator_proxy.h b/memory/memory_coordinator_proxy.h deleted file mode 100644 index b6e7b3f6e..000000000 --- a/memory/memory_coordinator_proxy.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_MEMORY_COORDINATOR_PROXY_H_ -#define BASE_MEMORY_MEMORY_COORDINATOR_PROXY_H_ - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/memory/memory_coordinator_client.h" -#include "base/memory/singleton.h" - -namespace base { - -// The MemoryCoordinator interface. See comments in MemoryCoordinatorProxy for -// method descriptions. -class BASE_EXPORT MemoryCoordinator { - public: - virtual ~MemoryCoordinator() = default; - - virtual MemoryState GetCurrentMemoryState() const = 0; -}; - -// The proxy of MemoryCoordinator to be accessed from components that are not -// in content/browser e.g. net. -class BASE_EXPORT MemoryCoordinatorProxy { - public: - static MemoryCoordinatorProxy* GetInstance(); - - // Sets an implementation of MemoryCoordinator. MemoryCoordinatorProxy doesn't - // take the ownership of |coordinator|. It must outlive this proxy. - // This should be called before any components starts using this proxy. - static void SetMemoryCoordinator(MemoryCoordinator* coordinator); - - // Returns the current memory state. - MemoryState GetCurrentMemoryState() const; - - private: - friend struct base::DefaultSingletonTraits; - - MemoryCoordinatorProxy(); - virtual ~MemoryCoordinatorProxy(); - - DISALLOW_COPY_AND_ASSIGN(MemoryCoordinatorProxy); -}; - -} // namespace base - -#endif // BASE_MEMORY_MEMORY_COORDINATOR_PROXY_H_ diff --git a/memory/memory_pressure_listener.cc b/memory/memory_pressure_listener.cc deleted file mode 100644 index 669fb17b7..000000000 --- a/memory/memory_pressure_listener.cc +++ /dev/null @@ -1,129 +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/memory_pressure_listener.h" - -#include "base/observer_list_threadsafe.h" -#include "base/trace_event/trace_event.h" - -namespace base { - -namespace { - -// This class is thread safe and internally synchronized. -class MemoryPressureObserver { - public: - // There is at most one MemoryPressureObserver and it is never deleted. - ~MemoryPressureObserver() = delete; - - void AddObserver(MemoryPressureListener* listener, bool sync) { - async_observers_->AddObserver(listener); - if (sync) { - AutoLock lock(sync_observers_lock_); - sync_observers_.AddObserver(listener); - } - } - - void RemoveObserver(MemoryPressureListener* listener) { - async_observers_->RemoveObserver(listener); - AutoLock lock(sync_observers_lock_); - sync_observers_.RemoveObserver(listener); - } - - void Notify( - MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { - async_observers_->Notify(FROM_HERE, &MemoryPressureListener::Notify, - memory_pressure_level); - AutoLock lock(sync_observers_lock_); - for (auto& observer : sync_observers_) - observer.SyncNotify(memory_pressure_level); - } - - private: - const scoped_refptr> - async_observers_ = base::MakeRefCounted< - ObserverListThreadSafe>(); - ObserverList sync_observers_; - Lock sync_observers_lock_; -}; - -// Gets the shared MemoryPressureObserver singleton instance. -MemoryPressureObserver* GetMemoryPressureObserver() { - static auto* const observer = new MemoryPressureObserver(); - return observer; -} - -subtle::Atomic32 g_notifications_suppressed = 0; - -} // namespace - -MemoryPressureListener::MemoryPressureListener( - const MemoryPressureListener::MemoryPressureCallback& callback) - : callback_(callback) { - GetMemoryPressureObserver()->AddObserver(this, false); -} - -MemoryPressureListener::MemoryPressureListener( - const MemoryPressureListener::MemoryPressureCallback& callback, - const MemoryPressureListener::SyncMemoryPressureCallback& - sync_memory_pressure_callback) - : callback_(callback), - sync_memory_pressure_callback_(sync_memory_pressure_callback) { - GetMemoryPressureObserver()->AddObserver(this, true); -} - -MemoryPressureListener::~MemoryPressureListener() { - GetMemoryPressureObserver()->RemoveObserver(this); -} - -void MemoryPressureListener::Notify(MemoryPressureLevel memory_pressure_level) { - callback_.Run(memory_pressure_level); -} - -void MemoryPressureListener::SyncNotify( - MemoryPressureLevel memory_pressure_level) { - if (!sync_memory_pressure_callback_.is_null()) { - sync_memory_pressure_callback_.Run(memory_pressure_level); - } -} - -// static -void MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureLevel memory_pressure_level) { - DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE); - TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("memory-infra"), - "MemoryPressureListener::NotifyMemoryPressure", - TRACE_EVENT_SCOPE_THREAD, "level", - memory_pressure_level); - if (AreNotificationsSuppressed()) - return; - DoNotifyMemoryPressure(memory_pressure_level); -} - -// static -bool MemoryPressureListener::AreNotificationsSuppressed() { - return subtle::Acquire_Load(&g_notifications_suppressed) == 1; -} - -// static -void MemoryPressureListener::SetNotificationsSuppressed(bool suppress) { - subtle::Release_Store(&g_notifications_suppressed, suppress ? 1 : 0); -} - -// static -void MemoryPressureListener::SimulatePressureNotification( - MemoryPressureLevel memory_pressure_level) { - // Notify all listeners even if regular pressure notifications are suppressed. - DoNotifyMemoryPressure(memory_pressure_level); -} - -// static -void MemoryPressureListener::DoNotifyMemoryPressure( - MemoryPressureLevel memory_pressure_level) { - DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE); - - GetMemoryPressureObserver()->Notify(memory_pressure_level); -} - -} // namespace base diff --git a/memory/memory_pressure_listener.h b/memory/memory_pressure_listener.h deleted file mode 100644 index 7e9701008..000000000 --- a/memory/memory_pressure_listener.h +++ /dev/null @@ -1,102 +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. - -// MemoryPressure provides static APIs for handling memory pressure on -// platforms that have such signals, such as Android and ChromeOS. -// The app will try to discard buffers that aren't deemed essential (individual -// modules will implement their own policy). - -#ifndef BASE_MEMORY_MEMORY_PRESSURE_LISTENER_H_ -#define BASE_MEMORY_MEMORY_PRESSURE_LISTENER_H_ - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/macros.h" - -namespace base { - -// To start listening, create a new instance, passing a callback to a -// function that takes a MemoryPressureLevel 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. -// Note that even on the same thread, the callback is not guaranteed to be -// called synchronously within the system memory pressure broadcast. -// Please see notes in MemoryPressureLevel enum below: some levels are -// absolutely critical, and if not enough memory is returned to the system, -// it'll potentially kill the app, and then later the app will have to be -// cold-started. -// -// Example: -// -// void OnMemoryPressure(MemoryPressureLevel memory_pressure_level) { -// ... -// } -// -// // Start listening. -// MemoryPressureListener* my_listener = -// new MemoryPressureListener(base::Bind(&OnMemoryPressure)); -// -// ... -// -// // Stop listening. -// delete my_listener; -// -class BASE_EXPORT MemoryPressureListener { - public: - // A Java counterpart will be generated for this enum. - // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base - enum MemoryPressureLevel { - // No problems, there is enough memory to use. This event is not sent via - // callback, but the enum is used in other places to find out the current - // state of the system. - MEMORY_PRESSURE_LEVEL_NONE, - - // Modules are advised to free buffers that are cheap to re-allocate and not - // immediately needed. - MEMORY_PRESSURE_LEVEL_MODERATE, - - // At this level, modules are advised to free all possible memory. The - // alternative is to be killed by the system, which means all memory will - // have to be re-created, plus the cost of a cold start. - MEMORY_PRESSURE_LEVEL_CRITICAL, - }; - - typedef Callback MemoryPressureCallback; - typedef Callback SyncMemoryPressureCallback; - - explicit MemoryPressureListener( - const MemoryPressureCallback& memory_pressure_callback); - MemoryPressureListener( - const MemoryPressureCallback& memory_pressure_callback, - const SyncMemoryPressureCallback& sync_memory_pressure_callback); - - ~MemoryPressureListener(); - - // Intended for use by the platform specific implementation. - static void NotifyMemoryPressure(MemoryPressureLevel memory_pressure_level); - - // These methods should not be used anywhere else but in memory measurement - // code, where they are intended to maintain stable conditions across - // measurements. - static bool AreNotificationsSuppressed(); - static void SetNotificationsSuppressed(bool suppressed); - static void SimulatePressureNotification( - MemoryPressureLevel memory_pressure_level); - - void Notify(MemoryPressureLevel memory_pressure_level); - void SyncNotify(MemoryPressureLevel memory_pressure_level); - - private: - static void DoNotifyMemoryPressure(MemoryPressureLevel memory_pressure_level); - - MemoryPressureCallback callback_; - SyncMemoryPressureCallback sync_memory_pressure_callback_; - - DISALLOW_COPY_AND_ASSIGN(MemoryPressureListener); -}; - -} // namespace base - -#endif // BASE_MEMORY_MEMORY_PRESSURE_LISTENER_H_ diff --git a/memory/memory_pressure_listener_unittest.cc b/memory/memory_pressure_listener_unittest.cc deleted file mode 100644 index 87d5f4cbb..000000000 --- a/memory/memory_pressure_listener_unittest.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_pressure_listener.h" - -#include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "testing/gmock/include/gmock/gmock.h" - -namespace base { - -using MemoryPressureLevel = MemoryPressureListener::MemoryPressureLevel; - -class MemoryPressureListenerTest : public testing::Test { - public: - void SetUp() override { - message_loop_.reset(new MessageLoopForUI()); - listener_.reset(new MemoryPressureListener( - Bind(&MemoryPressureListenerTest::OnMemoryPressure, Unretained(this)))); - } - - void TearDown() override { - listener_.reset(); - message_loop_.reset(); - } - - protected: - void ExpectNotification( - void (*notification_function)(MemoryPressureLevel), - MemoryPressureLevel level) { - EXPECT_CALL(*this, OnMemoryPressure(level)).Times(1); - notification_function(level); - RunLoop().RunUntilIdle(); - } - - void ExpectNoNotification( - void (*notification_function)(MemoryPressureLevel), - MemoryPressureLevel level) { - EXPECT_CALL(*this, OnMemoryPressure(testing::_)).Times(0); - notification_function(level); - RunLoop().RunUntilIdle(); - } - - private: - MOCK_METHOD1(OnMemoryPressure, - void(MemoryPressureListener::MemoryPressureLevel)); - - std::unique_ptr message_loop_; - std::unique_ptr listener_; -}; - -TEST_F(MemoryPressureListenerTest, NotifyMemoryPressure) { - // Memory pressure notifications are not suppressed by default. - EXPECT_FALSE(MemoryPressureListener::AreNotificationsSuppressed()); - ExpectNotification(&MemoryPressureListener::NotifyMemoryPressure, - MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE); - ExpectNotification(&MemoryPressureListener::SimulatePressureNotification, - MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE); - - // Enable suppressing memory pressure notifications. - MemoryPressureListener::SetNotificationsSuppressed(true); - EXPECT_TRUE(MemoryPressureListener::AreNotificationsSuppressed()); - ExpectNoNotification(&MemoryPressureListener::NotifyMemoryPressure, - MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE); - ExpectNotification(&MemoryPressureListener::SimulatePressureNotification, - MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE); - - // Disable suppressing memory pressure notifications. - MemoryPressureListener::SetNotificationsSuppressed(false); - EXPECT_FALSE(MemoryPressureListener::AreNotificationsSuppressed()); - ExpectNotification(&MemoryPressureListener::NotifyMemoryPressure, - MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL); - ExpectNotification(&MemoryPressureListener::SimulatePressureNotification, - MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL); -} - -} // namespace base diff --git a/memory/memory_pressure_monitor.cc b/memory/memory_pressure_monitor.cc deleted file mode 100644 index ed350b81b..000000000 --- a/memory/memory_pressure_monitor.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_pressure_monitor.h" - -#include "base/logging.h" -#include "base/metrics/histogram_macros.h" - -namespace base { -namespace { - -MemoryPressureMonitor* g_monitor = nullptr; - -// Enumeration of UMA memory pressure levels. This needs to be kept in sync with -// histograms.xml and the memory pressure levels defined in -// MemoryPressureListener. -enum MemoryPressureLevelUMA { - UMA_MEMORY_PRESSURE_LEVEL_NONE = 0, - UMA_MEMORY_PRESSURE_LEVEL_MODERATE = 1, - UMA_MEMORY_PRESSURE_LEVEL_CRITICAL = 2, - // This must be the last value in the enum. - UMA_MEMORY_PRESSURE_LEVEL_COUNT, -}; - -// Converts a memory pressure level to an UMA enumeration value. -MemoryPressureLevelUMA MemoryPressureLevelToUmaEnumValue( - base::MemoryPressureListener::MemoryPressureLevel level) { - switch (level) { - case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: - return UMA_MEMORY_PRESSURE_LEVEL_NONE; - case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: - return UMA_MEMORY_PRESSURE_LEVEL_MODERATE; - case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: - return UMA_MEMORY_PRESSURE_LEVEL_CRITICAL; - } - NOTREACHED(); - return UMA_MEMORY_PRESSURE_LEVEL_NONE; -} - -} // namespace - -MemoryPressureMonitor::MemoryPressureMonitor() { - DCHECK(!g_monitor); - g_monitor = this; -} - -MemoryPressureMonitor::~MemoryPressureMonitor() { - DCHECK(g_monitor); - g_monitor = nullptr; -} - -// static -MemoryPressureMonitor* MemoryPressureMonitor::Get() { - return g_monitor; -} -void MemoryPressureMonitor::RecordMemoryPressure( - base::MemoryPressureListener::MemoryPressureLevel level, - int ticks) { - // Use the more primitive STATIC_HISTOGRAM_POINTER_BLOCK macro because the - // simple UMA_HISTOGRAM macros don't expose 'AddCount' functionality. - STATIC_HISTOGRAM_POINTER_BLOCK( - "Memory.PressureLevel", - AddCount(MemoryPressureLevelToUmaEnumValue(level), ticks), - base::LinearHistogram::FactoryGet( - "Memory.PressureLevel", 1, UMA_MEMORY_PRESSURE_LEVEL_COUNT, - UMA_MEMORY_PRESSURE_LEVEL_COUNT + 1, - base::HistogramBase::kUmaTargetedHistogramFlag)); -} - -} // namespace base diff --git a/memory/memory_pressure_monitor.h b/memory/memory_pressure_monitor.h deleted file mode 100644 index e48244b43..000000000 --- a/memory/memory_pressure_monitor.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_MEMORY_PRESSURE_MONITOR_H_ -#define BASE_MEMORY_MEMORY_PRESSURE_MONITOR_H_ - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/memory_pressure_listener.h" - -namespace base { - -// TODO(chrisha): Make this a concrete class with per-OS implementations rather -// than an abstract base class. - -// Declares the interface for a MemoryPressureMonitor. There are multiple -// OS specific implementations of this class. An instance of the memory -// pressure observer is created at the process level, tracks memory usage, and -// pushes memory state change notifications to the static function -// base::MemoryPressureListener::NotifyMemoryPressure. This is turn notifies -// all MemoryPressureListener instances via a callback. -class BASE_EXPORT MemoryPressureMonitor { - public: - using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel; - using DispatchCallback = base::Callback; - - virtual ~MemoryPressureMonitor(); - - // Return the singleton MemoryPressureMonitor. - static MemoryPressureMonitor* Get(); - - // Record memory pressure UMA statistic. A tick is 5 seconds. - static void RecordMemoryPressure(MemoryPressureLevel level, int ticks); - - // Returns the currently observed memory pressure. - virtual MemoryPressureLevel GetCurrentPressureLevel() = 0; - - // Sets a notification callback. The default callback invokes - // base::MemoryPressureListener::NotifyMemoryPressure. - virtual void SetDispatchCallback(const DispatchCallback& callback) = 0; - - protected: - MemoryPressureMonitor(); - - private: - DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor); -}; - -} // namespace base - -#endif // BASE_MEMORY_MEMORY_PRESSURE_MONITOR_H_ diff --git a/memory/memory_pressure_monitor_chromeos.cc b/memory/memory_pressure_monitor_chromeos.cc deleted file mode 100644 index b4e4b9478..000000000 --- a/memory/memory_pressure_monitor_chromeos.cc +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_pressure_monitor_chromeos.h" - -#include -#include - -#include "base/metrics/histogram_macros.h" -#include "base/posix/eintr_wrapper.h" -#include "base/process/process_metrics.h" -#include "base/single_thread_task_runner.h" -#include "base/sys_info.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/time/time.h" - -namespace base { -namespace chromeos { - -namespace { - -// The time between memory pressure checks. While under critical pressure, this -// is also the timer to repeat cleanup attempts. -const int kMemoryPressureIntervalMs = 1000; - -// The time which should pass between two moderate memory pressure calls. -const int kModerateMemoryPressureCooldownMs = 10000; - -// Number of event polls before the next moderate pressure event can be sent. -const int kModerateMemoryPressureCooldown = - kModerateMemoryPressureCooldownMs / kMemoryPressureIntervalMs; - -// Threshold constants to emit pressure events. -const int kNormalMemoryPressureModerateThresholdPercent = 60; -const int kNormalMemoryPressureCriticalThresholdPercent = 95; -const int kAggressiveMemoryPressureModerateThresholdPercent = 35; -const int kAggressiveMemoryPressureCriticalThresholdPercent = 70; - -// The possible state for memory pressure level. The values should be in line -// with values in MemoryPressureListener::MemoryPressureLevel and should be -// updated if more memory pressure levels are introduced. -enum MemoryPressureLevelUMA { - MEMORY_PRESSURE_LEVEL_NONE = 0, - MEMORY_PRESSURE_LEVEL_MODERATE, - MEMORY_PRESSURE_LEVEL_CRITICAL, - NUM_MEMORY_PRESSURE_LEVELS -}; - -// This is the file that will exist if low memory notification is available -// on the device. Whenever it becomes readable, it signals a low memory -// condition. -const char kLowMemFile[] = "/dev/chromeos-low-mem"; - -// Converts a |MemoryPressureThreshold| value into a used memory percentage for -// the moderate pressure event. -int GetModerateMemoryThresholdInPercent( - MemoryPressureMonitor::MemoryPressureThresholds thresholds) { - return thresholds == MemoryPressureMonitor:: - THRESHOLD_AGGRESSIVE_CACHE_DISCARD || - thresholds == MemoryPressureMonitor::THRESHOLD_AGGRESSIVE - ? kAggressiveMemoryPressureModerateThresholdPercent - : kNormalMemoryPressureModerateThresholdPercent; -} - -// Converts a |MemoryPressureThreshold| value into a used memory percentage for -// the critical pressure event. -int GetCriticalMemoryThresholdInPercent( - MemoryPressureMonitor::MemoryPressureThresholds thresholds) { - return thresholds == MemoryPressureMonitor:: - THRESHOLD_AGGRESSIVE_TAB_DISCARD || - thresholds == MemoryPressureMonitor::THRESHOLD_AGGRESSIVE - ? kAggressiveMemoryPressureCriticalThresholdPercent - : kNormalMemoryPressureCriticalThresholdPercent; -} - -// Converts free percent of memory into a memory pressure value. -MemoryPressureListener::MemoryPressureLevel GetMemoryPressureLevelFromFillLevel( - int actual_fill_level, - int moderate_threshold, - int critical_threshold) { - if (actual_fill_level < moderate_threshold) - return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; - return actual_fill_level < critical_threshold - ? MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE - : MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; -} - -// This function will be called less than once a second. It will check if -// the kernel has detected a low memory situation. -bool IsLowMemoryCondition(int file_descriptor) { - fd_set fds; - struct timeval tv; - - FD_ZERO(&fds); - FD_SET(file_descriptor, &fds); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - return HANDLE_EINTR(select(file_descriptor + 1, &fds, NULL, NULL, &tv)) > 0; -} - -} // namespace - -MemoryPressureMonitor::MemoryPressureMonitor( - MemoryPressureThresholds thresholds) - : current_memory_pressure_level_( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), - moderate_pressure_repeat_count_(0), - seconds_since_reporting_(0), - moderate_pressure_threshold_percent_( - GetModerateMemoryThresholdInPercent(thresholds)), - critical_pressure_threshold_percent_( - GetCriticalMemoryThresholdInPercent(thresholds)), - low_mem_file_(HANDLE_EINTR(::open(kLowMemFile, O_RDONLY))), - dispatch_callback_( - base::Bind(&MemoryPressureListener::NotifyMemoryPressure)), - weak_ptr_factory_(this) { - StartObserving(); - LOG_IF(ERROR, - base::SysInfo::IsRunningOnChromeOS() && !low_mem_file_.is_valid()) - << "Cannot open kernel listener"; -} - -MemoryPressureMonitor::~MemoryPressureMonitor() { - StopObserving(); -} - -void MemoryPressureMonitor::ScheduleEarlyCheck() { - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&MemoryPressureMonitor::CheckMemoryPressure, - weak_ptr_factory_.GetWeakPtr())); -} - -MemoryPressureListener::MemoryPressureLevel -MemoryPressureMonitor::GetCurrentPressureLevel() { - return current_memory_pressure_level_; -} - -// static -MemoryPressureMonitor* MemoryPressureMonitor::Get() { - return static_cast( - base::MemoryPressureMonitor::Get()); -} - -void MemoryPressureMonitor::StartObserving() { - timer_.Start(FROM_HERE, - TimeDelta::FromMilliseconds(kMemoryPressureIntervalMs), - Bind(&MemoryPressureMonitor:: - CheckMemoryPressureAndRecordStatistics, - weak_ptr_factory_.GetWeakPtr())); -} - -void MemoryPressureMonitor::StopObserving() { - // If StartObserving failed, StopObserving will still get called. - timer_.Stop(); -} - -void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() { - CheckMemoryPressure(); - if (seconds_since_reporting_++ == 5) { - seconds_since_reporting_ = 0; - RecordMemoryPressure(current_memory_pressure_level_, 1); - } - // Record UMA histogram statistics for the current memory pressure level. - // TODO(lgrey): Remove this once there's a usable history for the - // "Memory.PressureLevel" statistic - MemoryPressureLevelUMA memory_pressure_level_uma(MEMORY_PRESSURE_LEVEL_NONE); - switch (current_memory_pressure_level_) { - case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: - memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_NONE; - break; - case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: - memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_MODERATE; - break; - case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: - memory_pressure_level_uma = MEMORY_PRESSURE_LEVEL_CRITICAL; - break; - } - - UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel", - memory_pressure_level_uma, - NUM_MEMORY_PRESSURE_LEVELS); -} - -void MemoryPressureMonitor::CheckMemoryPressure() { - MemoryPressureListener::MemoryPressureLevel old_pressure = - current_memory_pressure_level_; - - // If we have the kernel low memory observer, we use it's flag instead of our - // own computation (for now). Note that in "simulation mode" it can be null. - // TODO(skuhne): We need to add code which makes sure that the kernel and this - // computation come to similar results and then remove this override again. - // TODO(skuhne): Add some testing framework here to see how close the kernel - // and the internal functions are. - if (low_mem_file_.is_valid() && IsLowMemoryCondition(low_mem_file_.get())) { - current_memory_pressure_level_ = - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; - } else { - current_memory_pressure_level_ = GetMemoryPressureLevelFromFillLevel( - GetUsedMemoryInPercent(), - moderate_pressure_threshold_percent_, - critical_pressure_threshold_percent_); - - // When listening to the kernel, we ignore the reported memory pressure - // level from our own computation and reduce critical to moderate. - if (low_mem_file_.is_valid() && - current_memory_pressure_level_ == - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { - current_memory_pressure_level_ = - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; - } - } - - // In case there is no memory pressure we do not notify. - if (current_memory_pressure_level_ == - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) { - return; - } - if (old_pressure == current_memory_pressure_level_) { - // If the memory pressure is still at the same level, we notify again for a - // critical level. In case of a moderate level repeat however, we only send - // a notification after a certain time has passed. - if (current_memory_pressure_level_ == - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && - ++moderate_pressure_repeat_count_ < - kModerateMemoryPressureCooldown) { - return; - } - } else if (current_memory_pressure_level_ == - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE && - old_pressure == - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { - // When we reducing the pressure level from critical to moderate, we - // restart the timeout and do not send another notification. - moderate_pressure_repeat_count_ = 0; - return; - } - moderate_pressure_repeat_count_ = 0; - dispatch_callback_.Run(current_memory_pressure_level_); -} - -// Gets the used ChromeOS memory in percent. -int MemoryPressureMonitor::GetUsedMemoryInPercent() { - base::SystemMemoryInfoKB info; - if (!base::GetSystemMemoryInfo(&info)) { - VLOG(1) << "Cannot determine the free memory of the system."; - return 0; - } - // TODO(skuhne): Instead of adding the kernel memory pressure calculation - // logic here, we should have a kernel mechanism similar to the low memory - // notifier in ChromeOS which offers multiple pressure states. - // To track this, we have crbug.com/381196. - - // The available memory consists of "real" and virtual (z)ram memory. - // Since swappable memory uses a non pre-deterministic compression and - // the compression creates its own "dynamic" in the system, it gets - // de-emphasized by the |kSwapWeight| factor. - const int kSwapWeight = 4; - - // The total memory we have is the "real memory" plus the virtual (z)ram. - int total_memory = info.total + info.swap_total / kSwapWeight; - - // The kernel internally uses 50MB. - const int kMinFileMemory = 50 * 1024; - - // Most file memory can be easily reclaimed. - int file_memory = info.active_file + info.inactive_file; - // unless it is dirty or it's a minimal portion which is required. - file_memory -= info.dirty + kMinFileMemory; - - // Available memory is the sum of free, swap and easy reclaimable memory. - int available_memory = - info.free + info.swap_free / kSwapWeight + file_memory; - - DCHECK(available_memory < total_memory); - int percentage = ((total_memory - available_memory) * 100) / total_memory; - return percentage; -} - -void MemoryPressureMonitor::SetDispatchCallback( - const DispatchCallback& callback) { - dispatch_callback_ = callback; -} - -} // namespace chromeos -} // namespace base diff --git a/memory/memory_pressure_monitor_chromeos.h b/memory/memory_pressure_monitor_chromeos.h deleted file mode 100644 index 563ba8508..000000000 --- a/memory/memory_pressure_monitor_chromeos.h +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_MEMORY_PRESSURE_MONITOR_CHROMEOS_H_ -#define BASE_MEMORY_MEMORY_PRESSURE_MONITOR_CHROMEOS_H_ - -#include "base/base_export.h" -#include "base/files/scoped_file.h" -#include "base/macros.h" -#include "base/memory/memory_pressure_listener.h" -#include "base/memory/memory_pressure_monitor.h" -#include "base/memory/weak_ptr.h" -#include "base/timer/timer.h" - -namespace base { -namespace chromeos { - -class TestMemoryPressureMonitor; - -//////////////////////////////////////////////////////////////////////////////// -// MemoryPressureMonitor -// -// A class to handle the observation of our free memory. It notifies the -// MemoryPressureListener of memory fill level changes, so that it can take -// action to reduce memory resources accordingly. -// -class BASE_EXPORT MemoryPressureMonitor : public base::MemoryPressureMonitor { - public: - using GetUsedMemoryInPercentCallback = int (*)(); - - // There are two memory pressure events: - // MODERATE - which will mainly release caches. - // CRITICAL - which will discard tabs. - // The |MemoryPressureThresholds| enum selects the strategy of firing these - // events: A conservative strategy will keep as much content in memory as - // possible (causing the system to swap to zram) and an aggressive strategy - // will release memory earlier to avoid swapping. - enum MemoryPressureThresholds { - // Use the system default. - THRESHOLD_DEFAULT = 0, - // Try to keep as much content in memory as possible. - THRESHOLD_CONSERVATIVE = 1, - // Discard caches earlier, allowing to keep more tabs in memory. - THRESHOLD_AGGRESSIVE_CACHE_DISCARD = 2, - // Discard tabs earlier, allowing the system to get faster. - THRESHOLD_AGGRESSIVE_TAB_DISCARD = 3, - // Discard caches and tabs earlier to allow the system to be faster. - THRESHOLD_AGGRESSIVE = 4 - }; - - explicit MemoryPressureMonitor(MemoryPressureThresholds thresholds); - ~MemoryPressureMonitor() override; - - // Redo the memory pressure calculation soon and call again if a critical - // memory pressure prevails. Note that this call will trigger an asynchronous - // action which gives the system time to release memory back into the pool. - void ScheduleEarlyCheck(); - - // Get the current memory pressure level. - MemoryPressureListener::MemoryPressureLevel GetCurrentPressureLevel() - override; - void SetDispatchCallback(const DispatchCallback& callback) override; - - // Returns a type-casted version of the current memory pressure monitor. A - // simple wrapper to base::MemoryPressureMonitor::Get. - static MemoryPressureMonitor* Get(); - - private: - friend TestMemoryPressureMonitor; - // Starts observing the memory fill level. - // Calls to StartObserving should always be matched with calls to - // StopObserving. - void StartObserving(); - - // Stop observing the memory fill level. - // May be safely called if StartObserving has not been called. - void StopObserving(); - - // The function which gets periodically called to check any changes in the - // memory pressure. It will report pressure changes as well as continuous - // critical pressure levels. - void CheckMemoryPressure(); - - // The function periodically checks the memory pressure changes and records - // the UMA histogram statistics for the current memory pressure level. - void CheckMemoryPressureAndRecordStatistics(); - - // Get the memory pressure in percent (virtual for testing). - virtual int GetUsedMemoryInPercent(); - - // The current memory pressure. - base::MemoryPressureListener::MemoryPressureLevel - current_memory_pressure_level_; - - // A periodic timer to check for resource pressure changes. This will get - // replaced by a kernel triggered event system (see crbug.com/381196). - base::RepeatingTimer timer_; - - // To slow down the amount of moderate pressure event calls, this counter - // gets used to count the number of events since the last event occured. - int moderate_pressure_repeat_count_; - - // The "Memory.PressureLevel" statistic is recorded every - // 5 seconds, but the timer to report "ChromeOS.MemoryPressureLevel" - // fires every second. This counter is used to allow reporting - // "Memory.PressureLevel" correctly without adding another - // timer. - int seconds_since_reporting_; - - // The thresholds for moderate and critical pressure. - const int moderate_pressure_threshold_percent_; - const int critical_pressure_threshold_percent_; - - // File descriptor used to detect low memory condition. - ScopedFD low_mem_file_; - - DispatchCallback dispatch_callback_; - - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor); -}; - -} // namespace chromeos -} // namespace base - -#endif // BASE_MEMORY_MEMORY_PRESSURE_MONITOR_CHROMEOS_H_ diff --git a/memory/memory_pressure_monitor_chromeos_unittest.cc b/memory/memory_pressure_monitor_chromeos_unittest.cc deleted file mode 100644 index ee000911e..000000000 --- a/memory/memory_pressure_monitor_chromeos_unittest.cc +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_pressure_monitor_chromeos.h" - -#include "base/macros.h" -#include "base/memory/memory_pressure_listener.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/sys_info.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace chromeos { - -namespace { - -// True if the memory notifier got called. -// Do not read/modify value directly. -bool on_memory_pressure_called = false; - -// If the memory notifier got called, this is the memory pressure reported. -MemoryPressureListener::MemoryPressureLevel on_memory_pressure_level = - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; - -// Processes OnMemoryPressure calls. -void OnMemoryPressure(MemoryPressureListener::MemoryPressureLevel level) { - on_memory_pressure_called = true; - on_memory_pressure_level = level; -} - -// Resets the indicator for memory pressure. -void ResetOnMemoryPressureCalled() { - on_memory_pressure_called = false; -} - -// Returns true when OnMemoryPressure was called (and resets it). -bool WasOnMemoryPressureCalled() { - bool b = on_memory_pressure_called; - ResetOnMemoryPressureCalled(); - return b; -} - -} // namespace - -class TestMemoryPressureMonitor : public MemoryPressureMonitor { - public: - TestMemoryPressureMonitor() - : MemoryPressureMonitor(THRESHOLD_DEFAULT), - memory_in_percent_override_(0) { - // Disable any timers which are going on and set a special memory reporting - // function. - StopObserving(); - } - ~TestMemoryPressureMonitor() override = default; - - void SetMemoryInPercentOverride(int percent) { - memory_in_percent_override_ = percent; - } - - void CheckMemoryPressureForTest() { - CheckMemoryPressure(); - } - - private: - int GetUsedMemoryInPercent() override { - return memory_in_percent_override_; - } - - int memory_in_percent_override_; - DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor); -}; - -// This test tests the various transition states from memory pressure, looking -// for the correct behavior on event reposting as well as state updates. -TEST(ChromeOSMemoryPressureMonitorTest, CheckMemoryPressure) { - // crbug.com/844102: - if (base::SysInfo::IsRunningOnChromeOS()) - return; - - base::MessageLoopForUI message_loop; - std::unique_ptr monitor( - new TestMemoryPressureMonitor); - std::unique_ptr listener( - new MemoryPressureListener(base::Bind(&OnMemoryPressure))); - // Checking the memory pressure while 0% are used should not produce any - // events. - monitor->SetMemoryInPercentOverride(0); - ResetOnMemoryPressureCalled(); - - monitor->CheckMemoryPressureForTest(); - RunLoop().RunUntilIdle(); - EXPECT_FALSE(WasOnMemoryPressureCalled()); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - monitor->GetCurrentPressureLevel()); - - // Setting the memory level to 80% should produce a moderate pressure level. - monitor->SetMemoryInPercentOverride(80); - monitor->CheckMemoryPressureForTest(); - RunLoop().RunUntilIdle(); - EXPECT_TRUE(WasOnMemoryPressureCalled()); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor->GetCurrentPressureLevel()); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - on_memory_pressure_level); - - // We need to check that the event gets reposted after a while. - int i = 0; - for (; i < 100; i++) { - monitor->CheckMemoryPressureForTest(); - RunLoop().RunUntilIdle(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor->GetCurrentPressureLevel()); - if (WasOnMemoryPressureCalled()) { - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - on_memory_pressure_level); - break; - } - } - // Should be more than 5 and less than 100. - EXPECT_LE(5, i); - EXPECT_GE(99, i); - - // Setting the memory usage to 99% should produce critical levels. - monitor->SetMemoryInPercentOverride(99); - monitor->CheckMemoryPressureForTest(); - RunLoop().RunUntilIdle(); - EXPECT_TRUE(WasOnMemoryPressureCalled()); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, - on_memory_pressure_level); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, - monitor->GetCurrentPressureLevel()); - - // Calling it again should immediately produce a second call. - monitor->CheckMemoryPressureForTest(); - RunLoop().RunUntilIdle(); - EXPECT_TRUE(WasOnMemoryPressureCalled()); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, - on_memory_pressure_level); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, - monitor->GetCurrentPressureLevel()); - - // When lowering the pressure again we should not get an event, but the - // pressure should go back to moderate. - monitor->SetMemoryInPercentOverride(80); - monitor->CheckMemoryPressureForTest(); - RunLoop().RunUntilIdle(); - EXPECT_FALSE(WasOnMemoryPressureCalled()); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor->GetCurrentPressureLevel()); - - // We should need exactly the same amount of calls as before, before the next - // call comes in. - int j = 0; - for (; j < 100; j++) { - monitor->CheckMemoryPressureForTest(); - RunLoop().RunUntilIdle(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor->GetCurrentPressureLevel()); - if (WasOnMemoryPressureCalled()) { - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - on_memory_pressure_level); - break; - } - } - // We should have needed exactly the same amount of checks as before. - EXPECT_EQ(j, i); -} - -} // namespace chromeos -} // namespace base diff --git a/memory/memory_pressure_monitor_mac.cc b/memory/memory_pressure_monitor_mac.cc deleted file mode 100644 index 678c276d3..000000000 --- a/memory/memory_pressure_monitor_mac.cc +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_pressure_monitor_mac.h" - -#include - -#include -#include -#include - -#include - -#include "base/bind.h" -#include "base/logging.h" -#include "base/mac/mac_util.h" - -// Redeclare for partial 10.9 availability. -DISPATCH_EXPORT const struct dispatch_source_type_s - _dispatch_source_type_memorypressure; - -namespace { -static const int kUMATickSize = 5; -} // namespace - -namespace base { -namespace mac { - -MemoryPressureListener::MemoryPressureLevel -MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( - int mac_memory_pressure_level) { - switch (mac_memory_pressure_level) { - case DISPATCH_MEMORYPRESSURE_NORMAL: - return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; - case DISPATCH_MEMORYPRESSURE_WARN: - return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; - case DISPATCH_MEMORYPRESSURE_CRITICAL: - return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; - } - return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; -} - -void MemoryPressureMonitor::OnRunLoopExit(CFRunLoopObserverRef observer, - CFRunLoopActivity activity, - void* info) { - MemoryPressureMonitor* self = static_cast(info); - self->UpdatePressureLevelOnRunLoopExit(); -} - -MemoryPressureMonitor::MemoryPressureMonitor() - : memory_level_event_source_(dispatch_source_create( - DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, - 0, - DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL | - DISPATCH_MEMORYPRESSURE_NORMAL, - dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))), - dispatch_callback_( - base::Bind(&MemoryPressureListener::NotifyMemoryPressure)), - last_statistic_report_time_(CFAbsoluteTimeGetCurrent()), - last_pressure_level_(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), - subtick_seconds_(0) { - // Attach an event handler to the memory pressure event source. - if (memory_level_event_source_.get()) { - dispatch_source_set_event_handler(memory_level_event_source_, ^{ - OnMemoryPressureChanged(memory_level_event_source_.get(), - dispatch_callback_); - }); - - // Start monitoring the event source. - dispatch_resume(memory_level_event_source_); - } - - // Create a CFRunLoopObserver to check the memory pressure at the end of - // every pass through the event loop (modulo kUMATickSize). - CFRunLoopObserverContext observer_context = {0, this, NULL, NULL, NULL}; - - exit_observer_.reset( - CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopExit, true, 0, - OnRunLoopExit, &observer_context)); - - CFRunLoopRef run_loop = CFRunLoopGetCurrent(); - CFRunLoopAddObserver(run_loop, exit_observer_, kCFRunLoopCommonModes); - CFRunLoopAddObserver(run_loop, exit_observer_, - kMessageLoopExclusiveRunLoopMode); -} - -MemoryPressureMonitor::~MemoryPressureMonitor() { - // Detach from the run loop. - CFRunLoopRef run_loop = CFRunLoopGetCurrent(); - CFRunLoopRemoveObserver(run_loop, exit_observer_, kCFRunLoopCommonModes); - CFRunLoopRemoveObserver(run_loop, exit_observer_, - kMessageLoopExclusiveRunLoopMode); - - // Remove the memory pressure event source. - if (memory_level_event_source_.get()) { - dispatch_source_cancel(memory_level_event_source_); - } -} - -int MemoryPressureMonitor::GetMacMemoryPressureLevel() { - // Get the raw memory pressure level from macOS. - int mac_memory_pressure_level; - size_t length = sizeof(int); - sysctlbyname("kern.memorystatus_vm_pressure_level", - &mac_memory_pressure_level, &length, nullptr, 0); - - return mac_memory_pressure_level; -} - -void MemoryPressureMonitor::UpdatePressureLevel() { - // Get the current macOS pressure level and convert to the corresponding - // Chrome pressure level. - int mac_memory_pressure_level = GetMacMemoryPressureLevel(); - MemoryPressureListener::MemoryPressureLevel new_pressure_level = - MemoryPressureLevelForMacMemoryPressureLevel(mac_memory_pressure_level); - - // Compute the number of "ticks" spent at |last_pressure_level_| (since the - // last report sent to UMA). - CFTimeInterval now = CFAbsoluteTimeGetCurrent(); - CFTimeInterval time_since_last_report = now - last_statistic_report_time_; - last_statistic_report_time_ = now; - - double accumulated_time = time_since_last_report + subtick_seconds_; - int ticks_to_report = static_cast(accumulated_time / kUMATickSize); - // Save for later the seconds that didn't make it into a full tick. - subtick_seconds_ = std::fmod(accumulated_time, kUMATickSize); - - // Round the tick count up on a pressure level change to ensure we capture it. - bool pressure_level_changed = (new_pressure_level != last_pressure_level_); - if (pressure_level_changed && ticks_to_report < 1) { - ticks_to_report = 1; - subtick_seconds_ = 0; - } - - // Send elapsed ticks to UMA. - if (ticks_to_report >= 1) { - RecordMemoryPressure(last_pressure_level_, ticks_to_report); - } - - // Save the now-current memory pressure level. - last_pressure_level_ = new_pressure_level; -} - -void MemoryPressureMonitor::UpdatePressureLevelOnRunLoopExit() { - // Wait until it's time to check the pressure level. - CFTimeInterval now = CFAbsoluteTimeGetCurrent(); - if (now >= next_run_loop_update_time_) { - UpdatePressureLevel(); - - // Update again in kUMATickSize seconds. We can update at any frequency, - // but because we're only checking memory pressure levels for UMA there's - // no need to update more frequently than we're keeping statistics on. - next_run_loop_update_time_ = now + kUMATickSize - subtick_seconds_; - } -} - -// Static. -int MemoryPressureMonitor::GetSecondsPerUMATick() { - return kUMATickSize; -} - -MemoryPressureListener::MemoryPressureLevel -MemoryPressureMonitor::GetCurrentPressureLevel() { - return last_pressure_level_; -} - -void MemoryPressureMonitor::OnMemoryPressureChanged( - dispatch_source_s* event_source, - const MemoryPressureMonitor::DispatchCallback& dispatch_callback) { - // The OS has sent a notification that the memory pressure level has changed. - // Go through the normal memory pressure level checking mechanism so that - // last_pressure_level_ and UMA get updated to the current value. - UpdatePressureLevel(); - - // Run the callback that's waiting on memory pressure change notifications. - // The convention is to not send notifiations on memory pressure returning to - // normal. - if (last_pressure_level_ != - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) - dispatch_callback.Run(last_pressure_level_); -} - -void MemoryPressureMonitor::SetDispatchCallback( - const DispatchCallback& callback) { - dispatch_callback_ = callback; -} - -} // namespace mac -} // namespace base diff --git a/memory/memory_pressure_monitor_mac.h b/memory/memory_pressure_monitor_mac.h deleted file mode 100644 index b85b6c901..000000000 --- a/memory/memory_pressure_monitor_mac.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_MEMORY_PRESSURE_MONITOR_MAC_H_ -#define BASE_MEMORY_MEMORY_PRESSURE_MONITOR_MAC_H_ - -#include -#include - -#include "base/base_export.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/mac/scoped_dispatch_object.h" -#include "base/macros.h" -#include "base/memory/memory_pressure_listener.h" -#include "base/memory/memory_pressure_monitor.h" -#include "base/message_loop/message_pump_mac.h" - -namespace base { -namespace mac { - -class TestMemoryPressureMonitor; - -// Declares the interface for the Mac MemoryPressureMonitor, which reports -// memory pressure events and status. -class BASE_EXPORT MemoryPressureMonitor : public base::MemoryPressureMonitor { - public: - MemoryPressureMonitor(); - ~MemoryPressureMonitor() override; - - // Returns the currently-observed memory pressure. - MemoryPressureLevel GetCurrentPressureLevel() override; - - void SetDispatchCallback(const DispatchCallback& callback) override; - - private: - friend TestMemoryPressureMonitor; - - static MemoryPressureLevel MemoryPressureLevelForMacMemoryPressureLevel( - int mac_memory_pressure_level); - static void OnRunLoopExit(CFRunLoopObserverRef observer, - CFRunLoopActivity activity, - void* info); - // Returns the raw memory pressure level from the macOS. Exposed for - // unit testing. - virtual int GetMacMemoryPressureLevel(); - - // Updates |last_pressure_level_| with the current memory pressure level. - void UpdatePressureLevel(); - - // Updates |last_pressure_level_| at the end of every run loop pass (modulo - // some number of seconds). - void UpdatePressureLevelOnRunLoopExit(); - - // Run |dispatch_callback| on memory pressure notifications from the OS. - void OnMemoryPressureChanged(dispatch_source_s* event_source, - const DispatchCallback& dispatch_callback); - - // Returns the number of seconds per UMA tick (for statistics recording). - // Exposed for testing. - static int GetSecondsPerUMATick(); - - // The dispatch source that generates memory pressure change notifications. - ScopedDispatchObject memory_level_event_source_; - - // The callback to call upon receiving a memory pressure change notification. - DispatchCallback dispatch_callback_; - - // Last UMA report time. - CFTimeInterval last_statistic_report_time_; - - // Most-recent memory pressure level. - MemoryPressureLevel last_pressure_level_; - - // Observer that tracks exits from the main run loop. - ScopedCFTypeRef exit_observer_; - - // Next time to update the memory pressure level when exiting the run loop. - CFTimeInterval next_run_loop_update_time_; - - // Seconds left over from the last UMA tick calculation (to be added to the - // next calculation). - CFTimeInterval subtick_seconds_; - - DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor); -}; - -} // namespace mac -} // namespace base - -#endif // BASE_MEMORY_MEMORY_PRESSURE_MONITOR_MAC_H_ diff --git a/memory/memory_pressure_monitor_mac_unittest.cc b/memory/memory_pressure_monitor_mac_unittest.cc deleted file mode 100644 index 3f5f4b764..000000000 --- a/memory/memory_pressure_monitor_mac_unittest.cc +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_pressure_monitor_mac.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/macros.h" -#include "base/test/metrics/histogram_tester.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace mac { - -class TestMemoryPressureMonitor : public MemoryPressureMonitor { - public: - using MemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel; - - // A HistogramTester for verifying correct UMA stat generation. - base::HistogramTester tester; - - TestMemoryPressureMonitor() { } - - // Clears the next run loop update time so that the next pass of the run - // loop checks the memory pressure level immediately. Normally there's a - // 5 second delay between pressure readings. - void ResetRunLoopUpdateTime() { next_run_loop_update_time_ = 0; } - - // Sets the last UMA stat report time. Time spent in memory pressure is - // recorded in 5-second "ticks" from the last time statistics were recorded. - void SetLastStatisticReportTime(CFTimeInterval time) { - last_statistic_report_time_ = time; - } - - // Sets the raw macOS memory pressure level read by the memory pressure - // monitor. - int macos_pressure_level_for_testing_; - - // Exposes the UpdatePressureLevel() method for testing. - void UpdatePressureLevel() { MemoryPressureMonitor::UpdatePressureLevel(); } - - // Returns the number of seconds left over from the last UMA tick - // calculation. - int SubTickSeconds() { return subtick_seconds_; } - - // Returns the number of seconds per UMA tick. - static int GetSecondsPerUMATick() { - return MemoryPressureMonitor::GetSecondsPerUMATick(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor); - - int GetMacMemoryPressureLevel() override { - return macos_pressure_level_for_testing_; - } -}; - -TEST(MacMemoryPressureMonitorTest, MemoryPressureFromMacMemoryPressure) { - EXPECT_EQ( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( - DISPATCH_MEMORYPRESSURE_NORMAL)); - EXPECT_EQ( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( - DISPATCH_MEMORYPRESSURE_WARN)); - EXPECT_EQ( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, - TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( - DISPATCH_MEMORYPRESSURE_CRITICAL)); - EXPECT_EQ( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( - 0)); - EXPECT_EQ( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( - 3)); - EXPECT_EQ( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( - 5)); - EXPECT_EQ( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - TestMemoryPressureMonitor::MemoryPressureLevelForMacMemoryPressureLevel( - -1)); -} - -TEST(MacMemoryPressureMonitorTest, CurrentMemoryPressure) { - TestMemoryPressureMonitor monitor; - - MemoryPressureListener::MemoryPressureLevel memory_pressure = - monitor.GetCurrentPressureLevel(); - EXPECT_TRUE(memory_pressure == - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE || - memory_pressure == - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE || - memory_pressure == - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); -} - -TEST(MacMemoryPressureMonitorTest, MemoryPressureConversion) { - TestMemoryPressureMonitor monitor; - - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; - monitor.UpdatePressureLevel(); - MemoryPressureListener::MemoryPressureLevel memory_pressure = - monitor.GetCurrentPressureLevel(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - memory_pressure); - - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_WARN; - monitor.UpdatePressureLevel(); - memory_pressure = monitor.GetCurrentPressureLevel(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - memory_pressure); - - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; - monitor.UpdatePressureLevel(); - memory_pressure = monitor.GetCurrentPressureLevel(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, - memory_pressure); -} - -TEST(MacMemoryPressureMonitorTest, MemoryPressureRunLoopChecking) { - TestMemoryPressureMonitor monitor; - - // To test grabbing the memory presure at the end of the run loop, we have to - // run the run loop, but to do that the run loop needs a run loop source. Add - // a timer as the source. We know that the exit observer is attached to - // the kMessageLoopExclusiveRunLoopMode mode, so use that mode. - ScopedCFTypeRef timer_ref(CFRunLoopTimerCreate( - NULL, CFAbsoluteTimeGetCurrent() + 10, 0, 0, 0, nullptr, nullptr)); - CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer_ref, - kMessageLoopExclusiveRunLoopMode); - - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_WARN; - monitor.ResetRunLoopUpdateTime(); - CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0, true); - EXPECT_EQ(monitor.GetCurrentPressureLevel(), - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); - - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; - monitor.ResetRunLoopUpdateTime(); - CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0, true); - EXPECT_EQ(monitor.GetCurrentPressureLevel(), - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); - - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; - monitor.ResetRunLoopUpdateTime(); - CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0, true); - EXPECT_EQ(monitor.GetCurrentPressureLevel(), - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); - - CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer_ref, - kMessageLoopExclusiveRunLoopMode); -} - -TEST(MacMemoryPressureMonitorTest, RecordMemoryPressureStats) { - TestMemoryPressureMonitor monitor; - const char* kHistogram = "Memory.PressureLevel"; - CFTimeInterval now = CFAbsoluteTimeGetCurrent(); - const int seconds_per_tick = - TestMemoryPressureMonitor::GetSecondsPerUMATick(); - - // Set the initial pressure level. - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; - // Incur one UMA tick of time (and include one extra second of elapsed time). - monitor.SetLastStatisticReportTime(now - (seconds_per_tick + 1)); - monitor.UpdatePressureLevel(); - monitor.tester.ExpectTotalCount(kHistogram, 1); - monitor.tester.ExpectBucketCount(kHistogram, 0, 1); - // The report time above included an extra second so there should be 1 - // sub-tick second left over. - EXPECT_EQ(1, monitor.SubTickSeconds()); - - // Simulate sitting in normal pressure for 1 second less than 6 UMA tick - // seconds and then elevating to warning. With the left over sub-tick second - // from above, the total elapsed ticks should be an even 6 UMA ticks. - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_WARN; - monitor.SetLastStatisticReportTime(now - (seconds_per_tick * 6 - 1)); - monitor.UpdatePressureLevel(); - monitor.tester.ExpectTotalCount(kHistogram, 7); - monitor.tester.ExpectBucketCount(kHistogram, 0, 7); - monitor.tester.ExpectBucketCount(kHistogram, 1, 0); - EXPECT_EQ(0, monitor.SubTickSeconds()); - - // Simulate sitting in warning pressure for 20 UMA ticks and 2 seconds, and - // then elevating to critical. - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; - monitor.SetLastStatisticReportTime(now - (20 * seconds_per_tick + 2)); - monitor.UpdatePressureLevel(); - monitor.tester.ExpectTotalCount(kHistogram, 27); - monitor.tester.ExpectBucketCount(kHistogram, 0, 7); - monitor.tester.ExpectBucketCount(kHistogram, 1, 20); - monitor.tester.ExpectBucketCount(kHistogram, 2, 0); - EXPECT_EQ(2, monitor.SubTickSeconds()); - - // A quick update while critical - the stats should not budge because less - // than 1 tick of time has elapsed. - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_CRITICAL; - monitor.SetLastStatisticReportTime(now - 1); - monitor.UpdatePressureLevel(); - monitor.tester.ExpectTotalCount(kHistogram, 27); - monitor.tester.ExpectBucketCount(kHistogram, 0, 7); - monitor.tester.ExpectBucketCount(kHistogram, 1, 20); - monitor.tester.ExpectBucketCount(kHistogram, 2, 0); - EXPECT_EQ(3, monitor.SubTickSeconds()); - - // A quick change back to normal. Less than 1 tick of time has elapsed, but - // in this case the pressure level changed, so the critical bucket should - // get another sample (otherwise we could miss quick level changes). - monitor.macos_pressure_level_for_testing_ = DISPATCH_MEMORYPRESSURE_NORMAL; - monitor.SetLastStatisticReportTime(now - 1); - monitor.UpdatePressureLevel(); - monitor.tester.ExpectTotalCount(kHistogram, 28); - monitor.tester.ExpectBucketCount(kHistogram, 0, 7); - monitor.tester.ExpectBucketCount(kHistogram, 1, 20); - monitor.tester.ExpectBucketCount(kHistogram, 2, 1); - // When less than 1 tick of time has elapsed but the pressure level changed, - // the subtick remainder gets zeroed out. - EXPECT_EQ(0, monitor.SubTickSeconds()); -} -} // namespace mac -} // namespace base diff --git a/memory/memory_pressure_monitor_unittest.cc b/memory/memory_pressure_monitor_unittest.cc deleted file mode 100644 index 10d9d2428..000000000 --- a/memory/memory_pressure_monitor_unittest.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_pressure_monitor.h" - -#include "base/macros.h" -#include "base/memory/memory_pressure_listener.h" -#include "base/test/metrics/histogram_tester.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(MemoryPressureMonitorTest, RecordMemoryPressure) { - base::HistogramTester tester; - const char* kHistogram = "Memory.PressureLevel"; - - MemoryPressureMonitor::RecordMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, 3); - tester.ExpectTotalCount(kHistogram, 3); - tester.ExpectBucketCount(kHistogram, 0, 3); - - MemoryPressureMonitor::RecordMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, 2); - tester.ExpectTotalCount(kHistogram, 5); - tester.ExpectBucketCount(kHistogram, 1, 2); - - MemoryPressureMonitor::RecordMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, 1); - tester.ExpectTotalCount(kHistogram, 6); - tester.ExpectBucketCount(kHistogram, 2, 1); -} -} // namespace base diff --git a/memory/memory_pressure_monitor_win.cc b/memory/memory_pressure_monitor_win.cc deleted file mode 100644 index 3effe2cc2..000000000 --- a/memory/memory_pressure_monitor_win.cc +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_pressure_monitor_win.h" - -#include - -#include "base/single_thread_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/time/time.h" - -namespace base { -namespace win { - -namespace { - -static const DWORDLONG kMBBytes = 1024 * 1024; - -} // namespace - -// The following constants have been lifted from similar values in the ChromeOS -// memory pressure monitor. The values were determined experimentally to ensure -// sufficient responsiveness of the memory pressure subsystem, and minimal -// overhead. -const int MemoryPressureMonitor::kPollingIntervalMs = 5000; -const int MemoryPressureMonitor::kModeratePressureCooldownMs = 10000; -const int MemoryPressureMonitor::kModeratePressureCooldownCycles = - kModeratePressureCooldownMs / kPollingIntervalMs; - -// TODO(chrisha): Explore the following constants further with an experiment. - -// A system is considered 'high memory' if it has more than 1.5GB of system -// memory available for use by the memory manager (not reserved for hardware -// and drivers). This is a fuzzy version of the ~2GB discussed below. -const int MemoryPressureMonitor::kLargeMemoryThresholdMb = 1536; - -// These are the default thresholds used for systems with < ~2GB of physical -// memory. Such systems have been observed to always maintain ~100MB of -// available memory, paging until that is the case. To try to avoid paging a -// threshold slightly above this is chosen. The moderate threshold is slightly -// less grounded in reality and chosen as 2.5x critical. -const int MemoryPressureMonitor::kSmallMemoryDefaultModerateThresholdMb = 500; -const int MemoryPressureMonitor::kSmallMemoryDefaultCriticalThresholdMb = 200; - -// These are the default thresholds used for systems with >= ~2GB of physical -// memory. Such systems have been observed to always maintain ~300MB of -// available memory, paging until that is the case. -const int MemoryPressureMonitor::kLargeMemoryDefaultModerateThresholdMb = 1000; -const int MemoryPressureMonitor::kLargeMemoryDefaultCriticalThresholdMb = 400; - -MemoryPressureMonitor::MemoryPressureMonitor() - : moderate_threshold_mb_(0), - critical_threshold_mb_(0), - current_memory_pressure_level_( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), - moderate_pressure_repeat_count_(0), - dispatch_callback_( - base::Bind(&MemoryPressureListener::NotifyMemoryPressure)), - weak_ptr_factory_(this) { - InferThresholds(); - StartObserving(); -} - -MemoryPressureMonitor::MemoryPressureMonitor(int moderate_threshold_mb, - int critical_threshold_mb) - : moderate_threshold_mb_(moderate_threshold_mb), - critical_threshold_mb_(critical_threshold_mb), - current_memory_pressure_level_( - MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), - moderate_pressure_repeat_count_(0), - dispatch_callback_( - base::Bind(&MemoryPressureListener::NotifyMemoryPressure)), - weak_ptr_factory_(this) { - DCHECK_GE(moderate_threshold_mb_, critical_threshold_mb_); - DCHECK_LE(0, critical_threshold_mb_); - StartObserving(); -} - -MemoryPressureMonitor::~MemoryPressureMonitor() { - StopObserving(); -} - -void MemoryPressureMonitor::CheckMemoryPressureSoon() { - DCHECK(thread_checker_.CalledOnValidThread()); - - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, Bind(&MemoryPressureMonitor::CheckMemoryPressure, - weak_ptr_factory_.GetWeakPtr())); -} - -MemoryPressureListener::MemoryPressureLevel -MemoryPressureMonitor::GetCurrentPressureLevel() { - return current_memory_pressure_level_; -} - -void MemoryPressureMonitor::InferThresholds() { - // Default to a 'high' memory situation, which uses more conservative - // thresholds. - bool high_memory = true; - MEMORYSTATUSEX mem_status = {}; - if (GetSystemMemoryStatus(&mem_status)) { - static const DWORDLONG kLargeMemoryThresholdBytes = - static_cast(kLargeMemoryThresholdMb) * kMBBytes; - high_memory = mem_status.ullTotalPhys >= kLargeMemoryThresholdBytes; - } - - if (high_memory) { - moderate_threshold_mb_ = kLargeMemoryDefaultModerateThresholdMb; - critical_threshold_mb_ = kLargeMemoryDefaultCriticalThresholdMb; - } else { - moderate_threshold_mb_ = kSmallMemoryDefaultModerateThresholdMb; - critical_threshold_mb_ = kSmallMemoryDefaultCriticalThresholdMb; - } -} - -void MemoryPressureMonitor::StartObserving() { - DCHECK(thread_checker_.CalledOnValidThread()); - - timer_.Start(FROM_HERE, - TimeDelta::FromMilliseconds(kPollingIntervalMs), - Bind(&MemoryPressureMonitor:: - CheckMemoryPressureAndRecordStatistics, - weak_ptr_factory_.GetWeakPtr())); -} - -void MemoryPressureMonitor::StopObserving() { - DCHECK(thread_checker_.CalledOnValidThread()); - - // If StartObserving failed, StopObserving will still get called. - timer_.Stop(); - weak_ptr_factory_.InvalidateWeakPtrs(); -} - -void MemoryPressureMonitor::CheckMemoryPressure() { - DCHECK(thread_checker_.CalledOnValidThread()); - - // Get the previous pressure level and update the current one. - MemoryPressureLevel old_pressure = current_memory_pressure_level_; - current_memory_pressure_level_ = CalculateCurrentPressureLevel(); - - // |notify| will be set to true if MemoryPressureListeners need to be - // notified of a memory pressure level state change. - bool notify = false; - switch (current_memory_pressure_level_) { - case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE: - break; - - case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE: - if (old_pressure != current_memory_pressure_level_) { - // This is a new transition to moderate pressure so notify. - moderate_pressure_repeat_count_ = 0; - notify = true; - } else { - // Already in moderate pressure, only notify if sustained over the - // cooldown period. - if (++moderate_pressure_repeat_count_ == - kModeratePressureCooldownCycles) { - moderate_pressure_repeat_count_ = 0; - notify = true; - } - } - break; - - case MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL: - // Always notify of critical pressure levels. - notify = true; - break; - } - - if (!notify) - return; - - // Emit a notification of the current memory pressure level. This can only - // happen for moderate and critical pressure levels. - DCHECK_NE(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - current_memory_pressure_level_); - dispatch_callback_.Run(current_memory_pressure_level_); -} - -void MemoryPressureMonitor::CheckMemoryPressureAndRecordStatistics() { - DCHECK(thread_checker_.CalledOnValidThread()); - - CheckMemoryPressure(); - - RecordMemoryPressure(current_memory_pressure_level_, 1); -} - -MemoryPressureListener::MemoryPressureLevel -MemoryPressureMonitor::CalculateCurrentPressureLevel() { - MEMORYSTATUSEX mem_status = {}; - if (!GetSystemMemoryStatus(&mem_status)) - return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; - - // How much system memory is actively available for use right now, in MBs. - int phys_free = static_cast(mem_status.ullAvailPhys / kMBBytes); - - // TODO(chrisha): This should eventually care about address space pressure, - // but the browser process (where this is running) effectively never runs out - // of address space. Renderers occasionally do, but it does them no good to - // have the browser process monitor address space pressure. Long term, - // renderers should run their own address space pressure monitors and act - // accordingly, with the browser making cross-process decisions based on - // system memory pressure. - - // Determine if the physical memory is under critical memory pressure. - if (phys_free <= critical_threshold_mb_) - return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL; - - // Determine if the physical memory is under moderate memory pressure. - if (phys_free <= moderate_threshold_mb_) - return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE; - - // No memory pressure was detected. - return MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; -} - -bool MemoryPressureMonitor::GetSystemMemoryStatus( - MEMORYSTATUSEX* mem_status) { - DCHECK(mem_status != nullptr); - mem_status->dwLength = sizeof(*mem_status); - if (!::GlobalMemoryStatusEx(mem_status)) - return false; - return true; -} - -void MemoryPressureMonitor::SetDispatchCallback( - const DispatchCallback& callback) { - dispatch_callback_ = callback; -} - -} // namespace win -} // namespace base diff --git a/memory/memory_pressure_monitor_win.h b/memory/memory_pressure_monitor_win.h deleted file mode 100644 index a65c191a4..000000000 --- a/memory/memory_pressure_monitor_win.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_MEMORY_PRESSURE_MONITOR_WIN_H_ -#define BASE_MEMORY_MEMORY_PRESSURE_MONITOR_WIN_H_ - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/memory/memory_pressure_listener.h" -#include "base/memory/memory_pressure_monitor.h" -#include "base/memory/weak_ptr.h" -#include "base/threading/thread_checker.h" -#include "base/timer/timer.h" - -// To not pull in windows.h. -typedef struct _MEMORYSTATUSEX MEMORYSTATUSEX; - -namespace base { -namespace win { - -// Windows memory pressure monitor. Because there is no OS provided signal this -// polls at a low frequency (once per second), and applies internal hysteresis. -class BASE_EXPORT MemoryPressureMonitor : public base::MemoryPressureMonitor { - public: - // Constants governing the polling and hysteresis behaviour of the observer. - - // The polling interval, in milliseconds. While under critical pressure, this - // is also the timer to repeat cleanup attempts. - static const int kPollingIntervalMs; - // The time which should pass between 2 successive moderate memory pressure - // signals, in milliseconds. - static const int kModeratePressureCooldownMs; - // The number of cycles that should pass between 2 successive moderate memory - // pressure signals. - static const int kModeratePressureCooldownCycles; - - // Constants governing the memory pressure level detection. - - // The amount of total system memory beyond which a system is considered to be - // a large-memory system. - static const int kLargeMemoryThresholdMb; - // Default minimum free memory thresholds for small-memory systems, in MB. - static const int kSmallMemoryDefaultModerateThresholdMb; - static const int kSmallMemoryDefaultCriticalThresholdMb; - // Default minimum free memory thresholds for large-memory systems, in MB. - static const int kLargeMemoryDefaultModerateThresholdMb; - static const int kLargeMemoryDefaultCriticalThresholdMb; - - // Default constructor. Will choose thresholds automatically basd on the - // actual amount of system memory. - MemoryPressureMonitor(); - - // Constructor with explicit memory thresholds. These represent the amount of - // free memory below which the applicable memory pressure state engages. - MemoryPressureMonitor(int moderate_threshold_mb, int critical_threshold_mb); - - ~MemoryPressureMonitor() override; - - // Schedules a memory pressure check to run soon. This must be called on the - // same thread where the monitor was instantiated. - void CheckMemoryPressureSoon(); - - // Get the current memory pressure level. This can be called from any thread. - MemoryPressureLevel GetCurrentPressureLevel() override; - void SetDispatchCallback(const DispatchCallback& callback) override; - - // Returns the moderate pressure level free memory threshold, in MB. - int moderate_threshold_mb() const { return moderate_threshold_mb_; } - - // Returns the critical pressure level free memory threshold, in MB. - int critical_threshold_mb() const { return critical_threshold_mb_; } - - protected: - // Internals are exposed for unittests. - - // Automatically infers threshold values based on system memory. This invokes - // GetMemoryStatus so it can be mocked in unittests. - void InferThresholds(); - - // Starts observing the memory fill level. Calls to StartObserving should - // always be matched with calls to StopObserving. - void StartObserving(); - - // Stop observing the memory fill level. May be safely called if - // StartObserving has not been called. Must be called from the same thread on - // which the monitor was instantiated. - void StopObserving(); - - // Checks memory pressure, storing the current level, applying any hysteresis - // and emitting memory pressure level change signals as necessary. This - // function is called periodically while the monitor is observing memory - // pressure. This is split out from CheckMemoryPressureAndRecordStatistics so - // that it may be called by CheckMemoryPressureSoon and not invoke UMA - // logging. Must be called from the same thread on which the monitor was - // instantiated. - void CheckMemoryPressure(); - - // Wrapper to CheckMemoryPressure that also records the observed memory - // pressure level via an UMA enumeration. This is the function that is called - // periodically by the timer. Must be called from the same thread on which the - // monitor was instantiated. - void CheckMemoryPressureAndRecordStatistics(); - - // Calculates the current instantaneous memory pressure level. This does not - // use any hysteresis and simply returns the result at the current moment. Can - // be called on any thread. - MemoryPressureLevel CalculateCurrentPressureLevel(); - - // Gets system memory status. This is virtual as a unittesting hook. Returns - // true if the system call succeeds, false otherwise. Can be called on any - // thread. - virtual bool GetSystemMemoryStatus(MEMORYSTATUSEX* mem_status); - - private: - // Threshold amounts of available memory that trigger pressure levels. See - // memory_pressure_monitor.cc for a discussion of reasonable values for these. - int moderate_threshold_mb_; - int critical_threshold_mb_; - - // A periodic timer to check for memory pressure changes. - base::RepeatingTimer timer_; - - // The current memory pressure. - MemoryPressureLevel current_memory_pressure_level_; - - // To slow down the amount of moderate pressure event calls, this gets used to - // count the number of events since the last event occured. This is used by - // |CheckMemoryPressure| to apply hysteresis on the raw results of - // |CalculateCurrentPressureLevel|. - int moderate_pressure_repeat_count_; - - // Ensures that this object is used from a single thread. - base::ThreadChecker thread_checker_; - - DispatchCallback dispatch_callback_; - - // Weak pointer factory to ourself used for scheduling calls to - // CheckMemoryPressure/CheckMemoryPressureAndRecordStatistics via |timer_|. - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(MemoryPressureMonitor); -}; - -} // namespace win -} // namespace base - -#endif // BASE_MEMORY_MEMORY_PRESSURE_MONITOR_WIN_H_ diff --git a/memory/memory_pressure_monitor_win_unittest.cc b/memory/memory_pressure_monitor_win_unittest.cc deleted file mode 100644 index 1002a0178..000000000 --- a/memory/memory_pressure_monitor_win_unittest.cc +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/memory_pressure_monitor_win.h" - -#include "base/macros.h" -#include "base/memory/memory_pressure_listener.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace win { - -namespace { - -struct PressureSettings { - int phys_left_mb; - MemoryPressureListener::MemoryPressureLevel level; -}; - -} // namespace - -// This is outside of the anonymous namespace so that it can be seen as a friend -// to the monitor class. -class TestMemoryPressureMonitor : public MemoryPressureMonitor { - public: - using MemoryPressureMonitor::CalculateCurrentPressureLevel; - using MemoryPressureMonitor::CheckMemoryPressure; - - static const DWORDLONG kMBBytes = 1024 * 1024; - - explicit TestMemoryPressureMonitor(bool large_memory) - : mem_status_() { - // Generate a plausible amount of memory. - mem_status_.ullTotalPhys = - static_cast(GenerateTotalMemoryMb(large_memory)) * kMBBytes; - - // Rerun InferThresholds using the test fixture's GetSystemMemoryStatus. - InferThresholds(); - // Stop the timer. - StopObserving(); - } - - TestMemoryPressureMonitor(int system_memory_mb, - int moderate_threshold_mb, - int critical_threshold_mb) - : MemoryPressureMonitor(moderate_threshold_mb, critical_threshold_mb), - mem_status_() { - // Set the amount of system memory. - mem_status_.ullTotalPhys = static_cast( - system_memory_mb * kMBBytes); - - // Stop the timer. - StopObserving(); - } - - virtual ~TestMemoryPressureMonitor() {} - - MOCK_METHOD1(OnMemoryPressure, - void(MemoryPressureListener::MemoryPressureLevel level)); - - // Generates an amount of total memory that is consistent with the requested - // memory model. - int GenerateTotalMemoryMb(bool large_memory) { - int total_mb = 64; - while (total_mb < MemoryPressureMonitor::kLargeMemoryThresholdMb) - total_mb *= 2; - if (large_memory) - return total_mb * 2; - return total_mb / 2; - } - - // Sets up the memory status to reflect the provided absolute memory left. - void SetMemoryFree(int phys_left_mb) { - // ullTotalPhys is set in the constructor and not modified. - - // Set the amount of available memory. - mem_status_.ullAvailPhys = - static_cast(phys_left_mb) * kMBBytes; - DCHECK_LT(mem_status_.ullAvailPhys, mem_status_.ullTotalPhys); - - // These fields are unused. - mem_status_.dwMemoryLoad = 0; - mem_status_.ullTotalPageFile = 0; - mem_status_.ullAvailPageFile = 0; - mem_status_.ullTotalVirtual = 0; - mem_status_.ullAvailVirtual = 0; - } - - void SetNone() { - SetMemoryFree(moderate_threshold_mb() + 1); - } - - void SetModerate() { - SetMemoryFree(moderate_threshold_mb() - 1); - } - - void SetCritical() { - SetMemoryFree(critical_threshold_mb() - 1); - } - - private: - bool GetSystemMemoryStatus(MEMORYSTATUSEX* mem_status) override { - // Simply copy the memory status set by the test fixture. - *mem_status = mem_status_; - return true; - } - - MEMORYSTATUSEX mem_status_; - - DISALLOW_COPY_AND_ASSIGN(TestMemoryPressureMonitor); -}; - -class WinMemoryPressureMonitorTest : public testing::Test { - protected: - void CalculateCurrentMemoryPressureLevelTest( - TestMemoryPressureMonitor* monitor) { - - int mod = monitor->moderate_threshold_mb(); - monitor->SetMemoryFree(mod + 1); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - monitor->CalculateCurrentPressureLevel()); - - monitor->SetMemoryFree(mod); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor->CalculateCurrentPressureLevel()); - - monitor->SetMemoryFree(mod - 1); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor->CalculateCurrentPressureLevel()); - - int crit = monitor->critical_threshold_mb(); - monitor->SetMemoryFree(crit + 1); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor->CalculateCurrentPressureLevel()); - - monitor->SetMemoryFree(crit); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, - monitor->CalculateCurrentPressureLevel()); - - monitor->SetMemoryFree(crit - 1); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, - monitor->CalculateCurrentPressureLevel()); - } - - base::MessageLoopForUI message_loop_; -}; - -// Tests the fundamental direct calculation of memory pressure with automatic -// small-memory thresholds. -TEST_F(WinMemoryPressureMonitorTest, CalculateCurrentMemoryPressureLevelSmall) { - static const int kModerateMb = - MemoryPressureMonitor::kSmallMemoryDefaultModerateThresholdMb; - static const int kCriticalMb = - MemoryPressureMonitor::kSmallMemoryDefaultCriticalThresholdMb; - - TestMemoryPressureMonitor monitor(false); // Small-memory model. - - EXPECT_EQ(kModerateMb, monitor.moderate_threshold_mb()); - EXPECT_EQ(kCriticalMb, monitor.critical_threshold_mb()); - - ASSERT_NO_FATAL_FAILURE(CalculateCurrentMemoryPressureLevelTest(&monitor)); -} - -// Tests the fundamental direct calculation of memory pressure with automatic -// large-memory thresholds. -TEST_F(WinMemoryPressureMonitorTest, CalculateCurrentMemoryPressureLevelLarge) { - static const int kModerateMb = - MemoryPressureMonitor::kLargeMemoryDefaultModerateThresholdMb; - static const int kCriticalMb = - MemoryPressureMonitor::kLargeMemoryDefaultCriticalThresholdMb; - - TestMemoryPressureMonitor monitor(true); // Large-memory model. - - EXPECT_EQ(kModerateMb, monitor.moderate_threshold_mb()); - EXPECT_EQ(kCriticalMb, monitor.critical_threshold_mb()); - - ASSERT_NO_FATAL_FAILURE(CalculateCurrentMemoryPressureLevelTest(&monitor)); -} - -// Tests the fundamental direct calculation of memory pressure with manually -// specified threshold levels. -TEST_F(WinMemoryPressureMonitorTest, - CalculateCurrentMemoryPressureLevelCustom) { - static const int kSystemMb = 512; - static const int kModerateMb = 256; - static const int kCriticalMb = 128; - - TestMemoryPressureMonitor monitor(kSystemMb, kModerateMb, kCriticalMb); - - EXPECT_EQ(kModerateMb, monitor.moderate_threshold_mb()); - EXPECT_EQ(kCriticalMb, monitor.critical_threshold_mb()); - - ASSERT_NO_FATAL_FAILURE(CalculateCurrentMemoryPressureLevelTest(&monitor)); -} - -// This test tests the various transition states from memory pressure, looking -// for the correct behavior on event reposting as well as state updates. -TEST_F(WinMemoryPressureMonitorTest, CheckMemoryPressure) { - // Large-memory. - testing::StrictMock monitor(true); - MemoryPressureListener listener( - base::Bind(&TestMemoryPressureMonitor::OnMemoryPressure, - base::Unretained(&monitor))); - - // Checking the memory pressure at 0% load should not produce any - // events. - monitor.SetNone(); - monitor.CheckMemoryPressure(); - RunLoop().RunUntilIdle(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - monitor.GetCurrentPressureLevel()); - - // Setting the memory level to 80% should produce a moderate pressure level. - EXPECT_CALL(monitor, - OnMemoryPressure(MemoryPressureListener:: - MEMORY_PRESSURE_LEVEL_MODERATE)); - monitor.SetModerate(); - monitor.CheckMemoryPressure(); - RunLoop().RunUntilIdle(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor.GetCurrentPressureLevel()); - testing::Mock::VerifyAndClearExpectations(&monitor); - - // Check that the event gets reposted after a while. - for (int i = 0; i < monitor.kModeratePressureCooldownCycles; ++i) { - if (i + 1 == monitor.kModeratePressureCooldownCycles) { - EXPECT_CALL(monitor, - OnMemoryPressure(MemoryPressureListener:: - MEMORY_PRESSURE_LEVEL_MODERATE)); - } - monitor.CheckMemoryPressure(); - RunLoop().RunUntilIdle(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor.GetCurrentPressureLevel()); - testing::Mock::VerifyAndClearExpectations(&monitor); - } - - // Setting the memory usage to 99% should produce critical levels. - EXPECT_CALL(monitor, - OnMemoryPressure(MemoryPressureListener:: - MEMORY_PRESSURE_LEVEL_CRITICAL)); - monitor.SetCritical(); - monitor.CheckMemoryPressure(); - RunLoop().RunUntilIdle(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, - monitor.GetCurrentPressureLevel()); - testing::Mock::VerifyAndClearExpectations(&monitor); - - // Calling it again should immediately produce a second call. - EXPECT_CALL(monitor, - OnMemoryPressure(MemoryPressureListener:: - MEMORY_PRESSURE_LEVEL_CRITICAL)); - monitor.CheckMemoryPressure(); - RunLoop().RunUntilIdle(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, - monitor.GetCurrentPressureLevel()); - testing::Mock::VerifyAndClearExpectations(&monitor); - - // When lowering the pressure again there should be a notification and the - // pressure should go back to moderate. - EXPECT_CALL(monitor, - OnMemoryPressure(MemoryPressureListener:: - MEMORY_PRESSURE_LEVEL_MODERATE)); - monitor.SetModerate(); - monitor.CheckMemoryPressure(); - RunLoop().RunUntilIdle(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor.GetCurrentPressureLevel()); - testing::Mock::VerifyAndClearExpectations(&monitor); - - // Check that the event gets reposted after a while. - for (int i = 0; i < monitor.kModeratePressureCooldownCycles; ++i) { - if (i + 1 == monitor.kModeratePressureCooldownCycles) { - EXPECT_CALL(monitor, - OnMemoryPressure(MemoryPressureListener:: - MEMORY_PRESSURE_LEVEL_MODERATE)); - } - monitor.CheckMemoryPressure(); - RunLoop().RunUntilIdle(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, - monitor.GetCurrentPressureLevel()); - testing::Mock::VerifyAndClearExpectations(&monitor); - } - - // Going down to no pressure should not produce an notification. - monitor.SetNone(); - monitor.CheckMemoryPressure(); - RunLoop().RunUntilIdle(); - EXPECT_EQ(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, - monitor.GetCurrentPressureLevel()); - testing::Mock::VerifyAndClearExpectations(&monitor); -} - -} // namespace win -} // namespace base diff --git a/memory/platform_shared_memory_region.cc b/memory/platform_shared_memory_region.cc deleted file mode 100644 index c145336eb..000000000 --- a/memory/platform_shared_memory_region.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2018 The Chromium 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/platform_shared_memory_region.h" - -#include "base/memory/shared_memory_mapping.h" - -namespace base { -namespace subtle { - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::CreateWritable( - size_t size) { - return Create(Mode::kWritable, size); -} - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::CreateUnsafe( - size_t size) { - return Create(Mode::kUnsafe, size); -} - -PlatformSharedMemoryRegion::PlatformSharedMemoryRegion() = default; -PlatformSharedMemoryRegion::PlatformSharedMemoryRegion( - PlatformSharedMemoryRegion&& other) = default; -PlatformSharedMemoryRegion& PlatformSharedMemoryRegion::operator=( - PlatformSharedMemoryRegion&& other) = default; -PlatformSharedMemoryRegion::~PlatformSharedMemoryRegion() = default; - -PlatformSharedMemoryRegion::ScopedPlatformHandle -PlatformSharedMemoryRegion::PassPlatformHandle() { - return std::move(handle_); -} - -} // namespace subtle -} // namespace base diff --git a/memory/platform_shared_memory_region.h b/memory/platform_shared_memory_region.h deleted file mode 100644 index 3d830b6f9..000000000 --- a/memory/platform_shared_memory_region.h +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2018 The Chromium 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_PLATFORM_SHARED_MEMORY_REGION_H_ -#define BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_ - -#include - -#include "base/compiler_specific.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/unguessable_token.h" -#include "build/build_config.h" - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include -#include "base/mac/scoped_mach_port.h" -#elif defined(OS_FUCHSIA) -#include -#elif defined(OS_WIN) -#include "base/win/scoped_handle.h" -#include "base/win/windows_types.h" -#elif defined(OS_POSIX) -#include -#include "base/file_descriptor_posix.h" -#include "base/files/scoped_file.h" -#endif - -namespace base { -namespace subtle { - -#if defined(OS_POSIX) && (!defined(OS_MACOSX) || defined(OS_IOS)) && \ - !defined(OS_ANDROID) -// Helper structs to keep two descriptors on POSIX. It's needed to support -// ConvertToReadOnly(). -struct BASE_EXPORT FDPair { - int fd; - int readonly_fd; -}; - -struct BASE_EXPORT ScopedFDPair { - ScopedFDPair(); - ScopedFDPair(ScopedFD in_fd, ScopedFD in_readonly_fd); - ScopedFDPair(ScopedFDPair&&); - ScopedFDPair& operator=(ScopedFDPair&&); - ~ScopedFDPair(); - - FDPair get() const; - - ScopedFD fd; - ScopedFD readonly_fd; -}; -#endif - -// Implementation class for shared memory regions. -// -// This class does the following: -// -// - Wraps and owns a shared memory region platform handle. -// - Provides a way to allocate a new region of platform shared memory of given -// size. -// - Provides a way to create mapping of the region in the current process' -// address space, under special access-control constraints (see Mode). -// - Provides methods to help transferring the handle across process boundaries. -// - Holds a 128-bit unique identifier used to uniquely identify the same -// kernel region resource across processes (used for memory tracking). -// - Has a method to retrieve the region's size in bytes. -// -// IMPORTANT NOTE: Users should never use this directly, but -// ReadOnlySharedMemoryRegion, WritableSharedMemoryRegion or -// UnsafeSharedMemoryRegion since this is an implementation class. -class BASE_EXPORT PlatformSharedMemoryRegion { - public: - // Permission mode of the platform handle. Each mode corresponds to one of the - // typed shared memory classes: - // - // * ReadOnlySharedMemoryRegion: A region that can only create read-only - // mappings. - // - // * WritableSharedMemoryRegion: A region that can only create writable - // mappings. The region can be demoted to ReadOnlySharedMemoryRegion without - // the possibility of promoting back to writable. - // - // * UnsafeSharedMemoryRegion: A region that can only create writable - // mappings. The region cannot be demoted to ReadOnlySharedMemoryRegion. - enum class Mode { - kReadOnly, // ReadOnlySharedMemoryRegion - kWritable, // WritableSharedMemoryRegion - kUnsafe, // UnsafeSharedMemoryRegion - kMaxValue = kUnsafe - }; - -// Platform-specific shared memory type used by this class. -#if defined(OS_MACOSX) && !defined(OS_IOS) - using PlatformHandle = mach_port_t; - using ScopedPlatformHandle = mac::ScopedMachSendRight; -#elif defined(OS_FUCHSIA) - using PlatformHandle = zx_handle_t; - using ScopedPlatformHandle = zx::vmo; -#elif defined(OS_WIN) - using PlatformHandle = HANDLE; - using ScopedPlatformHandle = win::ScopedHandle; -#elif defined(OS_ANDROID) - using PlatformHandle = int; - using ScopedPlatformHandle = ScopedFD; -#else - using PlatformHandle = FDPair; - using ScopedPlatformHandle = ScopedFDPair; -#endif - - // The minimum alignment in bytes that any mapped address produced by Map() - // and MapAt() is guaranteed to have. - enum { kMapMinimumAlignment = 32 }; - - // Creates a new PlatformSharedMemoryRegion with corresponding mode and size. - // Creating in kReadOnly mode isn't supported because then there will be no - // way to modify memory content. - static PlatformSharedMemoryRegion CreateWritable(size_t size); - static PlatformSharedMemoryRegion CreateUnsafe(size_t size); - - // Returns a new PlatformSharedMemoryRegion that takes ownership of the - // |handle|. All parameters must be taken from another valid - // PlatformSharedMemoryRegion instance, e.g. |size| must be equal to the - // actual region size as allocated by the kernel. - // Closes the |handle| and returns an invalid instance if passed parameters - // are invalid. - static PlatformSharedMemoryRegion Take(ScopedPlatformHandle handle, - Mode mode, - size_t size, - const UnguessableToken& guid); - - // Default constructor initializes an invalid instance, i.e. an instance that - // doesn't wrap any valid platform handle. - PlatformSharedMemoryRegion(); - - // Move operations are allowed. - PlatformSharedMemoryRegion(PlatformSharedMemoryRegion&&); - PlatformSharedMemoryRegion& operator=(PlatformSharedMemoryRegion&&); - - // Destructor closes the platform handle. Does nothing if the handle is - // invalid. - ~PlatformSharedMemoryRegion(); - - // Passes ownership of the platform handle to the caller. The current instance - // becomes invalid. It's the responsibility of the caller to close the handle. - ScopedPlatformHandle PassPlatformHandle() WARN_UNUSED_RESULT; - - // Returns the platform handle. The current instance keeps ownership of this - // handle. - PlatformHandle GetPlatformHandle() const; - - // Whether the platform handle is valid. - bool IsValid() const; - - // Duplicates the platform handle and creates a new PlatformSharedMemoryRegion - // with the same |mode_|, |size_| and |guid_| that owns this handle. Returns - // invalid region on failure, the current instance remains valid. - // Can be called only in kReadOnly and kUnsafe modes, CHECK-fails if is - // called in kWritable mode. - PlatformSharedMemoryRegion Duplicate() const; - - // Converts the region to read-only. Returns whether the operation succeeded. - // Makes the current instance invalid on failure. Can be called only in - // kWritable mode, all other modes will CHECK-fail. The object will have - // kReadOnly mode after this call on success. - bool ConvertToReadOnly(); -#if defined(OS_MACOSX) && !defined(OS_IOS) - // Same as above, but |mapped_addr| is used as a hint to avoid additional - // mapping of the memory object. - // |mapped_addr| must be mapped location of |memory_object_|. If the location - // is unknown, |mapped_addr| should be |nullptr|. - bool ConvertToReadOnly(void* mapped_addr); -#endif // defined(OS_MACOSX) && !defined(OS_IOS) - - // Converts the region to unsafe. Returns whether the operation succeeded. - // Makes the current instance invalid on failure. Can be called only in - // kWritable mode, all other modes will CHECK-fail. The object will have - // kUnsafe mode after this call on success. - bool ConvertToUnsafe(); - - // Maps |size| bytes of the shared memory region starting with the given - // |offset| into the caller's address space. |offset| must be aligned to value - // of |SysInfo::VMAllocationGranularity()|. Fails if requested bytes are out - // of the region limits. - // Returns true and sets |memory| and |mapped_size| on success, returns false - // and leaves output parameters in unspecified state otherwise. The mapped - // address is guaranteed to have an alignment of at least - // |kMapMinimumAlignment|. - bool MapAt(off_t offset, - size_t size, - void** memory, - size_t* mapped_size) const; - - const UnguessableToken& GetGUID() const { return guid_; } - - size_t GetSize() const { return size_; } - - Mode GetMode() const { return mode_; } - - private: - FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest, - CreateReadOnlyRegionDeathTest); - FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest, - CheckPlatformHandlePermissionsCorrespondToMode); - static PlatformSharedMemoryRegion Create(Mode mode, size_t size); - - static bool CheckPlatformHandlePermissionsCorrespondToMode( - PlatformHandle handle, - Mode mode, - size_t size); - - PlatformSharedMemoryRegion(ScopedPlatformHandle handle, - Mode mode, - size_t size, - const UnguessableToken& guid); - - ScopedPlatformHandle handle_; - Mode mode_ = Mode::kReadOnly; - size_t size_ = 0; - UnguessableToken guid_; - - DISALLOW_COPY_AND_ASSIGN(PlatformSharedMemoryRegion); -}; - -} // namespace subtle -} // namespace base - -#endif // BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_ diff --git a/memory/platform_shared_memory_region_android.cc b/memory/platform_shared_memory_region_android.cc deleted file mode 100644 index 6c92b5e70..000000000 --- a/memory/platform_shared_memory_region_android.cc +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2018 The Chromium 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/platform_shared_memory_region.h" - -#include - -#include "base/memory/shared_memory_tracker.h" -#include "base/posix/eintr_wrapper.h" -#include "third_party/ashmem/ashmem.h" - -namespace base { -namespace subtle { - -// For Android, we use ashmem to implement SharedMemory. ashmem_create_region -// will automatically pin the region. We never explicitly call pin/unpin. When -// all the file descriptors from different processes associated with the region -// are closed, the memory buffer will go away. - -namespace { - -static int GetAshmemRegionProtectionMask(int fd) { - int prot = ashmem_get_prot_region(fd); - if (prot < 0) { - DPLOG(ERROR) << "ashmem_get_prot_region failed"; - return -1; - } - return prot; -} - -} // namespace - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take( - ScopedFD fd, - Mode mode, - size_t size, - const UnguessableToken& guid) { - if (!fd.is_valid()) - return {}; - - if (size == 0) - return {}; - - if (size > static_cast(std::numeric_limits::max())) - return {}; - - CHECK(CheckPlatformHandlePermissionsCorrespondToMode(fd.get(), mode, size)); - - return PlatformSharedMemoryRegion(std::move(fd), mode, size, guid); -} - -int PlatformSharedMemoryRegion::GetPlatformHandle() const { - return handle_.get(); -} - -bool PlatformSharedMemoryRegion::IsValid() const { - return handle_.is_valid(); -} - -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const { - if (!IsValid()) - return {}; - - CHECK_NE(mode_, Mode::kWritable) - << "Duplicating a writable shared memory region is prohibited"; - - ScopedFD duped_fd(HANDLE_EINTR(dup(handle_.get()))); - if (!duped_fd.is_valid()) { - DPLOG(ERROR) << "dup(" << handle_.get() << ") failed"; - return {}; - } - - return PlatformSharedMemoryRegion(std::move(duped_fd), mode_, size_, guid_); -} - -bool PlatformSharedMemoryRegion::ConvertToReadOnly() { - if (!IsValid()) - return false; - - CHECK_EQ(mode_, Mode::kWritable) - << "Only writable shared memory region can be converted to read-only"; - - ScopedFD handle_copy(handle_.release()); - - int prot = GetAshmemRegionProtectionMask(handle_copy.get()); - if (prot < 0) - return false; - - prot &= ~PROT_WRITE; - int ret = ashmem_set_prot_region(handle_copy.get(), prot); - if (ret != 0) { - DPLOG(ERROR) << "ashmem_set_prot_region failed"; - return false; - } - - handle_ = std::move(handle_copy); - mode_ = Mode::kReadOnly; - return true; -} - -bool PlatformSharedMemoryRegion::ConvertToUnsafe() { - if (!IsValid()) - return false; - - CHECK_EQ(mode_, Mode::kWritable) - << "Only writable shared memory region can be converted to unsafe"; - - mode_ = Mode::kUnsafe; - return true; -} - -bool PlatformSharedMemoryRegion::MapAt(off_t offset, - size_t size, - void** memory, - size_t* mapped_size) const { - if (!IsValid()) - return false; - - size_t end_byte; - if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) { - return false; - } - - bool write_allowed = mode_ != Mode::kReadOnly; - *memory = mmap(nullptr, size, PROT_READ | (write_allowed ? PROT_WRITE : 0), - MAP_SHARED, handle_.get(), offset); - - bool mmap_succeeded = *memory && *memory != reinterpret_cast(-1); - if (!mmap_succeeded) { - DPLOG(ERROR) << "mmap " << handle_.get() << " failed"; - return false; - } - - *mapped_size = size; - DCHECK_EQ(0U, - reinterpret_cast(*memory) & (kMapMinimumAlignment - 1)); - return true; -} - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode, - size_t size) { - if (size == 0) - return {}; - - if (size > static_cast(std::numeric_limits::max())) - return {}; - - CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will " - "lead to this region being non-modifiable"; - - UnguessableToken guid = UnguessableToken::Create(); - - ScopedFD fd(ashmem_create_region( - SharedMemoryTracker::GetDumpNameForTracing(guid).c_str(), size)); - if (!fd.is_valid()) { - DPLOG(ERROR) << "ashmem_create_region failed"; - return {}; - } - - int err = ashmem_set_prot_region(fd.get(), PROT_READ | PROT_WRITE); - if (err < 0) { - DPLOG(ERROR) << "ashmem_set_prot_region failed"; - return {}; - } - - return PlatformSharedMemoryRegion(std::move(fd), mode, size, guid); -} - -bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode( - PlatformHandle handle, - Mode mode, - size_t size) { - int prot = GetAshmemRegionProtectionMask(handle); - if (prot < 0) - return false; - - bool is_read_only = (prot & PROT_WRITE) == 0; - bool expected_read_only = mode == Mode::kReadOnly; - - if (is_read_only != expected_read_only) { - DLOG(ERROR) << "Ashmem region has a wrong protection mask: it is" - << (is_read_only ? " " : " not ") << "read-only but it should" - << (expected_read_only ? " " : " not ") << "be"; - return false; - } - - return true; -} - -PlatformSharedMemoryRegion::PlatformSharedMemoryRegion( - ScopedFD fd, - Mode mode, - size_t size, - const UnguessableToken& guid) - : handle_(std::move(fd)), mode_(mode), size_(size), guid_(guid) {} - -} // namespace subtle -} // namespace base diff --git a/memory/platform_shared_memory_region_fuchsia.cc b/memory/platform_shared_memory_region_fuchsia.cc deleted file mode 100644 index a3e195860..000000000 --- a/memory/platform_shared_memory_region_fuchsia.cc +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2018 The Chromium 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/platform_shared_memory_region.h" - -#include -#include -#include - -#include "base/bits.h" -#include "base/fuchsia/fuchsia_logging.h" -#include "base/numerics/checked_math.h" -#include "base/process/process_metrics.h" - -namespace base { -namespace subtle { - -static constexpr int kNoWriteOrExec = - ZX_DEFAULT_VMO_RIGHTS & - ~(ZX_RIGHT_WRITE | ZX_RIGHT_EXECUTE | ZX_RIGHT_SET_PROPERTY); - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take( - zx::vmo handle, - Mode mode, - size_t size, - const UnguessableToken& guid) { - if (!handle.is_valid()) - return {}; - - if (size == 0) - return {}; - - if (size > static_cast(std::numeric_limits::max())) - return {}; - - CHECK( - CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size)); - - return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid); -} - -zx_handle_t PlatformSharedMemoryRegion::GetPlatformHandle() const { - return handle_.get(); -} - -bool PlatformSharedMemoryRegion::IsValid() const { - return handle_.is_valid(); -} - -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const { - if (!IsValid()) - return {}; - - CHECK_NE(mode_, Mode::kWritable) - << "Duplicating a writable shared memory region is prohibited"; - - zx::vmo duped_handle; - zx_status_t status = handle_.duplicate(ZX_RIGHT_SAME_RIGHTS, &duped_handle); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "zx_handle_duplicate"; - return {}; - } - - return PlatformSharedMemoryRegion(std::move(duped_handle), mode_, size_, - guid_); -} - -bool PlatformSharedMemoryRegion::ConvertToReadOnly() { - if (!IsValid()) - return false; - - CHECK_EQ(mode_, Mode::kWritable) - << "Only writable shared memory region can be converted to read-only"; - - zx_status_t status = handle_.replace(kNoWriteOrExec, &handle_); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "zx_handle_replace"; - return false; - } - - mode_ = Mode::kReadOnly; - return true; -} - -bool PlatformSharedMemoryRegion::ConvertToUnsafe() { - if (!IsValid()) - return false; - - CHECK_EQ(mode_, Mode::kWritable) - << "Only writable shared memory region can be converted to unsafe"; - - mode_ = Mode::kUnsafe; - return true; -} - -bool PlatformSharedMemoryRegion::MapAt(off_t offset, - size_t size, - void** memory, - size_t* mapped_size) const { - if (!IsValid()) - return false; - - size_t end_byte; - if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) { - return false; - } - - bool write_allowed = mode_ != Mode::kReadOnly; - uintptr_t addr; - zx_status_t status = zx_vmar_map( - zx_vmar_root_self(), 0, handle_.get(), offset, size, - ZX_VM_FLAG_PERM_READ | (write_allowed ? ZX_VM_FLAG_PERM_WRITE : 0), - &addr); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "zx_vmar_map"; - return false; - } - - *memory = reinterpret_cast(addr); - *mapped_size = size; - DCHECK_EQ(0U, - reinterpret_cast(*memory) & (kMapMinimumAlignment - 1)); - return true; -} - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode, - size_t size) { - if (size == 0) - return {}; - - size_t rounded_size = bits::Align(size, GetPageSize()); - if (rounded_size > static_cast(std::numeric_limits::max())) - return {}; - - CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will " - "lead to this region being non-modifiable"; - - zx::vmo vmo; - zx_status_t status = zx::vmo::create(rounded_size, 0, &vmo); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "zx_vmo_create"; - return {}; - } - - const int kNoExecFlags = ZX_DEFAULT_VMO_RIGHTS & ~ZX_RIGHT_EXECUTE; - status = vmo.replace(kNoExecFlags, &vmo); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "zx_handle_replace"; - return {}; - } - - return PlatformSharedMemoryRegion(std::move(vmo), mode, size, - UnguessableToken::Create()); -} - -// static -bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode( - PlatformHandle handle, - Mode mode, - size_t size) { - zx_info_handle_basic_t basic = {}; - zx_status_t status = zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &basic, - sizeof(basic), nullptr, nullptr); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "zx_object_get_info"; - return false; - } - - bool is_read_only = (basic.rights & kNoWriteOrExec) == basic.rights; - bool expected_read_only = mode == Mode::kReadOnly; - - if (is_read_only != expected_read_only) { - DLOG(ERROR) << "VMO object has wrong access rights: it is" - << (is_read_only ? " " : " not ") << "read-only but it should" - << (expected_read_only ? " " : " not ") << "be"; - return false; - } - - return true; -} - -PlatformSharedMemoryRegion::PlatformSharedMemoryRegion( - zx::vmo handle, - Mode mode, - size_t size, - const UnguessableToken& guid) - : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {} - -} // namespace subtle -} // namespace base diff --git a/memory/platform_shared_memory_region_mac.cc b/memory/platform_shared_memory_region_mac.cc deleted file mode 100644 index 4a8b440c2..000000000 --- a/memory/platform_shared_memory_region_mac.cc +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2018 The Chromium 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/platform_shared_memory_region.h" - -#include - -#include "base/mac/mach_logging.h" -#include "base/mac/scoped_mach_vm.h" -#include "base/numerics/checked_math.h" -#include "build/build_config.h" - -#if defined(OS_IOS) -#error "MacOS only - iOS uses platform_shared_memory_region_posix.cc" -#endif - -namespace base { -namespace subtle { - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take( - mac::ScopedMachSendRight handle, - Mode mode, - size_t size, - const UnguessableToken& guid) { - if (!handle.is_valid()) - return {}; - - if (size == 0) - return {}; - - if (size > static_cast(std::numeric_limits::max())) - return {}; - - CHECK( - CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size)); - - return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid); -} - -mach_port_t PlatformSharedMemoryRegion::GetPlatformHandle() const { - return handle_.get(); -} - -bool PlatformSharedMemoryRegion::IsValid() const { - return handle_.is_valid(); -} - -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const { - if (!IsValid()) - return {}; - - CHECK_NE(mode_, Mode::kWritable) - << "Duplicating a writable shared memory region is prohibited"; - - // Increment the ref count. - kern_return_t kr = mach_port_mod_refs(mach_task_self(), handle_.get(), - MACH_PORT_RIGHT_SEND, 1); - if (kr != KERN_SUCCESS) { - MACH_DLOG(ERROR, kr) << "mach_port_mod_refs"; - return {}; - } - - return PlatformSharedMemoryRegion(mac::ScopedMachSendRight(handle_.get()), - mode_, size_, guid_); -} - -bool PlatformSharedMemoryRegion::ConvertToReadOnly() { - return ConvertToReadOnly(nullptr); -} - -bool PlatformSharedMemoryRegion::ConvertToReadOnly(void* mapped_addr) { - if (!IsValid()) - return false; - - CHECK_EQ(mode_, Mode::kWritable) - << "Only writable shared memory region can be converted to read-only"; - - mac::ScopedMachSendRight handle_copy(handle_.release()); - - void* temp_addr = mapped_addr; - mac::ScopedMachVM scoped_memory; - if (!temp_addr) { - // Intentionally lower current prot and max prot to |VM_PROT_READ|. - kern_return_t kr = mach_vm_map( - mach_task_self(), reinterpret_cast(&temp_addr), - size_, 0, VM_FLAGS_ANYWHERE, handle_copy.get(), 0, FALSE, VM_PROT_READ, - VM_PROT_READ, VM_INHERIT_NONE); - if (kr != KERN_SUCCESS) { - MACH_DLOG(ERROR, kr) << "mach_vm_map"; - return false; - } - scoped_memory.reset(reinterpret_cast(temp_addr), - mach_vm_round_page(size_)); - } - - // Make new memory object. - memory_object_size_t allocation_size = size_; - mac::ScopedMachSendRight named_right; - kern_return_t kr = mach_make_memory_entry_64( - mach_task_self(), &allocation_size, - reinterpret_cast(temp_addr), VM_PROT_READ, - named_right.receive(), MACH_PORT_NULL); - if (kr != KERN_SUCCESS) { - MACH_DLOG(ERROR, kr) << "mach_make_memory_entry_64"; - return false; - } - DCHECK_GE(allocation_size, size_); - - handle_ = std::move(named_right); - mode_ = Mode::kReadOnly; - return true; -} - -bool PlatformSharedMemoryRegion::ConvertToUnsafe() { - if (!IsValid()) - return false; - - CHECK_EQ(mode_, Mode::kWritable) - << "Only writable shared memory region can be converted to unsafe"; - - mode_ = Mode::kUnsafe; - return true; -} - -bool PlatformSharedMemoryRegion::MapAt(off_t offset, - size_t size, - void** memory, - size_t* mapped_size) const { - if (!IsValid()) - return false; - - size_t end_byte; - if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) { - return false; - } - - bool write_allowed = mode_ != Mode::kReadOnly; - vm_prot_t vm_prot_write = write_allowed ? VM_PROT_WRITE : 0; - kern_return_t kr = mach_vm_map( - mach_task_self(), - reinterpret_cast(memory), // Output parameter - size, - 0, // Alignment mask - VM_FLAGS_ANYWHERE, handle_.get(), offset, - FALSE, // Copy - VM_PROT_READ | vm_prot_write, // Current protection - VM_PROT_READ | vm_prot_write, // Maximum protection - VM_INHERIT_NONE); - if (kr != KERN_SUCCESS) { - MACH_DLOG(ERROR, kr) << "mach_vm_map"; - return false; - } - - *mapped_size = size; - DCHECK_EQ(0U, - reinterpret_cast(*memory) & (kMapMinimumAlignment - 1)); - return true; -} - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode, - size_t size) { - if (size == 0) - return {}; - - if (size > static_cast(std::numeric_limits::max())) - return {}; - - CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will " - "lead to this region being non-modifiable"; - - mach_vm_size_t vm_size = size; - mac::ScopedMachSendRight named_right; - kern_return_t kr = mach_make_memory_entry_64( - mach_task_self(), &vm_size, - 0, // Address. - MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE, - named_right.receive(), - MACH_PORT_NULL); // Parent handle. - if (kr != KERN_SUCCESS) { - MACH_DLOG(ERROR, kr) << "mach_make_memory_entry_64"; - return {}; - } - DCHECK_GE(vm_size, size); - - return PlatformSharedMemoryRegion(std::move(named_right), mode, size, - UnguessableToken::Create()); -} - -// static -bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode( - PlatformHandle handle, - Mode mode, - size_t size) { - mach_vm_address_t temp_addr = 0; - kern_return_t kr = - mach_vm_map(mach_task_self(), &temp_addr, size, 0, VM_FLAGS_ANYWHERE, - handle, 0, FALSE, VM_PROT_READ | VM_PROT_WRITE, - VM_PROT_READ | VM_PROT_WRITE, VM_INHERIT_NONE); - if (kr == KERN_SUCCESS) { - kern_return_t kr_deallocate = - mach_vm_deallocate(mach_task_self(), temp_addr, size); - MACH_DLOG_IF(ERROR, kr_deallocate != KERN_SUCCESS, kr_deallocate) - << "mach_vm_deallocate"; - } else if (kr != KERN_INVALID_RIGHT) { - MACH_DLOG(ERROR, kr) << "mach_vm_map"; - return false; - } - - bool is_read_only = kr == KERN_INVALID_RIGHT; - bool expected_read_only = mode == Mode::kReadOnly; - - if (is_read_only != expected_read_only) { - DLOG(ERROR) << "VM region has a wrong protection mask: it is" - << (is_read_only ? " " : " not ") << "read-only but it should" - << (expected_read_only ? " " : " not ") << "be"; - return false; - } - - return true; -} - -PlatformSharedMemoryRegion::PlatformSharedMemoryRegion( - mac::ScopedMachSendRight handle, - Mode mode, - size_t size, - const UnguessableToken& guid) - : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {} - -} // namespace subtle -} // namespace base diff --git a/memory/platform_shared_memory_region_posix.cc b/memory/platform_shared_memory_region_posix.cc deleted file mode 100644 index d4b6d5c00..000000000 --- a/memory/platform_shared_memory_region_posix.cc +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2018 The Chromium 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/platform_shared_memory_region.h" - -#include -#include -#include - -#include "base/files/file_util.h" -#include "base/numerics/checked_math.h" -#include "base/threading/thread_restrictions.h" -#include "build/build_config.h" - -namespace base { -namespace subtle { - -namespace { - -struct ScopedPathUnlinkerTraits { - static const FilePath* InvalidValue() { return nullptr; } - - static void Free(const FilePath* path) { - if (unlink(path->value().c_str())) - PLOG(WARNING) << "unlink"; - } -}; - -// Unlinks the FilePath when the object is destroyed. -using ScopedPathUnlinker = - ScopedGeneric; - -#if !defined(OS_NACL) -bool CheckFDAccessMode(int fd, int expected_mode) { - int fd_status = fcntl(fd, F_GETFL); - if (fd_status == -1) { - DPLOG(ERROR) << "fcntl(" << fd << ", F_GETFL) failed"; - return false; - } - - int mode = fd_status & O_ACCMODE; - if (mode != expected_mode) { - DLOG(ERROR) << "Descriptor access mode (" << mode - << ") differs from expected (" << expected_mode << ")"; - return false; - } - - return true; -} -#endif // !defined(OS_NACL) - -} // namespace - -ScopedFDPair::ScopedFDPair() = default; - -ScopedFDPair::ScopedFDPair(ScopedFDPair&&) = default; - -ScopedFDPair& ScopedFDPair::operator=(ScopedFDPair&&) = default; - -ScopedFDPair::~ScopedFDPair() = default; - -ScopedFDPair::ScopedFDPair(ScopedFD in_fd, ScopedFD in_readonly_fd) - : fd(std::move(in_fd)), readonly_fd(std::move(in_readonly_fd)) {} - -FDPair ScopedFDPair::get() const { - return {fd.get(), readonly_fd.get()}; -} - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take( - ScopedFDPair handle, - Mode mode, - size_t size, - const UnguessableToken& guid) { - if (!handle.fd.is_valid()) - return {}; - - if (size == 0) - return {}; - - if (size > static_cast(std::numeric_limits::max())) - return {}; - - CHECK( - CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size)); - - switch (mode) { - case Mode::kReadOnly: - case Mode::kUnsafe: - if (handle.readonly_fd.is_valid()) { - handle.readonly_fd.reset(); - DLOG(WARNING) << "Readonly handle shouldn't be valid for a " - "non-writable memory region; closing"; - } - break; - case Mode::kWritable: - if (!handle.readonly_fd.is_valid()) { - DLOG(ERROR) - << "Readonly handle must be valid for writable memory region"; - return {}; - } - break; - default: - DLOG(ERROR) << "Invalid permission mode: " << static_cast(mode); - return {}; - } - - return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid); -} - -FDPair PlatformSharedMemoryRegion::GetPlatformHandle() const { - return handle_.get(); -} - -bool PlatformSharedMemoryRegion::IsValid() const { - return handle_.fd.is_valid() && - (mode_ == Mode::kWritable ? handle_.readonly_fd.is_valid() : true); -} - -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const { - if (!IsValid()) - return {}; - - CHECK_NE(mode_, Mode::kWritable) - << "Duplicating a writable shared memory region is prohibited"; - - ScopedFD duped_fd(HANDLE_EINTR(dup(handle_.fd.get()))); - if (!duped_fd.is_valid()) { - DPLOG(ERROR) << "dup(" << handle_.fd.get() << ") failed"; - return {}; - } - - return PlatformSharedMemoryRegion({std::move(duped_fd), ScopedFD()}, mode_, - size_, guid_); -} - -bool PlatformSharedMemoryRegion::ConvertToReadOnly() { - if (!IsValid()) - return false; - - CHECK_EQ(mode_, Mode::kWritable) - << "Only writable shared memory region can be converted to read-only"; - - handle_.fd.reset(handle_.readonly_fd.release()); - mode_ = Mode::kReadOnly; - return true; -} - -bool PlatformSharedMemoryRegion::ConvertToUnsafe() { - if (!IsValid()) - return false; - - CHECK_EQ(mode_, Mode::kWritable) - << "Only writable shared memory region can be converted to unsafe"; - - handle_.readonly_fd.reset(); - mode_ = Mode::kUnsafe; - return true; -} - -bool PlatformSharedMemoryRegion::MapAt(off_t offset, - size_t size, - void** memory, - size_t* mapped_size) const { - if (!IsValid()) - return false; - - size_t end_byte; - if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) { - return false; - } - - bool write_allowed = mode_ != Mode::kReadOnly; - *memory = mmap(nullptr, size, PROT_READ | (write_allowed ? PROT_WRITE : 0), - MAP_SHARED, handle_.fd.get(), offset); - - bool mmap_succeeded = *memory && *memory != MAP_FAILED; - if (!mmap_succeeded) { - DPLOG(ERROR) << "mmap " << handle_.fd.get() << " failed"; - return false; - } - - *mapped_size = size; - DCHECK_EQ(0U, - reinterpret_cast(*memory) & (kMapMinimumAlignment - 1)); - return true; -} - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode, - size_t size) { -#if defined(OS_NACL) - // Untrusted code can't create descriptors or handles. - return {}; -#else - if (size == 0) - return {}; - - if (size > static_cast(std::numeric_limits::max())) - return {}; - - CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will " - "lead to this region being non-modifiable"; - - // This function theoretically can block on the disk, but realistically - // the temporary files we create will just go into the buffer cache - // and be deleted before they ever make it out to disk. - ThreadRestrictions::ScopedAllowIO allow_io; - - // We don't use shm_open() API in order to support the --disable-dev-shm-usage - // flag. - FilePath directory; - if (!GetShmemTempDir(false /* executable */, &directory)) - return {}; - - ScopedFD fd; - FilePath path; - fd.reset(CreateAndOpenFdForTemporaryFileInDir(directory, &path)); - - if (!fd.is_valid()) { - PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; - FilePath dir = path.DirName(); - if (access(dir.value().c_str(), W_OK | X_OK) < 0) { - PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); - if (dir.value() == "/dev/shm") { - LOG(FATAL) << "This is frequently caused by incorrect permissions on " - << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; - } - } - return {}; - } - - // Deleting the file prevents anyone else from mapping it in (making it - // private), and prevents the need for cleanup (once the last fd is - // closed, it is truly freed). - ScopedPathUnlinker path_unlinker(&path); - - ScopedFD readonly_fd; - if (mode == Mode::kWritable) { - // Also open as readonly so that we can ConvertToReadOnly(). - 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"; - return {}; - } - } - - // Get current size. - struct stat stat = {}; - if (fstat(fd.get(), &stat) != 0) - return {}; - const size_t current_size = stat.st_size; - if (current_size != size) { - if (HANDLE_EINTR(ftruncate(fd.get(), size)) != 0) - return {}; - } - - if (readonly_fd.is_valid()) { - struct stat readonly_stat = {}; - if (fstat(readonly_fd.get(), &readonly_stat)) - NOTREACHED(); - - if (stat.st_dev != readonly_stat.st_dev || - stat.st_ino != readonly_stat.st_ino) { - LOG(ERROR) << "Writable and read-only inodes don't match; bailing"; - return {}; - } - } - - return PlatformSharedMemoryRegion({std::move(fd), std::move(readonly_fd)}, - mode, size, UnguessableToken::Create()); -#endif // !defined(OS_NACL) -} - -bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode( - PlatformHandle handle, - Mode mode, - size_t size) { -#if !defined(OS_NACL) - if (!CheckFDAccessMode(handle.fd, - mode == Mode::kReadOnly ? O_RDONLY : O_RDWR)) { - return false; - } - - if (mode == Mode::kWritable) - return CheckFDAccessMode(handle.readonly_fd, O_RDONLY); - - // The second descriptor must be invalid in kReadOnly and kUnsafe modes. - if (handle.readonly_fd != -1) { - DLOG(ERROR) << "The second descriptor must be invalid"; - return false; - } - - return true; -#else - // fcntl(_, F_GETFL) is not implemented on NaCl. - void* temp_memory = nullptr; - temp_memory = - mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, handle.fd, 0); - - bool mmap_succeeded = temp_memory && temp_memory != MAP_FAILED; - if (mmap_succeeded) - munmap(temp_memory, size); - - bool is_read_only = !mmap_succeeded; - bool expected_read_only = mode == Mode::kReadOnly; - - if (is_read_only != expected_read_only) { - DLOG(ERROR) << "Descriptor has a wrong access mode: it is" - << (is_read_only ? " " : " not ") << "read-only but it should" - << (expected_read_only ? " " : " not ") << "be"; - return false; - } - - return true; -#endif // !defined(OS_NACL) -} - -PlatformSharedMemoryRegion::PlatformSharedMemoryRegion( - ScopedFDPair handle, - Mode mode, - size_t size, - const UnguessableToken& guid) - : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {} - -} // namespace subtle -} // namespace base diff --git a/memory/platform_shared_memory_region_unittest.cc b/memory/platform_shared_memory_region_unittest.cc deleted file mode 100644 index d4e96473e..000000000 --- a/memory/platform_shared_memory_region_unittest.cc +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2018 The Chromium 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/platform_shared_memory_region.h" - -#include "base/memory/shared_memory_mapping.h" -#include "base/process/process_metrics.h" -#include "base/sys_info.h" -#include "base/test/gtest_util.h" -#include "base/test/test_shared_memory_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include -#endif - -namespace base { -namespace subtle { - -const size_t kRegionSize = 1024; - -class PlatformSharedMemoryRegionTest : public ::testing::Test {}; - -// Tests that a default constructed region is invalid and produces invalid -// mappings. -TEST_F(PlatformSharedMemoryRegionTest, DefaultConstructedRegionIsInvalid) { - PlatformSharedMemoryRegion region; - EXPECT_FALSE(region.IsValid()); - WritableSharedMemoryMapping mapping = MapForTesting(®ion); - EXPECT_FALSE(mapping.IsValid()); - PlatformSharedMemoryRegion duplicate = region.Duplicate(); - EXPECT_FALSE(duplicate.IsValid()); - EXPECT_FALSE(region.ConvertToReadOnly()); -} - -// Tests that creating a region of 0 size returns an invalid region. -TEST_F(PlatformSharedMemoryRegionTest, CreateRegionOfZeroSizeIsInvalid) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(0); - EXPECT_FALSE(region.IsValid()); - - PlatformSharedMemoryRegion region2 = - PlatformSharedMemoryRegion::CreateUnsafe(0); - EXPECT_FALSE(region2.IsValid()); -} - -// Tests that creating a region of size bigger than the integer max value -// returns an invalid region. -TEST_F(PlatformSharedMemoryRegionTest, CreateTooLargeRegionIsInvalid) { - size_t too_large_region_size = - static_cast(std::numeric_limits::max()) + 1; - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(too_large_region_size); - EXPECT_FALSE(region.IsValid()); - - PlatformSharedMemoryRegion region2 = - PlatformSharedMemoryRegion::CreateUnsafe(too_large_region_size); - EXPECT_FALSE(region2.IsValid()); -} - -// Tests that regions consistently report their size as the size requested at -// creation time even if their allocation size is larger due to platform -// constraints. -TEST_F(PlatformSharedMemoryRegionTest, ReportedSizeIsRequestedSize) { - constexpr size_t kTestSizes[] = {1, 2, 3, 64, 4096, 1024 * 1024}; - for (size_t size : kTestSizes) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(size); - EXPECT_EQ(region.GetSize(), size); - - region.ConvertToReadOnly(); - EXPECT_EQ(region.GetSize(), size); - } -} - -// Tests that a writable region can be converted to read-only. -TEST_F(PlatformSharedMemoryRegionTest, ConvertWritableToReadOnly) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - EXPECT_EQ(region.GetMode(), PlatformSharedMemoryRegion::Mode::kWritable); - ASSERT_TRUE(region.ConvertToReadOnly()); - EXPECT_EQ(region.GetMode(), PlatformSharedMemoryRegion::Mode::kReadOnly); -} - -// Tests that a writable region can be converted to unsafe. -TEST_F(PlatformSharedMemoryRegionTest, ConvertWritableToUnsafe) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - EXPECT_EQ(region.GetMode(), PlatformSharedMemoryRegion::Mode::kWritable); - ASSERT_TRUE(region.ConvertToUnsafe()); - EXPECT_EQ(region.GetMode(), PlatformSharedMemoryRegion::Mode::kUnsafe); -} - -// Tests that the platform-specific handle converted to read-only cannot be used -// to perform a writable mapping with low-level system APIs like mmap(). -TEST_F(PlatformSharedMemoryRegionTest, ReadOnlyHandleIsNotWritable) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - EXPECT_TRUE(region.ConvertToReadOnly()); - EXPECT_EQ(region.GetMode(), PlatformSharedMemoryRegion::Mode::kReadOnly); - EXPECT_TRUE( - CheckReadOnlyPlatformSharedMemoryRegionForTesting(std::move(region))); -} - -// Tests that the PassPlatformHandle() call invalidates the region. -TEST_F(PlatformSharedMemoryRegionTest, InvalidAfterPass) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - ignore_result(region.PassPlatformHandle()); - EXPECT_FALSE(region.IsValid()); -} - -// Tests that the region is invalid after move. -TEST_F(PlatformSharedMemoryRegionTest, InvalidAfterMove) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - PlatformSharedMemoryRegion moved_region = std::move(region); - EXPECT_FALSE(region.IsValid()); - EXPECT_TRUE(moved_region.IsValid()); -} - -// Tests that calling Take() with the size parameter equal to zero returns an -// invalid region. -TEST_F(PlatformSharedMemoryRegionTest, TakeRegionOfZeroSizeIsInvalid) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - PlatformSharedMemoryRegion region2 = PlatformSharedMemoryRegion::Take( - region.PassPlatformHandle(), region.GetMode(), 0, region.GetGUID()); - EXPECT_FALSE(region2.IsValid()); -} - -// Tests that calling Take() with the size parameter bigger than the integer max -// value returns an invalid region. -TEST_F(PlatformSharedMemoryRegionTest, TakeTooLargeRegionIsInvalid) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - PlatformSharedMemoryRegion region2 = PlatformSharedMemoryRegion::Take( - region.PassPlatformHandle(), region.GetMode(), - static_cast(std::numeric_limits::max()) + 1, - region.GetGUID()); - EXPECT_FALSE(region2.IsValid()); -} - -// Tests that mapping bytes out of the region limits fails. -TEST_F(PlatformSharedMemoryRegionTest, MapAtOutOfTheRegionLimitsTest) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - WritableSharedMemoryMapping mapping = - MapAtForTesting(®ion, 0, region.GetSize() + 1); - EXPECT_FALSE(mapping.IsValid()); -} - -// Tests that mapping with a size and offset causing overflow fails. -TEST_F(PlatformSharedMemoryRegionTest, MapAtWithOverflowTest) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable( - SysInfo::VMAllocationGranularity() * 2); - ASSERT_TRUE(region.IsValid()); - size_t size = std::numeric_limits::max(); - size_t offset = SysInfo::VMAllocationGranularity(); - // |size| + |offset| should be below the region size due to overflow but - // mapping a region with these parameters should be invalid. - EXPECT_LT(size + offset, region.GetSize()); - WritableSharedMemoryMapping mapping = MapAtForTesting(®ion, offset, size); - EXPECT_FALSE(mapping.IsValid()); -} - -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) && \ - !defined(OS_MACOSX) -// Tests that the second handle is closed after a conversion to read-only on -// POSIX. -TEST_F(PlatformSharedMemoryRegionTest, - ConvertToReadOnlyInvalidatesSecondHandle) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - ASSERT_TRUE(region.ConvertToReadOnly()); - FDPair fds = region.GetPlatformHandle(); - EXPECT_LT(fds.readonly_fd, 0); -} - -// Tests that the second handle is closed after a conversion to unsafe on -// POSIX. -TEST_F(PlatformSharedMemoryRegionTest, ConvertToUnsafeInvalidatesSecondHandle) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - ASSERT_TRUE(region.ConvertToUnsafe()); - FDPair fds = region.GetPlatformHandle(); - EXPECT_LT(fds.readonly_fd, 0); -} -#endif - -#if defined(OS_MACOSX) && !defined(OS_IOS) -// Tests that protection bits are set correctly for read-only region on MacOS. -TEST_F(PlatformSharedMemoryRegionTest, MapCurrentAndMaxProtectionSetCorrectly) { - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - ASSERT_TRUE(region.ConvertToReadOnly()); - WritableSharedMemoryMapping ro_mapping = MapForTesting(®ion); - ASSERT_TRUE(ro_mapping.IsValid()); - - vm_region_basic_info_64 basic_info; - mach_vm_size_t dummy_size = 0; - void* temp_addr = ro_mapping.memory(); - MachVMRegionResult result = GetBasicInfo( - mach_task_self(), &dummy_size, - reinterpret_cast(&temp_addr), &basic_info); - EXPECT_EQ(result, MachVMRegionResult::Success); - EXPECT_EQ(basic_info.protection & VM_PROT_ALL, VM_PROT_READ); - EXPECT_EQ(basic_info.max_protection & VM_PROT_ALL, VM_PROT_READ); -} -#endif - -// Tests that platform handle permissions are checked correctly. -TEST_F(PlatformSharedMemoryRegionTest, - CheckPlatformHandlePermissionsCorrespondToMode) { - using Mode = PlatformSharedMemoryRegion::Mode; - auto check = [](const PlatformSharedMemoryRegion& region, - PlatformSharedMemoryRegion::Mode mode) { - return PlatformSharedMemoryRegion:: - CheckPlatformHandlePermissionsCorrespondToMode( - region.GetPlatformHandle(), mode, region.GetSize()); - }; - - // Check kWritable region. - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - EXPECT_TRUE(check(region, Mode::kWritable)); - EXPECT_FALSE(check(region, Mode::kReadOnly)); - - // Check kReadOnly region. - ASSERT_TRUE(region.ConvertToReadOnly()); - EXPECT_TRUE(check(region, Mode::kReadOnly)); - EXPECT_FALSE(check(region, Mode::kWritable)); - EXPECT_FALSE(check(region, Mode::kUnsafe)); - - // Check kUnsafe region. - PlatformSharedMemoryRegion region2 = - PlatformSharedMemoryRegion::CreateUnsafe(kRegionSize); - ASSERT_TRUE(region2.IsValid()); - EXPECT_TRUE(check(region2, Mode::kUnsafe)); - EXPECT_FALSE(check(region2, Mode::kReadOnly)); -} - -// Tests that it's impossible to create read-only platform shared memory region. -TEST_F(PlatformSharedMemoryRegionTest, CreateReadOnlyRegionDeathTest) { -#ifdef OFFICIAL_BUILD - // The official build does not print the reason a CHECK failed. - const char kErrorRegex[] = ""; -#else - const char kErrorRegex[] = - "Creating a region in read-only mode will lead to this region being " - "non-modifiable"; -#endif - EXPECT_DEATH_IF_SUPPORTED( - PlatformSharedMemoryRegion::Create( - PlatformSharedMemoryRegion::Mode::kReadOnly, kRegionSize), - kErrorRegex); -} - -// Tests that it's prohibited to duplicate a writable region. -TEST_F(PlatformSharedMemoryRegionTest, DuplicateWritableRegionDeathTest) { -#ifdef OFFICIAL_BUILD - const char kErrorRegex[] = ""; -#else - const char kErrorRegex[] = - "Duplicating a writable shared memory region is prohibited"; -#endif - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - EXPECT_DEATH_IF_SUPPORTED(region.Duplicate(), kErrorRegex); -} - -// Tests that it's prohibited to convert an unsafe region to read-only. -TEST_F(PlatformSharedMemoryRegionTest, UnsafeRegionConvertToReadOnlyDeathTest) { -#ifdef OFFICIAL_BUILD - const char kErrorRegex[] = ""; -#else - const char kErrorRegex[] = - "Only writable shared memory region can be converted to read-only"; -#endif - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateUnsafe(kRegionSize); - ASSERT_TRUE(region.IsValid()); - EXPECT_DEATH_IF_SUPPORTED(region.ConvertToReadOnly(), kErrorRegex); -} - -// Tests that it's prohibited to convert a read-only region to read-only. -TEST_F(PlatformSharedMemoryRegionTest, - ReadOnlyRegionConvertToReadOnlyDeathTest) { -#ifdef OFFICIAL_BUILD - const char kErrorRegex[] = ""; -#else - const char kErrorRegex[] = - "Only writable shared memory region can be converted to read-only"; -#endif - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - EXPECT_TRUE(region.ConvertToReadOnly()); - EXPECT_DEATH_IF_SUPPORTED(region.ConvertToReadOnly(), kErrorRegex); -} - -// Tests that it's prohibited to convert a read-only region to unsafe. -TEST_F(PlatformSharedMemoryRegionTest, ReadOnlyRegionConvertToUnsafeDeathTest) { -#ifdef OFFICIAL_BUILD - const char kErrorRegex[] = ""; -#else - const char kErrorRegex[] = - "Only writable shared memory region can be converted to unsafe"; -#endif - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateWritable(kRegionSize); - ASSERT_TRUE(region.IsValid()); - ASSERT_TRUE(region.ConvertToReadOnly()); - EXPECT_DEATH_IF_SUPPORTED(region.ConvertToUnsafe(), kErrorRegex); -} - -// Tests that it's prohibited to convert an unsafe region to unsafe. -TEST_F(PlatformSharedMemoryRegionTest, UnsafeRegionConvertToUnsafeDeathTest) { -#ifdef OFFICIAL_BUILD - const char kErrorRegex[] = ""; -#else - const char kErrorRegex[] = - "Only writable shared memory region can be converted to unsafe"; -#endif - PlatformSharedMemoryRegion region = - PlatformSharedMemoryRegion::CreateUnsafe(kRegionSize); - ASSERT_TRUE(region.IsValid()); - EXPECT_DEATH_IF_SUPPORTED(region.ConvertToUnsafe(), kErrorRegex); -} - -} // namespace subtle -} // namespace base diff --git a/memory/platform_shared_memory_region_win.cc b/memory/platform_shared_memory_region_win.cc deleted file mode 100644 index 034664154..000000000 --- a/memory/platform_shared_memory_region_win.cc +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright 2018 The Chromium 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/platform_shared_memory_region.h" - -#include -#include -#include - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/bits.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" -#include "base/numerics/checked_math.h" -#include "base/process/process_handle.h" -#include "base/rand_util.h" -#include "base/strings/stringprintf.h" - -namespace base { -namespace subtle { - -namespace { - -// Errors that can occur during Shared Memory construction. -// These match tools/metrics/histograms/histograms.xml. -// This enum is append-only. -enum CreateError { - SUCCESS = 0, - SIZE_ZERO = 1, - SIZE_TOO_LARGE = 2, - INITIALIZE_ACL_FAILURE = 3, - INITIALIZE_SECURITY_DESC_FAILURE = 4, - SET_SECURITY_DESC_FAILURE = 5, - CREATE_FILE_MAPPING_FAILURE = 6, - REDUCE_PERMISSIONS_FAILURE = 7, - ALREADY_EXISTS = 8, - CREATE_ERROR_LAST = ALREADY_EXISTS -}; - -// Emits UMA metrics about encountered errors. Pass zero (0) for |winerror| -// if there is no associated Windows error. -void LogError(CreateError error, DWORD winerror) { - UMA_HISTOGRAM_ENUMERATION("SharedMemory.CreateError", error, - CREATE_ERROR_LAST + 1); - static_assert(ERROR_SUCCESS == 0, "Windows error code changed!"); - if (winerror != ERROR_SUCCESS) - UmaHistogramSparse("SharedMemory.CreateWinError", winerror); -} - -typedef enum _SECTION_INFORMATION_CLASS { - SectionBasicInformation, -} SECTION_INFORMATION_CLASS; - -typedef struct _SECTION_BASIC_INFORMATION { - PVOID BaseAddress; - ULONG Attributes; - LARGE_INTEGER Size; -} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; - -typedef ULONG(__stdcall* NtQuerySectionType)( - HANDLE SectionHandle, - SECTION_INFORMATION_CLASS SectionInformationClass, - PVOID SectionInformation, - ULONG SectionInformationLength, - PULONG ResultLength); - -// Returns the length of the memory section starting at the supplied address. -size_t GetMemorySectionSize(void* address) { - MEMORY_BASIC_INFORMATION memory_info; - if (!::VirtualQuery(address, &memory_info, sizeof(memory_info))) - return 0; - return memory_info.RegionSize - - (static_cast(address) - - static_cast(memory_info.AllocationBase)); -} - -// Checks if the section object is safe to map. At the moment this just means -// it's not an image section. -bool IsSectionSafeToMap(HANDLE handle) { - static NtQuerySectionType nt_query_section_func = - reinterpret_cast( - ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection")); - DCHECK(nt_query_section_func); - - // The handle must have SECTION_QUERY access for this to succeed. - SECTION_BASIC_INFORMATION basic_information = {}; - ULONG status = - nt_query_section_func(handle, SectionBasicInformation, &basic_information, - sizeof(basic_information), nullptr); - if (status) - return false; - return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE; -} - -// Returns a HANDLE on success and |nullptr| on failure. -// This function is similar to CreateFileMapping, but removes the permissions -// WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE. -// -// A newly created file mapping has two sets of permissions. It has access -// control permissions (WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE) and -// file permissions (FILE_MAP_READ, FILE_MAP_WRITE, etc.). The Chrome sandbox -// prevents HANDLEs with the WRITE_DAC permission from being duplicated into -// unprivileged processes. -// -// In order to remove the access control permissions, after being created the -// handle is duplicated with only the file access permissions. -HANDLE CreateFileMappingWithReducedPermissions(SECURITY_ATTRIBUTES* sa, - size_t rounded_size, - LPCWSTR name) { - HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, sa, PAGE_READWRITE, 0, - static_cast(rounded_size), name); - if (!h) { - LogError(CREATE_FILE_MAPPING_FAILURE, GetLastError()); - return nullptr; - } - - HANDLE h2; - ProcessHandle process = GetCurrentProcess(); - BOOL success = ::DuplicateHandle( - process, h, process, &h2, FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY, - FALSE, 0); - BOOL rv = ::CloseHandle(h); - DCHECK(rv); - - if (!success) { - LogError(REDUCE_PERMISSIONS_FAILURE, GetLastError()); - return nullptr; - } - - return h2; -} - -} // namespace - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take( - win::ScopedHandle handle, - Mode mode, - size_t size, - const UnguessableToken& guid) { - if (!handle.IsValid()) - return {}; - - if (size == 0) - return {}; - - if (size > static_cast(std::numeric_limits::max())) - return {}; - - if (!IsSectionSafeToMap(handle.Get())) - return {}; - - CHECK( - CheckPlatformHandlePermissionsCorrespondToMode(handle.Get(), mode, size)); - - return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid); -} - -HANDLE PlatformSharedMemoryRegion::GetPlatformHandle() const { - return handle_.Get(); -} - -bool PlatformSharedMemoryRegion::IsValid() const { - return handle_.IsValid(); -} - -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const { - if (!IsValid()) - return {}; - - CHECK_NE(mode_, Mode::kWritable) - << "Duplicating a writable shared memory region is prohibited"; - - HANDLE duped_handle; - ProcessHandle process = GetCurrentProcess(); - BOOL success = - ::DuplicateHandle(process, handle_.Get(), process, &duped_handle, 0, - FALSE, DUPLICATE_SAME_ACCESS); - if (!success) - return {}; - - return PlatformSharedMemoryRegion(win::ScopedHandle(duped_handle), mode_, - size_, guid_); -} - -bool PlatformSharedMemoryRegion::ConvertToReadOnly() { - if (!IsValid()) - return false; - - CHECK_EQ(mode_, Mode::kWritable) - << "Only writable shared memory region can be converted to read-only"; - - win::ScopedHandle handle_copy(handle_.Take()); - - HANDLE duped_handle; - ProcessHandle process = GetCurrentProcess(); - BOOL success = - ::DuplicateHandle(process, handle_copy.Get(), process, &duped_handle, - FILE_MAP_READ | SECTION_QUERY, FALSE, 0); - if (!success) - return false; - - handle_.Set(duped_handle); - mode_ = Mode::kReadOnly; - return true; -} - -bool PlatformSharedMemoryRegion::ConvertToUnsafe() { - if (!IsValid()) - return false; - - CHECK_EQ(mode_, Mode::kWritable) - << "Only writable shared memory region can be converted to unsafe"; - - mode_ = Mode::kUnsafe; - return true; -} - -bool PlatformSharedMemoryRegion::MapAt(off_t offset, - size_t size, - void** memory, - size_t* mapped_size) const { - if (!IsValid()) - return false; - - size_t end_byte; - if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) { - return false; - } - - bool write_allowed = mode_ != Mode::kReadOnly; - // Try to map the shared memory. On the first failure, release any reserved - // address space for a single entry. - for (int i = 0; i < 2; ++i) { - *memory = MapViewOfFile( - handle_.Get(), FILE_MAP_READ | (write_allowed ? FILE_MAP_WRITE : 0), - static_cast(offset) >> 32, static_cast(offset), size); - if (*memory) - break; - ReleaseReservation(); - } - if (!*memory) { - DPLOG(ERROR) << "Failed executing MapViewOfFile"; - return false; - } - - *mapped_size = GetMemorySectionSize(*memory); - DCHECK_EQ(0U, - reinterpret_cast(*memory) & (kMapMinimumAlignment - 1)); - return true; -} - -// static -PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode, - size_t size) { - // TODO(crbug.com/210609): NaCl forces us to round up 64k here, wasting 32k - // per mapping on average. - static const size_t kSectionSize = 65536; - if (size == 0) { - LogError(SIZE_ZERO, 0); - return {}; - } - - size_t rounded_size = bits::Align(size, kSectionSize); - if (rounded_size > static_cast(std::numeric_limits::max())) { - LogError(SIZE_TOO_LARGE, 0); - return {}; - } - - CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will " - "lead to this region being non-modifiable"; - - // Add an empty DACL to enforce anonymous read-only sections. - ACL dacl; - SECURITY_DESCRIPTOR sd; - if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) { - LogError(INITIALIZE_ACL_FAILURE, GetLastError()); - return {}; - } - if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { - LogError(INITIALIZE_SECURITY_DESC_FAILURE, GetLastError()); - return {}; - } - if (!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE)) { - LogError(SET_SECURITY_DESC_FAILURE, GetLastError()); - return {}; - } - - // Windows ignores DACLs on unnamed shared section. Generate a random name in - // order to be able to enforce read-only. - uint64_t rand_values[4]; - RandBytes(&rand_values, sizeof(rand_values)); - string16 name = - StringPrintf(L"CrSharedMem_%016llx%016llx%016llx%016llx", rand_values[0], - rand_values[1], rand_values[2], rand_values[3]); - - SECURITY_ATTRIBUTES sa = {sizeof(sa), &sd, FALSE}; - // Ask for the file mapping with reduced permisions to avoid passing the - // access control permissions granted by default into unpriviledged process. - HANDLE h = - CreateFileMappingWithReducedPermissions(&sa, rounded_size, name.c_str()); - if (h == nullptr) { - // The error is logged within CreateFileMappingWithReducedPermissions(). - return {}; - } - - win::ScopedHandle scoped_h(h); - // Check if the shared memory pre-exists. - if (GetLastError() == ERROR_ALREADY_EXISTS) { - LogError(ALREADY_EXISTS, ERROR_ALREADY_EXISTS); - return {}; - } - - return PlatformSharedMemoryRegion(std::move(scoped_h), mode, size, - UnguessableToken::Create()); -} - -// static -bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode( - PlatformHandle handle, - Mode mode, - size_t size) { - // Call ::DuplicateHandle() with FILE_MAP_WRITE as a desired access to check - // if the |handle| has a write access. - ProcessHandle process = GetCurrentProcess(); - HANDLE duped_handle; - BOOL success = ::DuplicateHandle(process, handle, process, &duped_handle, - FILE_MAP_WRITE, FALSE, 0); - if (success) { - BOOL rv = ::CloseHandle(duped_handle); - DCHECK(rv); - } - - bool is_read_only = !success; - bool expected_read_only = mode == Mode::kReadOnly; - - if (is_read_only != expected_read_only) { - DLOG(ERROR) << "File mapping handle has wrong access rights: it is" - << (is_read_only ? " " : " not ") << "read-only but it should" - << (expected_read_only ? " " : " not ") << "be"; - return false; - } - - return true; -} - -PlatformSharedMemoryRegion::PlatformSharedMemoryRegion( - win::ScopedHandle handle, - Mode mode, - size_t size, - const UnguessableToken& guid) - : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {} - -} // namespace subtle -} // namespace base diff --git a/memory/protected_memory.cc b/memory/protected_memory.cc deleted file mode 100644 index 157a677e0..000000000 --- a/memory/protected_memory.cc +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017 The Chromium 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/protected_memory.h" -#include "base/synchronization/lock.h" - -namespace base { - -#if !defined(COMPONENT_BUILD) -PROTECTED_MEMORY_SECTION int AutoWritableMemory::writers = 0; -#endif // !defined(COMPONENT_BUILD) - -base::LazyInstance::Leaky AutoWritableMemory::writers_lock = - LAZY_INSTANCE_INITIALIZER; - -} // namespace base diff --git a/memory/protected_memory.h b/memory/protected_memory.h deleted file mode 100644 index 3cb2ec3c8..000000000 --- a/memory/protected_memory.h +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Protected memory is memory holding security-sensitive data intended to be -// left read-only for the majority of its lifetime to avoid being overwritten -// by attackers. ProtectedMemory is a simple wrapper around platform-specific -// APIs to set memory read-write and read-only when required. Protected memory -// should be set read-write for the minimum amount of time required. - -// Normally mutable variables are held in read-write memory and constant data -// is held in read-only memory to ensure it is not accidentally overwritten. -// In some cases we want to hold mutable variables in read-only memory, except -// when they are being written to, to ensure that they are not tampered with. -// -// ProtectedMemory is a container class intended to hold a single variable in -// read-only memory, except when explicitly set read-write. The variable can be -// set read-write by creating a scoped AutoWritableMemory object by calling -// AutoWritableMemory::Create(), the memory stays writable until the returned -// object goes out of scope and is destructed. The wrapped variable can be -// accessed using operator* and operator->. -// -// Instances of ProtectedMemory must be declared in the PROTECTED_MEMORY_SECTION -// and as global variables. Because protected memory variables are globals, the -// the same rules apply disallowing non-trivial constructors and destructors. -// Global definitions are required to avoid the linker placing statics in -// inlinable functions into a comdat section and setting the protected memory -// section read-write when they are merged. -// -// EXAMPLE: -// -// struct Items { void* item1; }; -// static PROTECTED_MEMORY_SECTION base::ProtectedMemory items; -// void InitializeItems() { -// // Explicitly set items read-write before writing to it. -// auto writer = base::AutoWritableMemory::Create(items); -// items->item1 = /* ... */; -// assert(items->item1 != nullptr); -// // items is set back to read-only on the destruction of writer -// } -// -// using FnPtr = void (*)(void); -// PROTECTED_MEMORY_SECTION base::ProtectedMemory fnPtr; -// FnPtr ResolveFnPtr(void) { -// // The Initializer nested class is a helper class for creating a static -// // initializer for a ProtectedMemory variable. It implicitly sets the -// // variable read-write during initialization. -// static base::ProtectedMemory::Initializer I(&fnPtr, -// reinterpret_cast(dlsym(/* ... */))); -// return *fnPtr; -// } - -#ifndef BASE_MEMORY_PROTECTED_MEMORY_H_ -#define BASE_MEMORY_PROTECTED_MEMORY_H_ - -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/protected_memory_buildflags.h" -#include "base/synchronization/lock.h" -#include "build/build_config.h" - -#define PROTECTED_MEMORY_ENABLED 1 - -// Linking with lld is required to workaround crbug.com/792777. -// TODO(vtsyrklevich): Remove once support for gold on Android/CrOs is dropped -#if defined(OS_LINUX) && BUILDFLAG(USE_LLD) -// Define the section read-only -__asm__(".section protected_memory, \"a\"\n\t"); -#define PROTECTED_MEMORY_SECTION __attribute__((section("protected_memory"))) - -// Explicitly mark these variables hidden so the symbols are local to the -// currently built component. Otherwise they are created with global (external) -// linkage and component builds would break because a single pair of these -// symbols would override the rest. -__attribute__((visibility("hidden"))) extern char __start_protected_memory; -__attribute__((visibility("hidden"))) extern char __stop_protected_memory; - -#elif defined(OS_MACOSX) && !defined(OS_IOS) -// The segment the section is in is defined read-only with a linker flag in -// build/config/mac/BUILD.gn -#define PROTECTED_MEMORY_SECTION \ - __attribute__((section("PROTECTED_MEMORY, protected_memory"))) -extern char __start_protected_memory __asm( - "section$start$PROTECTED_MEMORY$protected_memory"); -extern char __stop_protected_memory __asm( - "section$end$PROTECTED_MEMORY$protected_memory"); - -#elif defined(OS_WIN) -// Define a read-write prot section. The $a, $mem, and $z 'sub-sections' are -// merged alphabetically so $a and $z are used to define the start and end of -// the protected memory section, and $mem holds protected variables. -// (Note: Sections in Portable Executables are equivalent to segments in other -// executable formats, so this section is mapped into its own pages.) -#pragma section("prot$a", read, write) -#pragma section("prot$mem", read, write) -#pragma section("prot$z", read, write) - -// We want the protected memory section to be read-only, not read-write so we -// instruct the linker to set the section read-only at link time. We do this -// at link time instead of compile time, because defining the prot section -// read-only would cause mis-compiles due to optimizations assuming that the -// section contents are constant. -#pragma comment(linker, "/SECTION:prot,R") - -__declspec(allocate("prot$a")) __declspec(selectany) -char __start_protected_memory; -__declspec(allocate("prot$z")) __declspec(selectany) -char __stop_protected_memory; - -#define PROTECTED_MEMORY_SECTION __declspec(allocate("prot$mem")) - -#else -#undef PROTECTED_MEMORY_ENABLED -#define PROTECTED_MEMORY_ENABLED 0 -#define PROTECTED_MEMORY_SECTION -#endif - -namespace base { - -template -class ProtectedMemory { - public: - ProtectedMemory() = default; - - // Expose direct access to the encapsulated variable - T& operator*() { return data; } - const T& operator*() const { return data; } - T* operator->() { return &data; } - const T* operator->() const { return &data; } - - // Helper class for creating simple ProtectedMemory static initializers. - class Initializer { - public: - // Defined out-of-line below to break circular definition dependency between - // ProtectedMemory and AutoWritableMemory. - Initializer(ProtectedMemory* PM, const T& Init); - - DISALLOW_IMPLICIT_CONSTRUCTORS(Initializer); - }; - - private: - T data; - - DISALLOW_COPY_AND_ASSIGN(ProtectedMemory); -}; - -// DCHECK that the byte at |ptr| is read-only. -BASE_EXPORT void AssertMemoryIsReadOnly(const void* ptr); - -// Abstract out platform-specific methods to get the beginning and end of the -// PROTECTED_MEMORY_SECTION. ProtectedMemoryEnd returns a pointer to the byte -// past the end of the PROTECTED_MEMORY_SECTION. -#if PROTECTED_MEMORY_ENABLED -constexpr void* ProtectedMemoryStart = &__start_protected_memory; -constexpr void* ProtectedMemoryEnd = &__stop_protected_memory; -#endif - -#if defined(COMPONENT_BUILD) -namespace internal { - -// For component builds we want to define a separate global writers variable -// (explained below) in every DSO that includes this header. To do that we use -// this template to define a global without duplicate symbol errors. -template -struct DsoSpecific { - static T value; -}; -template -T DsoSpecific::value = 0; - -} // namespace internal -#endif // defined(COMPONENT_BUILD) - -// A class that sets a given ProtectedMemory variable writable while the -// AutoWritableMemory is in scope. This class implements the logic for setting -// the protected memory region read-only/read-write in a thread-safe manner. -class AutoWritableMemory { - private: - // 'writers' is a global holding the number of ProtectedMemory instances set - // writable, used to avoid races setting protected memory readable/writable. - // When this reaches zero the protected memory region is set read only. - // Access is controlled by writers_lock. -#if defined(COMPONENT_BUILD) - // For component builds writers is a reference to an int defined separately in - // every DSO. - static constexpr int& writers = internal::DsoSpecific::value; -#else - // Otherwise, we declare writers in the protected memory section to avoid the - // scenario where an attacker could overwrite it with a large value and invoke - // code that constructs and destructs an AutoWritableMemory. After such a call - // protected memory would still be set writable because writers > 0. - static int writers; -#endif // defined(COMPONENT_BUILD) - - // Synchronizes access to the writers variable and the simultaneous actions - // that need to happen alongside writers changes, e.g. setting the protected - // memory region readable when writers is decremented to 0. - static BASE_EXPORT base::LazyInstance::Leaky writers_lock; - - // Abstract out platform-specific memory APIs. |end| points to the byte past - // the end of the region of memory having its memory protections changed. - BASE_EXPORT bool SetMemoryReadWrite(void* start, void* end); - BASE_EXPORT bool SetMemoryReadOnly(void* start, void* end); - - // If this is the first writer (e.g. writers == 0) set the writers variable - // read-write. Next, increment writers and set the requested memory writable. - AutoWritableMemory(void* ptr, void* ptr_end) { -#if PROTECTED_MEMORY_ENABLED - DCHECK(ptr >= ProtectedMemoryStart && ptr_end <= ProtectedMemoryEnd); - - { - base::AutoLock auto_lock(writers_lock.Get()); - if (writers == 0) { - AssertMemoryIsReadOnly(ptr); -#if !defined(COMPONENT_BUILD) - AssertMemoryIsReadOnly(&writers); - CHECK(SetMemoryReadWrite(&writers, &writers + 1)); -#endif // !defined(COMPONENT_BUILD) - } - - writers++; - } - - CHECK(SetMemoryReadWrite(ptr, ptr_end)); -#endif // PROTECTED_MEMORY_ENABLED - } - - public: - // Wrap the private constructor to create an easy-to-use interface to - // construct AutoWritableMemory objects. - template - static AutoWritableMemory Create(ProtectedMemory& PM) { - T* ptr = &*PM; - return AutoWritableMemory(ptr, ptr + 1); - } - - // Move constructor just increments writers - AutoWritableMemory(AutoWritableMemory&& original) { -#if PROTECTED_MEMORY_ENABLED - base::AutoLock auto_lock(writers_lock.Get()); - CHECK_GT(writers, 0); - writers++; -#endif // PROTECTED_MEMORY_ENABLED - } - - // On destruction decrement writers, and if no other writers exist, set the - // entire protected memory region read-only. - ~AutoWritableMemory() { -#if PROTECTED_MEMORY_ENABLED - base::AutoLock auto_lock(writers_lock.Get()); - CHECK_GT(writers, 0); - writers--; - - if (writers == 0) { - CHECK(SetMemoryReadOnly(ProtectedMemoryStart, ProtectedMemoryEnd)); -#if !defined(COMPONENT_BUILD) - AssertMemoryIsReadOnly(&writers); -#endif // !defined(COMPONENT_BUILD) - } -#endif // PROTECTED_MEMORY_ENABLED - } - - DISALLOW_IMPLICIT_CONSTRUCTORS(AutoWritableMemory); -}; - -template -ProtectedMemory::Initializer::Initializer(ProtectedMemory* PM, - const T& Init) { - AutoWritableMemory writer = AutoWritableMemory::Create(*PM); - **PM = Init; -} - -} // namespace base - -#endif // BASE_MEMORY_PROTECTED_MEMORY_H_ diff --git a/memory/protected_memory_cfi.h b/memory/protected_memory_cfi.h deleted file mode 100644 index a90023bc8..000000000 --- a/memory/protected_memory_cfi.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Helper routines to call function pointers stored in protected memory with -// Control Flow Integrity indirect call checking disabled. Some indirect calls, -// e.g. dynamically resolved symbols in another DSO, can not be accounted for by -// CFI-icall. These routines allow those symbols to be called without CFI-icall -// checking safely by ensuring that they are placed in protected memory. - -#ifndef BASE_MEMORY_PROTECTED_MEMORY_CFI_H_ -#define BASE_MEMORY_PROTECTED_MEMORY_CFI_H_ - -#include - -#include "base/cfi_buildflags.h" -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/protected_memory.h" -#include "build/build_config.h" - -#if BUILDFLAG(CFI_ICALL_CHECK) && !PROTECTED_MEMORY_ENABLED -#error "CFI-icall enabled for platform without protected memory support" -#endif // BUILDFLAG(CFI_ICALL_CHECK) && !PROTECTED_MEMORY_ENABLED - -namespace base { -namespace internal { - -// This class is used to exempt calls to function pointers stored in -// ProtectedMemory from cfi-icall checking. It's not secure to use directly, it -// should only be used by the UnsanitizedCfiCall() functions below. Given an -// UnsanitizedCfiCall object, you can use operator() to call the encapsulated -// function pointer without cfi-icall checking. -template -class UnsanitizedCfiCall { - public: - explicit UnsanitizedCfiCall(FunctionType function) : function_(function) {} - UnsanitizedCfiCall(UnsanitizedCfiCall&&) = default; - - template - NO_SANITIZE("cfi-icall") - auto operator()(Args&&... args) { - return function_(std::forward(args)...); - } - - private: - FunctionType function_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(UnsanitizedCfiCall); -}; - -} // namespace internal - -// These functions can be used to call function pointers in ProtectedMemory -// without cfi-icall checking. They are intended to be used to create an -// UnsanitizedCfiCall object and immediately call it. UnsanitizedCfiCall objects -// should not initialized directly or stored because they hold a function -// pointer that will be called without CFI-icall checking in mutable memory. The -// functions can be used as shown below: - -// ProtectedMemory p; -// UnsanitizedCfiCall(p)(5); /* In place of (*p)(5); */ - -template -auto UnsanitizedCfiCall(const ProtectedMemory& PM) { -#if PROTECTED_MEMORY_ENABLED - DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd); -#endif // PROTECTED_MEMORY_ENABLED - return internal::UnsanitizedCfiCall(*PM); -} - -// struct S { void (*fp)(int); } s; -// ProtectedMemory p; -// UnsanitizedCfiCall(p, &S::fp)(5); /* In place of p->fp(5); */ - -template -auto UnsanitizedCfiCall(const ProtectedMemory& PM, Member member) { -#if PROTECTED_MEMORY_ENABLED - DCHECK(&PM >= ProtectedMemoryStart && &PM < ProtectedMemoryEnd); -#endif // PROTECTED_MEMORY_ENABLED - return internal::UnsanitizedCfiCall(*PM.*member); -} - -} // namespace base - -#endif // BASE_MEMORY_PROTECTED_MEMORY_CFI_H_ diff --git a/memory/protected_memory_posix.cc b/memory/protected_memory_posix.cc deleted file mode 100644 index d003d79bd..000000000 --- a/memory/protected_memory_posix.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2017 The Chromium 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/protected_memory.h" - -#include -#include -#include - -#if defined(OS_LINUX) -#include -#endif // defined(OS_LINUX) - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include -#include -#endif // defined(OS_MACOSX) && !defined(OS_IOS) - -#include "base/posix/eintr_wrapper.h" -#include "base/process/process_metrics.h" -#include "base/synchronization/lock.h" -#include "build/build_config.h" - -namespace base { - -namespace { - -bool SetMemory(void* start, void* end, int prot) { - DCHECK(end > start); - const uintptr_t page_mask = ~(base::GetPageSize() - 1); - const uintptr_t page_start = reinterpret_cast(start) & page_mask; - return mprotect(reinterpret_cast(page_start), - reinterpret_cast(end) - page_start, prot) == 0; -} - -} // namespace - -bool AutoWritableMemory::SetMemoryReadWrite(void* start, void* end) { - return SetMemory(start, end, PROT_READ | PROT_WRITE); -} - -bool AutoWritableMemory::SetMemoryReadOnly(void* start, void* end) { - return SetMemory(start, end, PROT_READ); -} - -#if defined(OS_LINUX) -void AssertMemoryIsReadOnly(const void* ptr) { -#if DCHECK_IS_ON() - const uintptr_t page_mask = ~(base::GetPageSize() - 1); - const uintptr_t page_start = reinterpret_cast(ptr) & page_mask; - - // Note: We've casted away const here, which should not be meaningful since - // if the memory is written to we will abort immediately. - int result = - getrlimit(RLIMIT_NPROC, reinterpret_cast(page_start)); - DCHECK_EQ(result, -1); - DCHECK_EQ(errno, EFAULT); -#endif // DCHECK_IS_ON() -} -#elif defined(OS_MACOSX) && !defined(OS_IOS) -void AssertMemoryIsReadOnly(const void* ptr) { -#if DCHECK_IS_ON() - mach_port_t object_name; - vm_region_basic_info_64 region_info; - mach_vm_size_t size = 1; - mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; - - kern_return_t kr = mach_vm_region( - mach_task_self(), reinterpret_cast(&ptr), &size, - VM_REGION_BASIC_INFO_64, reinterpret_cast(®ion_info), - &count, &object_name); - DCHECK_EQ(kr, KERN_SUCCESS); - DCHECK_EQ(region_info.protection, VM_PROT_READ); -#endif // DCHECK_IS_ON() -} -#endif // defined(OS_LINUX) || (defined(OS_MACOSX) && !defined(OS_IOS)) - -} // namespace base diff --git a/memory/protected_memory_unittest.cc b/memory/protected_memory_unittest.cc deleted file mode 100644 index b7daed34f..000000000 --- a/memory/protected_memory_unittest.cc +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2017 The Chromium 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/protected_memory.h" -#include "base/cfi_buildflags.h" -#include "base/memory/protected_memory_cfi.h" -#include "base/synchronization/lock.h" -#include "base/test/gtest_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -struct Data { - Data() = default; - Data(int foo_) : foo(foo_) {} - int foo; -}; - -} // namespace - -class ProtectedMemoryTest : public ::testing::Test { - protected: - // Run tests one at a time. Some of the negative tests can not be made thread - // safe. - void SetUp() final { lock.Acquire(); } - void TearDown() final { lock.Release(); } - - Lock lock; -}; - -PROTECTED_MEMORY_SECTION ProtectedMemory init; - -TEST_F(ProtectedMemoryTest, Initializer) { - static ProtectedMemory::Initializer I(&init, 4); - EXPECT_EQ(*init, 4); -} - -PROTECTED_MEMORY_SECTION ProtectedMemory data; - -TEST_F(ProtectedMemoryTest, Basic) { - AutoWritableMemory writer = AutoWritableMemory::Create(data); - data->foo = 5; - EXPECT_EQ(data->foo, 5); -} - -#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) - -#if PROTECTED_MEMORY_ENABLED -TEST_F(ProtectedMemoryTest, ReadOnlyOnStart) { - EXPECT_DEATH({ data->foo = 6; AutoWritableMemory::Create(data); }, ""); -} - -TEST_F(ProtectedMemoryTest, ReadOnlyAfterSetWritable) { - { AutoWritableMemory writer = AutoWritableMemory::Create(data); } - EXPECT_DEATH({ data->foo = 7; }, ""); -} - -TEST_F(ProtectedMemoryTest, AssertMemoryIsReadOnly) { - AssertMemoryIsReadOnly(&data->foo); - { AutoWritableMemory::Create(data); } - AssertMemoryIsReadOnly(&data->foo); - - ProtectedMemory writable_data; - EXPECT_DCHECK_DEATH({ AssertMemoryIsReadOnly(&writable_data->foo); }); -} - -TEST_F(ProtectedMemoryTest, FailsIfDefinedOutsideOfProtectMemoryRegion) { - ProtectedMemory data; - EXPECT_DCHECK_DEATH({ AutoWritableMemory::Create(data); }); -} - -TEST_F(ProtectedMemoryTest, UnsanitizedCfiCallOutsideOfProtectedMemoryRegion) { - ProtectedMemory data; - EXPECT_DCHECK_DEATH({ UnsanitizedCfiCall(data)(); }); -} -#endif // PROTECTED_MEMORY_ENABLED - -namespace { - -struct BadIcall { - BadIcall() = default; - BadIcall(int (*fp_)(int)) : fp(fp_) {} - int (*fp)(int); -}; - -unsigned int bad_icall(int i) { - return 4 + i; -} - -} // namespace - -PROTECTED_MEMORY_SECTION ProtectedMemory icall_pm1; - -TEST_F(ProtectedMemoryTest, BadMemberCall) { - static ProtectedMemory::Initializer I( - &icall_pm1, BadIcall(reinterpret_cast(&bad_icall))); - - EXPECT_EQ(UnsanitizedCfiCall(icall_pm1, &BadIcall::fp)(1), 5); -#if !BUILDFLAG(CFI_ICALL_CHECK) - EXPECT_EQ(icall_pm1->fp(1), 5); -#elif BUILDFLAG(CFI_ENFORCEMENT_TRAP) || BUILDFLAG(CFI_ENFORCEMENT_DIAGNOSTIC) - EXPECT_DEATH({ icall_pm1->fp(1); }, ""); -#endif -} - -PROTECTED_MEMORY_SECTION ProtectedMemory icall_pm2; - -TEST_F(ProtectedMemoryTest, BadFnPtrCall) { - static ProtectedMemory::Initializer I( - &icall_pm2, reinterpret_cast(&bad_icall)); - - EXPECT_EQ(UnsanitizedCfiCall(icall_pm2)(1), 5); -#if !BUILDFLAG(CFI_ICALL_CHECK) - EXPECT_EQ((*icall_pm2)(1), 5); -#elif BUILDFLAG(CFI_ENFORCEMENT_TRAP) || BUILDFLAG(CFI_ENFORCEMENT_DIAGNOSTIC) - EXPECT_DEATH({ (*icall_pm2)(1); }, ""); -#endif -} - -#endif // defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID) - -} // namespace base diff --git a/memory/protected_memory_win.cc b/memory/protected_memory_win.cc deleted file mode 100644 index cf3da78dd..000000000 --- a/memory/protected_memory_win.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017 The Chromium 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/protected_memory.h" - -#include - -#include - -#include "base/process/process_metrics.h" -#include "base/synchronization/lock.h" -#include "build/build_config.h" - -namespace base { - -namespace { - -bool SetMemory(void* start, void* end, DWORD prot) { - DCHECK(end > start); - const uintptr_t page_mask = ~(base::GetPageSize() - 1); - const uintptr_t page_start = reinterpret_cast(start) & page_mask; - DWORD old_prot; - return VirtualProtect(reinterpret_cast(page_start), - reinterpret_cast(end) - page_start, prot, - &old_prot) != 0; -} - -} // namespace - -bool AutoWritableMemory::SetMemoryReadWrite(void* start, void* end) { - return SetMemory(start, end, PAGE_READWRITE); -} - -bool AutoWritableMemory::SetMemoryReadOnly(void* start, void* end) { - return SetMemory(start, end, PAGE_READONLY); -} - -void AssertMemoryIsReadOnly(const void* ptr) { -#if DCHECK_IS_ON() - const uintptr_t page_mask = ~(base::GetPageSize() - 1); - const uintptr_t page_start = reinterpret_cast(ptr) & page_mask; - - MEMORY_BASIC_INFORMATION info; - SIZE_T result = - VirtualQuery(reinterpret_cast(page_start), &info, sizeof(info)); - DCHECK_GT(result, 0U); - DCHECK(info.Protect == PAGE_READONLY); -#endif // DCHECK_IS_ON() -} - -} // namespace base diff --git a/memory/ptr_util.h b/memory/ptr_util.h deleted file mode 100644 index 42f4f49ee..000000000 --- a/memory/ptr_util.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_PTR_UTIL_H_ -#define BASE_MEMORY_PTR_UTIL_H_ - -#include -#include - -namespace base { - -// Helper to transfer ownership of a raw pointer to a std::unique_ptr. -// Note that std::unique_ptr has very different semantics from -// std::unique_ptr: do not use this helper for array allocations. -template -std::unique_ptr WrapUnique(T* ptr) { - return std::unique_ptr(ptr); -} - -} // namespace base - -#endif // BASE_MEMORY_PTR_UTIL_H_ diff --git a/memory/ptr_util_unittest.cc b/memory/ptr_util_unittest.cc deleted file mode 100644 index 3fa40d809..000000000 --- a/memory/ptr_util_unittest.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/ptr_util.h" - -#include - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -class DeleteCounter { - public: - DeleteCounter() { ++count_; } - ~DeleteCounter() { --count_; } - - static size_t count() { return count_; } - - private: - static size_t count_; -}; - -size_t DeleteCounter::count_ = 0; - -} // namespace - -TEST(PtrUtilTest, WrapUnique) { - EXPECT_EQ(0u, DeleteCounter::count()); - DeleteCounter* counter = new DeleteCounter; - EXPECT_EQ(1u, DeleteCounter::count()); - std::unique_ptr owned_counter = WrapUnique(counter); - EXPECT_EQ(1u, DeleteCounter::count()); - owned_counter.reset(); - EXPECT_EQ(0u, DeleteCounter::count()); -} - -} // namespace base diff --git a/memory/raw_scoped_refptr_mismatch_checker.h b/memory/raw_scoped_refptr_mismatch_checker.h deleted file mode 100644 index ab8b2abcb..000000000 --- a/memory/raw_scoped_refptr_mismatch_checker.h +++ /dev/null @@ -1,52 +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_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_ -#define BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_ - -#include - -#include "base/template_util.h" - -// It is dangerous to post a task with a T* argument where T is a subtype of -// RefCounted(Base|ThreadSafeBase), since by the time the parameter is used, the -// object may already have been deleted since it was not held with a -// scoped_refptr. Example: http://crbug.com/27191 -// The following set of traits are designed to generate a compile error -// whenever this antipattern is attempted. - -namespace base { - -// This is a base internal implementation file used by task.h and callback.h. -// Not for public consumption, so we wrap it in namespace internal. -namespace internal { - -template -struct IsRefCountedType : std::false_type {}; - -template -struct IsRefCountedType()->AddRef()), - decltype(std::declval()->Release())>> - : std::true_type {}; - -template -struct NeedsScopedRefptrButGetsRawPtr { - static_assert(!std::is_reference::value, - "NeedsScopedRefptrButGetsRawPtr requires non-reference type."); - - enum { - // Human readable translation: you needed to be a scoped_refptr if you are a - // raw pointer type and are convertible to a RefCounted(Base|ThreadSafeBase) - // type. - value = std::is_pointer::value && - IsRefCountedType>::value - }; -}; - -} // namespace internal - -} // namespace base - -#endif // BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_ diff --git a/memory/read_only_shared_memory_region.cc b/memory/read_only_shared_memory_region.cc deleted file mode 100644 index 6b654c989..000000000 --- a/memory/read_only_shared_memory_region.cc +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2018 The Chromium 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/read_only_shared_memory_region.h" - -#include - -#include "base/memory/shared_memory.h" -#include "build/build_config.h" - -namespace base { - -// static -MappedReadOnlyRegion ReadOnlySharedMemoryRegion::Create(size_t size) { - subtle::PlatformSharedMemoryRegion handle = - subtle::PlatformSharedMemoryRegion::CreateWritable(size); - if (!handle.IsValid()) - return {}; - - void* memory_ptr = nullptr; - size_t mapped_size = 0; - if (!handle.MapAt(0, handle.GetSize(), &memory_ptr, &mapped_size)) - return {}; - - WritableSharedMemoryMapping mapping(memory_ptr, size, mapped_size, - handle.GetGUID()); -#if defined(OS_MACOSX) && !defined(OS_IOS) - handle.ConvertToReadOnly(memory_ptr); -#else - handle.ConvertToReadOnly(); -#endif // defined(OS_MACOSX) && !defined(OS_IOS) - ReadOnlySharedMemoryRegion region(std::move(handle)); - - if (!region.IsValid() || !mapping.IsValid()) - return {}; - - return {std::move(region), std::move(mapping)}; -} - -// static -ReadOnlySharedMemoryRegion ReadOnlySharedMemoryRegion::Deserialize( - subtle::PlatformSharedMemoryRegion handle) { - return ReadOnlySharedMemoryRegion(std::move(handle)); -} - -// static -subtle::PlatformSharedMemoryRegion -ReadOnlySharedMemoryRegion::TakeHandleForSerialization( - ReadOnlySharedMemoryRegion region) { - return std::move(region.handle_); -} - -ReadOnlySharedMemoryRegion::ReadOnlySharedMemoryRegion() = default; -ReadOnlySharedMemoryRegion::ReadOnlySharedMemoryRegion( - ReadOnlySharedMemoryRegion&& region) = default; -ReadOnlySharedMemoryRegion& ReadOnlySharedMemoryRegion::operator=( - ReadOnlySharedMemoryRegion&& region) = default; -ReadOnlySharedMemoryRegion::~ReadOnlySharedMemoryRegion() = default; - -ReadOnlySharedMemoryRegion ReadOnlySharedMemoryRegion::Duplicate() const { - return ReadOnlySharedMemoryRegion(handle_.Duplicate()); -} - -ReadOnlySharedMemoryMapping ReadOnlySharedMemoryRegion::Map() const { - return MapAt(0, handle_.GetSize()); -} - -ReadOnlySharedMemoryMapping ReadOnlySharedMemoryRegion::MapAt( - off_t offset, - size_t size) const { - if (!IsValid()) - return {}; - - void* memory = nullptr; - size_t mapped_size = 0; - if (!handle_.MapAt(offset, size, &memory, &mapped_size)) - return {}; - - return ReadOnlySharedMemoryMapping(memory, size, mapped_size, - handle_.GetGUID()); -} - -bool ReadOnlySharedMemoryRegion::IsValid() const { - return handle_.IsValid(); -} - -ReadOnlySharedMemoryRegion::ReadOnlySharedMemoryRegion( - subtle::PlatformSharedMemoryRegion handle) - : handle_(std::move(handle)) { - if (handle_.IsValid()) { - CHECK_EQ(handle_.GetMode(), - subtle::PlatformSharedMemoryRegion::Mode::kReadOnly); - } -} - -} // namespace base diff --git a/memory/read_only_shared_memory_region.h b/memory/read_only_shared_memory_region.h deleted file mode 100644 index 4f92762d5..000000000 --- a/memory/read_only_shared_memory_region.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2018 The Chromium 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_READ_ONLY_SHARED_MEMORY_REGION_H_ -#define BASE_MEMORY_READ_ONLY_SHARED_MEMORY_REGION_H_ - -#include - -#include "base/macros.h" -#include "base/memory/platform_shared_memory_region.h" -#include "base/memory/shared_memory_mapping.h" - -namespace base { - -struct MappedReadOnlyRegion; - -// Scoped move-only handle to a region of platform shared memory. The instance -// owns the platform handle it wraps. Mappings created by this region are -// read-only. These mappings remain valid even after the region handle is moved -// or destroyed. -class BASE_EXPORT ReadOnlySharedMemoryRegion { - public: - using MappingType = ReadOnlySharedMemoryMapping; - // Creates a new ReadOnlySharedMemoryRegion instance of a given size along - // with the WritableSharedMemoryMapping which provides the only way to modify - // the content of the newly created region. The returned region and mapping - // are guaranteed to either be both valid or both invalid. Use - // |MappedReadOnlyRegion::IsValid()| as a shortcut for checking creation - // success. - // - // This means that the caller's process is the only process that can modify - // the region content. If you need to pass write access to another process, - // consider using WritableSharedMemoryRegion or UnsafeSharedMemoryRegion. - static MappedReadOnlyRegion Create(size_t size); - - // Returns a ReadOnlySharedMemoryRegion built from a platform-specific handle - // that was taken from another ReadOnlySharedMemoryRegion instance. Returns an - // invalid region iff the |handle| is invalid. CHECK-fails if the |handle| - // isn't read-only. - // This should be used only by the code passing handles across process - // boundaries. - static ReadOnlySharedMemoryRegion Deserialize( - subtle::PlatformSharedMemoryRegion handle); - - // Extracts a platform handle from the region. Ownership is transferred to the - // returned region object. - // This should be used only for sending the handle from the current process to - // another. - static subtle::PlatformSharedMemoryRegion TakeHandleForSerialization( - ReadOnlySharedMemoryRegion region); - - // Default constructor initializes an invalid instance. - ReadOnlySharedMemoryRegion(); - - // Move operations are allowed. - ReadOnlySharedMemoryRegion(ReadOnlySharedMemoryRegion&&); - ReadOnlySharedMemoryRegion& operator=(ReadOnlySharedMemoryRegion&&); - - // Destructor closes shared memory region if valid. - // All created mappings will remain valid. - ~ReadOnlySharedMemoryRegion(); - - // Duplicates the underlying platform handle and creates a new - // ReadOnlySharedMemoryRegion instance that owns this handle. Returns a valid - // ReadOnlySharedMemoryRegion on success, invalid otherwise. The current - // region instance remains valid in any case. - ReadOnlySharedMemoryRegion Duplicate() const; - - // Maps the shared memory region into the caller's address space with - // read-only access. The mapped address is guaranteed to have an alignment of - // at least |subtle::PlatformSharedMemoryRegion::kMapMinimumAlignment|. - // Returns a valid ReadOnlySharedMemoryMapping instance on success, invalid - // otherwise. - ReadOnlySharedMemoryMapping Map() const; - - // Same as above, but maps only |size| bytes of the shared memory region - // starting with the given |offset|. |offset| must be aligned to value of - // |SysInfo::VMAllocationGranularity()|. Returns an invalid mapping if - // requested bytes are out of the region limits. - ReadOnlySharedMemoryMapping MapAt(off_t offset, size_t size) const; - - // Whether the underlying platform handle is valid. - bool IsValid() const; - - // Returns the maximum mapping size that can be created from this region. - size_t GetSize() const { - DCHECK(IsValid()); - return handle_.GetSize(); - } - - // Returns 128-bit GUID of the region. - const UnguessableToken& GetGUID() const { - DCHECK(IsValid()); - return handle_.GetGUID(); - } - - private: - explicit ReadOnlySharedMemoryRegion( - subtle::PlatformSharedMemoryRegion handle); - - subtle::PlatformSharedMemoryRegion handle_; - - DISALLOW_COPY_AND_ASSIGN(ReadOnlySharedMemoryRegion); -}; - -// Helper struct for return value of ReadOnlySharedMemoryRegion::Create(). -struct MappedReadOnlyRegion { - ReadOnlySharedMemoryRegion region; - WritableSharedMemoryMapping mapping; - // Helper function to check return value of - // ReadOnlySharedMemoryRegion::Create(). |region| and |mapping| either both - // valid or invalid. - bool IsValid() { - DCHECK_EQ(region.IsValid(), mapping.IsValid()); - return region.IsValid() && mapping.IsValid(); - } -}; - -} // namespace base - -#endif // BASE_MEMORY_READ_ONLY_SHARED_MEMORY_REGION_H_ diff --git a/memory/ref_counted.cc b/memory/ref_counted.cc deleted file mode 100644 index b9fa15fd5..000000000 --- a/memory/ref_counted.cc +++ /dev/null @@ -1,68 +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/memory/ref_counted.h" - -#include "base/threading/thread_collision_warner.h" - -namespace base { -namespace { - -#if DCHECK_IS_ON() -std::atomic_int g_cross_thread_ref_count_access_allow_count(0); -#endif - -} // namespace - -namespace subtle { - -bool RefCountedThreadSafeBase::HasOneRef() const { - return ref_count_.IsOne(); -} - -#if DCHECK_IS_ON() -RefCountedThreadSafeBase::~RefCountedThreadSafeBase() { - DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without " - "calling Release()"; -} -#endif - -#if defined(ARCH_CPU_64_BIT) -void RefCountedBase::AddRefImpl() const { - // Check if |ref_count_| overflow only on 64 bit archs since the number of - // objects may exceed 2^32. - // To avoid the binary size bloat, use non-inline function here. - CHECK(++ref_count_ > 0); -} -#endif - -#if !defined(ARCH_CPU_X86_FAMILY) -bool RefCountedThreadSafeBase::Release() const { - return ReleaseImpl(); -} -void RefCountedThreadSafeBase::AddRef() const { - AddRefImpl(); -} -#endif - -#if DCHECK_IS_ON() -bool RefCountedBase::CalledOnValidSequence() const { - return sequence_checker_.CalledOnValidSequence() || - g_cross_thread_ref_count_access_allow_count.load() != 0; -} -#endif - -} // namespace subtle - -#if DCHECK_IS_ON() -ScopedAllowCrossThreadRefCountAccess::ScopedAllowCrossThreadRefCountAccess() { - ++g_cross_thread_ref_count_access_allow_count; -} - -ScopedAllowCrossThreadRefCountAccess::~ScopedAllowCrossThreadRefCountAccess() { - --g_cross_thread_ref_count_access_allow_count; -} -#endif - -} // namespace base diff --git a/memory/ref_counted.h b/memory/ref_counted.h deleted file mode 100644 index 249f70e05..000000000 --- a/memory/ref_counted.h +++ /dev/null @@ -1,425 +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_MEMORY_REF_COUNTED_H_ -#define BASE_MEMORY_REF_COUNTED_H_ - -#include - -#include - -#include "base/atomic_ref_count.h" -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/scoped_refptr.h" -#include "base/sequence_checker.h" -#include "base/threading/thread_collision_warner.h" -#include "build/build_config.h" - -namespace base { -namespace subtle { - -class BASE_EXPORT RefCountedBase { - public: - bool HasOneRef() const { return ref_count_ == 1; } - - protected: - explicit RefCountedBase(StartRefCountFromZeroTag) { -#if DCHECK_IS_ON() - sequence_checker_.DetachFromSequence(); -#endif - } - - explicit RefCountedBase(StartRefCountFromOneTag) : ref_count_(1) { -#if DCHECK_IS_ON() - needs_adopt_ref_ = true; - sequence_checker_.DetachFromSequence(); -#endif - } - - ~RefCountedBase() { -#if DCHECK_IS_ON() - DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()"; -#endif - } - - 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_); -#if DCHECK_IS_ON() - DCHECK(!in_dtor_); - DCHECK(!needs_adopt_ref_) - << "This RefCounted object is created with non-zero reference count." - << " The first reference to such a object has to be made by AdoptRef or" - << " MakeRefCounted."; - if (ref_count_ >= 1) { - DCHECK(CalledOnValidSequence()); - } -#endif - - AddRefImpl(); - } - - // Returns true if the object should self-delete. - bool Release() const { - --ref_count_; - - // 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_); - -#if DCHECK_IS_ON() - DCHECK(!in_dtor_); - if (ref_count_ == 0) - in_dtor_ = true; - - if (ref_count_ >= 1) - DCHECK(CalledOnValidSequence()); - if (ref_count_ == 1) - sequence_checker_.DetachFromSequence(); -#endif - - return ref_count_ == 0; - } - - // Returns true if it is safe to read or write the object, from a thread - // safety standpoint. Should be DCHECK'd from the methods of RefCounted - // classes if there is a danger of objects being shared across threads. - // - // This produces fewer false positives than adding a separate SequenceChecker - // into the subclass, because it automatically detaches from the sequence when - // the reference count is 1 (and never fails if there is only one reference). - // - // This means unlike a separate SequenceChecker, it will permit a singly - // referenced object to be passed between threads (not holding a reference on - // the sending thread), but will trap if the sending thread holds onto a - // reference, or if the object is accessed from multiple threads - // simultaneously. - bool IsOnValidSequence() const { -#if DCHECK_IS_ON() - return ref_count_ <= 1 || CalledOnValidSequence(); -#else - return true; -#endif - } - - private: - template - friend scoped_refptr base::AdoptRef(U*); - - void Adopted() const { -#if DCHECK_IS_ON() - DCHECK(needs_adopt_ref_); - needs_adopt_ref_ = false; -#endif - } - -#if defined(ARCH_CPU_64_BIT) - void AddRefImpl() const; -#else - void AddRefImpl() const { ++ref_count_; } -#endif - -#if DCHECK_IS_ON() - bool CalledOnValidSequence() const; -#endif - - mutable uint32_t ref_count_ = 0; - -#if DCHECK_IS_ON() - mutable bool needs_adopt_ref_ = false; - mutable bool in_dtor_ = false; - mutable SequenceChecker sequence_checker_; -#endif - - DFAKE_MUTEX(add_release_); - - DISALLOW_COPY_AND_ASSIGN(RefCountedBase); -}; - -class BASE_EXPORT RefCountedThreadSafeBase { - public: - bool HasOneRef() const; - - protected: - explicit constexpr RefCountedThreadSafeBase(StartRefCountFromZeroTag) {} - explicit constexpr RefCountedThreadSafeBase(StartRefCountFromOneTag) - : ref_count_(1) { -#if DCHECK_IS_ON() - needs_adopt_ref_ = true; -#endif - } - -#if DCHECK_IS_ON() - ~RefCountedThreadSafeBase(); -#else - ~RefCountedThreadSafeBase() = default; -#endif - -// Release and AddRef are suitable for inlining on X86 because they generate -// very small code sequences. On other platforms (ARM), it causes a size -// regression and is probably not worth it. -#if defined(ARCH_CPU_X86_FAMILY) - // Returns true if the object should self-delete. - bool Release() const { return ReleaseImpl(); } - void AddRef() const { AddRefImpl(); } -#else - // Returns true if the object should self-delete. - bool Release() const; - void AddRef() const; -#endif - - private: - template - friend scoped_refptr base::AdoptRef(U*); - - void Adopted() const { -#if DCHECK_IS_ON() - DCHECK(needs_adopt_ref_); - needs_adopt_ref_ = false; -#endif - } - - ALWAYS_INLINE void AddRefImpl() const { -#if DCHECK_IS_ON() - DCHECK(!in_dtor_); - DCHECK(!needs_adopt_ref_) - << "This RefCounted object is created with non-zero reference count." - << " The first reference to such a object has to be made by AdoptRef or" - << " MakeRefCounted."; -#endif - ref_count_.Increment(); - } - - ALWAYS_INLINE bool ReleaseImpl() const { -#if DCHECK_IS_ON() - DCHECK(!in_dtor_); - DCHECK(!ref_count_.IsZero()); -#endif - if (!ref_count_.Decrement()) { -#if DCHECK_IS_ON() - in_dtor_ = true; -#endif - return true; - } - return false; - } - - mutable AtomicRefCount ref_count_{0}; -#if DCHECK_IS_ON() - mutable bool needs_adopt_ref_ = false; - mutable bool in_dtor_ = false; -#endif - - DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase); -}; - -} // namespace subtle - -// ScopedAllowCrossThreadRefCountAccess disables the check documented on -// RefCounted below for rare pre-existing use cases where thread-safety was -// guaranteed through other means (e.g. explicit sequencing of calls across -// execution sequences when bouncing between threads in order). New callers -// should refrain from using this (callsites handling thread-safety through -// locks should use RefCountedThreadSafe per the overhead of its atomics being -// negligible compared to locks anyways and callsites doing explicit sequencing -// should properly std::move() the ref to avoid hitting this check). -// TODO(tzik): Cleanup existing use cases and remove -// ScopedAllowCrossThreadRefCountAccess. -class BASE_EXPORT ScopedAllowCrossThreadRefCountAccess final { - public: -#if DCHECK_IS_ON() - ScopedAllowCrossThreadRefCountAccess(); - ~ScopedAllowCrossThreadRefCountAccess(); -#else - ScopedAllowCrossThreadRefCountAccess() {} - ~ScopedAllowCrossThreadRefCountAccess() {} -#endif -}; - -// -// A base class for reference counted classes. Otherwise, known as a cheap -// knock-off of WebKit's RefCounted class. To use this, just extend your -// class from it like so: -// -// class MyFoo : public base::RefCounted { -// ... -// private: -// friend class base::RefCounted; -// ~MyFoo(); -// }; -// -// You should always make your destructor non-public, to avoid any code deleting -// the object accidently while there are references to it. -// -// -// The ref count manipulation to RefCounted is NOT thread safe and has DCHECKs -// to trap unsafe cross thread usage. A subclass instance of RefCounted can be -// passed to another execution sequence only when its ref count is 1. If the ref -// count is more than 1, the RefCounted class verifies the ref updates are made -// on the same execution sequence as the previous ones. The subclass can also -// manually call IsOnValidSequence to trap other non-thread-safe accesses; see -// the documentation for that method. -// -// -// The reference count starts from zero by default, and we intended to migrate -// to start-from-one ref count. Put REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() to -// the ref counted class to opt-in. -// -// If an object has start-from-one ref count, the first scoped_refptr need to be -// created by base::AdoptRef() or base::MakeRefCounted(). We can use -// base::MakeRefCounted() to create create both type of ref counted object. -// -// The motivations to use start-from-one ref count are: -// - Start-from-one ref count doesn't need the ref count increment for the -// first reference. -// - It can detect an invalid object acquisition for a being-deleted object -// that has zero ref count. That tends to happen on custom deleter that -// delays the deletion. -// TODO(tzik): Implement invalid acquisition detection. -// - Behavior parity to Blink's WTF::RefCounted, whose count starts from one. -// And start-from-one ref count is a step to merge WTF::RefCounted into -// base::RefCounted. -// -#define REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() \ - static constexpr ::base::subtle::StartRefCountFromOneTag \ - kRefCountPreference = ::base::subtle::kStartRefCountFromOneTag - -template -class RefCounted; - -template -struct DefaultRefCountedTraits { - static void Destruct(const T* x) { - RefCounted::DeleteInternal(x); - } -}; - -template > -class RefCounted : public subtle::RefCountedBase { - public: - static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference = - subtle::kStartRefCountFromZeroTag; - - RefCounted() : subtle::RefCountedBase(T::kRefCountPreference) {} - - void AddRef() const { - subtle::RefCountedBase::AddRef(); - } - - void Release() const { - if (subtle::RefCountedBase::Release()) { - // Prune the code paths which the static analyzer may take to simulate - // object destruction. Use-after-free errors aren't possible given the - // lifetime guarantees of the refcounting system. - ANALYZER_SKIP_THIS_PATH(); - - Traits::Destruct(static_cast(this)); - } - } - - protected: - ~RefCounted() = default; - - private: - friend struct DefaultRefCountedTraits; - template - static void DeleteInternal(const U* x) { - delete x; - } - - DISALLOW_COPY_AND_ASSIGN(RefCounted); -}; - -// Forward declaration. -template class RefCountedThreadSafe; - -// Default traits for RefCountedThreadSafe. Deletes the object when its ref -// count reaches 0. Overload to delete it on a different thread etc. -template -struct DefaultRefCountedThreadSafeTraits { - static void Destruct(const T* x) { - // Delete through RefCountedThreadSafe to make child classes only need to be - // friend with RefCountedThreadSafe instead of this struct, which is an - // implementation detail. - RefCountedThreadSafe::DeleteInternal(x); - } -}; - -// -// A thread-safe variant of RefCounted -// -// class MyFoo : public base::RefCountedThreadSafe { -// ... -// }; -// -// If you're using the default trait, then you should add compile time -// asserts that no one else is deleting your object. i.e. -// private: -// friend class base::RefCountedThreadSafe; -// ~MyFoo(); -// -// We can use REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() with RefCountedThreadSafe -// too. See the comment above the RefCounted definition for details. -template > -class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase { - public: - static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference = - subtle::kStartRefCountFromZeroTag; - - explicit RefCountedThreadSafe() - : subtle::RefCountedThreadSafeBase(T::kRefCountPreference) {} - - void AddRef() const { - subtle::RefCountedThreadSafeBase::AddRef(); - } - - void Release() const { - if (subtle::RefCountedThreadSafeBase::Release()) { - ANALYZER_SKIP_THIS_PATH(); - Traits::Destruct(static_cast(this)); - } - } - - protected: - ~RefCountedThreadSafe() = default; - - private: - friend struct DefaultRefCountedThreadSafeTraits; - template - static void DeleteInternal(const U* x) { - delete x; - } - - DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe); -}; - -// -// A thread-safe wrapper for some piece of data so we can place other -// things in scoped_refptrs<>. -// -template -class RefCountedData - : public base::RefCountedThreadSafe< base::RefCountedData > { - public: - RefCountedData() : data() {} - RefCountedData(const T& in_value) : data(in_value) {} - RefCountedData(T&& in_value) : data(std::move(in_value)) {} - - T data; - - private: - friend class base::RefCountedThreadSafe >; - ~RefCountedData() = default; -}; - -} // namespace base - -#endif // BASE_MEMORY_REF_COUNTED_H_ diff --git a/memory/ref_counted_delete_on_sequence.h b/memory/ref_counted_delete_on_sequence.h deleted file mode 100644 index dd301063d..000000000 --- a/memory/ref_counted_delete_on_sequence.h +++ /dev/null @@ -1,82 +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_REF_COUNTED_DELETE_ON_SEQUENCE_H_ -#define BASE_MEMORY_REF_COUNTED_DELETE_ON_SEQUENCE_H_ - -#include - -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/sequenced_task_runner.h" - -namespace base { - -// RefCountedDeleteOnSequence is similar to RefCountedThreadSafe, and ensures -// that the object will be deleted on a specified sequence. -// -// Sample usage: -// class Foo : public RefCountedDeleteOnSequence { -// -// Foo(scoped_refptr task_runner) -// : RefCountedDeleteOnSequence(std::move(task_runner)) {} -// ... -// private: -// friend class RefCountedDeleteOnSequence; -// friend class DeleteHelper; -// -// ~Foo(); -// }; -template -class RefCountedDeleteOnSequence : public subtle::RefCountedThreadSafeBase { - public: - static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference = - subtle::kStartRefCountFromZeroTag; - - // A SequencedTaskRunner for the current sequence can be acquired by calling - // SequencedTaskRunnerHandle::Get(). - RefCountedDeleteOnSequence( - scoped_refptr owning_task_runner) - : subtle::RefCountedThreadSafeBase(T::kRefCountPreference), - owning_task_runner_(std::move(owning_task_runner)) { - DCHECK(owning_task_runner_); - } - - void AddRef() const { subtle::RefCountedThreadSafeBase::AddRef(); } - - void Release() const { - if (subtle::RefCountedThreadSafeBase::Release()) - DestructOnSequence(); - } - - protected: - friend class DeleteHelper; - ~RefCountedDeleteOnSequence() = default; - - SequencedTaskRunner* owning_task_runner() { - return owning_task_runner_.get(); - } - const SequencedTaskRunner* owning_task_runner() const { - return owning_task_runner_.get(); - } - - private: - void DestructOnSequence() const { - const T* t = static_cast(this); - if (owning_task_runner_->RunsTasksInCurrentSequence()) - delete t; - else - owning_task_runner_->DeleteSoon(FROM_HERE, t); - } - - const scoped_refptr owning_task_runner_; - - DISALLOW_COPY_AND_ASSIGN(RefCountedDeleteOnSequence); -}; - -} // namespace base - -#endif // BASE_MEMORY_REF_COUNTED_DELETE_ON_SEQUENCE_H_ diff --git a/memory/ref_counted_memory.cc b/memory/ref_counted_memory.cc deleted file mode 100644 index 23a5ffc25..000000000 --- a/memory/ref_counted_memory.cc +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/ref_counted_memory.h" - -#include - -#include "base/logging.h" -#include "base/memory/read_only_shared_memory_region.h" - -namespace base { - -bool RefCountedMemory::Equals( - const scoped_refptr& other) const { - return other.get() && - size() == other->size() && - (memcmp(front(), other->front(), size()) == 0); -} - -RefCountedMemory::RefCountedMemory() = default; - -RefCountedMemory::~RefCountedMemory() = default; - -const unsigned char* RefCountedStaticMemory::front() const { - return data_; -} - -size_t RefCountedStaticMemory::size() const { - return length_; -} - -RefCountedStaticMemory::~RefCountedStaticMemory() = default; - -RefCountedBytes::RefCountedBytes() = default; - -RefCountedBytes::RefCountedBytes(const std::vector& initializer) - : data_(initializer) { -} - -RefCountedBytes::RefCountedBytes(const unsigned char* p, size_t size) - : data_(p, p + size) {} - -RefCountedBytes::RefCountedBytes(size_t size) : data_(size, 0) {} - -scoped_refptr RefCountedBytes::TakeVector( - std::vector* to_destroy) { - auto bytes = MakeRefCounted(); - bytes->data_.swap(*to_destroy); - return bytes; -} - -const unsigned char* RefCountedBytes::front() const { - // STL will assert if we do front() on an empty vector, but calling code - // expects a NULL. - return size() ? &data_.front() : nullptr; -} - -size_t RefCountedBytes::size() const { - return data_.size(); -} - -RefCountedBytes::~RefCountedBytes() = default; - -RefCountedString::RefCountedString() = default; - -RefCountedString::~RefCountedString() = default; - -// static -scoped_refptr RefCountedString::TakeString( - std::string* to_destroy) { - auto self = MakeRefCounted(); - to_destroy->swap(self->data_); - return self; -} - -const unsigned char* RefCountedString::front() const { - return data_.empty() ? nullptr - : reinterpret_cast(data_.data()); -} - -size_t RefCountedString::size() const { - return data_.size(); -} - -RefCountedSharedMemory::RefCountedSharedMemory( - std::unique_ptr shm, - size_t size) - : shm_(std::move(shm)), size_(size) { - DCHECK(shm_); - DCHECK(shm_->memory()); - DCHECK_GT(size_, 0U); - DCHECK_LE(size_, shm_->mapped_size()); -} - -RefCountedSharedMemory::~RefCountedSharedMemory() = default; - -const unsigned char* RefCountedSharedMemory::front() const { - return static_cast(shm_->memory()); -} - -size_t RefCountedSharedMemory::size() const { - return size_; -} - -RefCountedSharedMemoryMapping::RefCountedSharedMemoryMapping( - ReadOnlySharedMemoryMapping mapping) - : mapping_(std::move(mapping)), size_(mapping_.size()) { - DCHECK_GT(size_, 0U); -} - -RefCountedSharedMemoryMapping::~RefCountedSharedMemoryMapping() = default; - -const unsigned char* RefCountedSharedMemoryMapping::front() const { - return static_cast(mapping_.memory()); -} - -size_t RefCountedSharedMemoryMapping::size() const { - return size_; -} - -// static -scoped_refptr -RefCountedSharedMemoryMapping::CreateFromWholeRegion( - const ReadOnlySharedMemoryRegion& region) { - ReadOnlySharedMemoryMapping mapping = region.Map(); - if (!mapping.IsValid()) - return nullptr; - return MakeRefCounted(std::move(mapping)); -} - -} // namespace base diff --git a/memory/ref_counted_memory.h b/memory/ref_counted_memory.h deleted file mode 100644 index 92a7d7b17..000000000 --- a/memory/ref_counted_memory.h +++ /dev/null @@ -1,193 +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_MEMORY_REF_COUNTED_MEMORY_H_ -#define BASE_MEMORY_REF_COUNTED_MEMORY_H_ - -#include - -#include -#include -#include - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/shared_memory.h" -#include "base/memory/shared_memory_mapping.h" - -namespace base { - -class ReadOnlySharedMemoryRegion; - -// A generic interface to memory. This object is reference counted because most -// of its subclasses own the data they carry, and this interface needs to -// support heterogeneous containers of these different types of memory. -class BASE_EXPORT RefCountedMemory - : public RefCountedThreadSafe { - public: - // Retrieves a pointer to the beginning of the data we point to. If the data - // is empty, this will return NULL. - virtual const unsigned char* front() const = 0; - - // Size of the memory pointed to. - virtual size_t size() const = 0; - - // Returns true if |other| is byte for byte equal. - bool Equals(const scoped_refptr& other) const; - - // Handy method to simplify calling front() with a reinterpret_cast. - template const T* front_as() const { - return reinterpret_cast(front()); - } - - protected: - friend class RefCountedThreadSafe; - RefCountedMemory(); - virtual ~RefCountedMemory(); -}; - -// An implementation of RefCountedMemory, where the ref counting does not -// matter. -class BASE_EXPORT RefCountedStaticMemory : public RefCountedMemory { - public: - RefCountedStaticMemory() : data_(nullptr), length_(0) {} - RefCountedStaticMemory(const void* data, size_t length) - : data_(static_cast(length ? data : nullptr)), - length_(length) {} - - // RefCountedMemory: - const unsigned char* front() const override; - size_t size() const override; - - private: - ~RefCountedStaticMemory() override; - - const unsigned char* data_; - size_t length_; - - DISALLOW_COPY_AND_ASSIGN(RefCountedStaticMemory); -}; - -// An implementation of RefCountedMemory, where the data is stored in a STL -// vector. -class BASE_EXPORT RefCountedBytes : public RefCountedMemory { - public: - RefCountedBytes(); - - // Constructs a RefCountedBytes object by copying from |initializer|. - explicit RefCountedBytes(const std::vector& initializer); - - // Constructs a RefCountedBytes object by copying |size| bytes from |p|. - RefCountedBytes(const unsigned char* p, size_t size); - - // Constructs a RefCountedBytes object by zero-initializing a new vector of - // |size| bytes. - explicit RefCountedBytes(size_t size); - - // Constructs a RefCountedBytes object by performing a swap. (To non - // destructively build a RefCountedBytes, use the constructor that takes a - // vector.) - static scoped_refptr TakeVector( - std::vector* to_destroy); - - // RefCountedMemory: - const unsigned char* front() const override; - size_t size() const override; - - const std::vector& data() const { return data_; } - std::vector& data() { return data_; } - - // Non-const versions of front() and front_as() that are simply shorthand for - // data().data(). - unsigned char* front() { return data_.data(); } - template - T* front_as() { - return reinterpret_cast(front()); - } - - private: - ~RefCountedBytes() override; - - std::vector data_; - - DISALLOW_COPY_AND_ASSIGN(RefCountedBytes); -}; - -// An implementation of RefCountedMemory, where the bytes are stored in a STL -// string. Use this if your data naturally arrives in that format. -class BASE_EXPORT RefCountedString : public RefCountedMemory { - public: - RefCountedString(); - - // Constructs a RefCountedString object by performing a swap. (To non - // destructively build a RefCountedString, use the default constructor and - // copy into object->data()). - static scoped_refptr TakeString(std::string* to_destroy); - - // RefCountedMemory: - const unsigned char* front() const override; - size_t size() const override; - - const std::string& data() const { return data_; } - std::string& data() { return data_; } - - private: - ~RefCountedString() override; - - std::string data_; - - DISALLOW_COPY_AND_ASSIGN(RefCountedString); -}; - -// An implementation of RefCountedMemory, where the bytes are stored in -// SharedMemory. -class BASE_EXPORT RefCountedSharedMemory : public RefCountedMemory { - public: - // Constructs a RefCountedMemory object by taking ownership of an already - // mapped SharedMemory object. - RefCountedSharedMemory(std::unique_ptr shm, size_t size); - - // RefCountedMemory: - const unsigned char* front() const override; - size_t size() const override; - - private: - ~RefCountedSharedMemory() override; - - const std::unique_ptr shm_; - const size_t size_; - - DISALLOW_COPY_AND_ASSIGN(RefCountedSharedMemory); -}; - -// An implementation of RefCountedMemory, where the bytes are stored in -// ReadOnlySharedMemoryMapping. -class BASE_EXPORT RefCountedSharedMemoryMapping : public RefCountedMemory { - public: - // Constructs a RefCountedMemory object by taking ownership of an already - // mapped ReadOnlySharedMemoryMapping object. - explicit RefCountedSharedMemoryMapping(ReadOnlySharedMemoryMapping mapping); - - // Convenience method to map all of |region| and take ownership of the - // mapping. Returns an empty scoped_refptr if the map operation fails. - static scoped_refptr CreateFromWholeRegion( - const ReadOnlySharedMemoryRegion& region); - - // RefCountedMemory: - const unsigned char* front() const override; - size_t size() const override; - - private: - ~RefCountedSharedMemoryMapping() override; - - const ReadOnlySharedMemoryMapping mapping_; - const size_t size_; - - DISALLOW_COPY_AND_ASSIGN(RefCountedSharedMemoryMapping); -}; - -} // namespace base - -#endif // BASE_MEMORY_REF_COUNTED_MEMORY_H_ diff --git a/memory/ref_counted_memory_unittest.cc b/memory/ref_counted_memory_unittest.cc deleted file mode 100644 index b7498f9ed..000000000 --- a/memory/ref_counted_memory_unittest.cc +++ /dev/null @@ -1,145 +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/memory/ref_counted_memory.h" - -#include - -#include - -#include "base/memory/read_only_shared_memory_region.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::Each; -using testing::ElementsAre; - -namespace base { - -TEST(RefCountedMemoryUnitTest, RefCountedStaticMemory) { - auto mem = MakeRefCounted("static mem00", 10); - - EXPECT_EQ(10U, mem->size()); - EXPECT_EQ("static mem", std::string(mem->front_as(), mem->size())); -} - -TEST(RefCountedMemoryUnitTest, RefCountedBytes) { - std::vector data; - data.push_back(45); - data.push_back(99); - scoped_refptr mem = RefCountedBytes::TakeVector(&data); - - EXPECT_EQ(0U, data.size()); - - ASSERT_EQ(2U, mem->size()); - EXPECT_EQ(45U, mem->front()[0]); - EXPECT_EQ(99U, mem->front()[1]); - - scoped_refptr mem2; - { - const unsigned char kData[] = {12, 11, 99}; - mem2 = MakeRefCounted(kData, arraysize(kData)); - } - ASSERT_EQ(3U, mem2->size()); - EXPECT_EQ(12U, mem2->front()[0]); - EXPECT_EQ(11U, mem2->front()[1]); - EXPECT_EQ(99U, mem2->front()[2]); -} - -TEST(RefCountedMemoryUnitTest, RefCountedBytesMutable) { - auto mem = base::MakeRefCounted(10); - - ASSERT_EQ(10U, mem->size()); - EXPECT_THAT(mem->data(), Each(0U)); - - // Test non-const versions of data(), front() and front_as<>(). - mem->data()[0] = 1; - mem->front()[1] = 2; - mem->front_as()[2] = 3; - - EXPECT_THAT(mem->data(), ElementsAre(1, 2, 3, 0, 0, 0, 0, 0, 0, 0)); -} - -TEST(RefCountedMemoryUnitTest, RefCountedString) { - std::string s("destroy me"); - scoped_refptr mem = RefCountedString::TakeString(&s); - - EXPECT_EQ(0U, s.size()); - - ASSERT_EQ(10U, mem->size()); - EXPECT_EQ('d', mem->front()[0]); - EXPECT_EQ('e', mem->front()[1]); - EXPECT_EQ('e', mem->front()[9]); -} - -TEST(RefCountedMemoryUnitTest, RefCountedSharedMemory) { - static const char kData[] = "shm_dummy_data"; - auto shm = std::make_unique(); - ASSERT_TRUE(shm->CreateAndMapAnonymous(sizeof(kData))); - memcpy(shm->memory(), kData, sizeof(kData)); - - auto mem = - MakeRefCounted(std::move(shm), sizeof(kData)); - ASSERT_EQ(sizeof(kData), mem->size()); - EXPECT_EQ('s', mem->front()[0]); - EXPECT_EQ('h', mem->front()[1]); - EXPECT_EQ('_', mem->front()[9]); -} - -TEST(RefCountedMemoryUnitTest, RefCountedSharedMemoryMapping) { - static const char kData[] = "mem_region_dummy_data"; - scoped_refptr mem; - { - MappedReadOnlyRegion region = - ReadOnlySharedMemoryRegion::Create(sizeof(kData)); - ReadOnlySharedMemoryMapping ro_mapping = region.region.Map(); - WritableSharedMemoryMapping rw_mapping = std::move(region.mapping); - ASSERT_TRUE(rw_mapping.IsValid()); - memcpy(rw_mapping.memory(), kData, sizeof(kData)); - mem = MakeRefCounted(std::move(ro_mapping)); - } - - ASSERT_LE(sizeof(kData), mem->size()); - EXPECT_EQ('e', mem->front()[1]); - EXPECT_EQ('m', mem->front()[2]); - EXPECT_EQ('o', mem->front()[8]); - - { - MappedReadOnlyRegion region = - ReadOnlySharedMemoryRegion::Create(sizeof(kData)); - WritableSharedMemoryMapping rw_mapping = std::move(region.mapping); - ASSERT_TRUE(rw_mapping.IsValid()); - memcpy(rw_mapping.memory(), kData, sizeof(kData)); - mem = RefCountedSharedMemoryMapping::CreateFromWholeRegion(region.region); - } - - ASSERT_LE(sizeof(kData), mem->size()); - EXPECT_EQ('_', mem->front()[3]); - EXPECT_EQ('r', mem->front()[4]); - EXPECT_EQ('i', mem->front()[7]); -} - -TEST(RefCountedMemoryUnitTest, Equals) { - std::string s1("same"); - scoped_refptr mem1 = RefCountedString::TakeString(&s1); - - std::vector d2 = {'s', 'a', 'm', 'e'}; - scoped_refptr mem2 = RefCountedBytes::TakeVector(&d2); - - EXPECT_TRUE(mem1->Equals(mem2)); - - std::string s3("diff"); - scoped_refptr mem3 = RefCountedString::TakeString(&s3); - - EXPECT_FALSE(mem1->Equals(mem3)); - EXPECT_FALSE(mem2->Equals(mem3)); -} - -TEST(RefCountedMemoryUnitTest, EqualsNull) { - std::string s("str"); - scoped_refptr mem = RefCountedString::TakeString(&s); - EXPECT_FALSE(mem->Equals(nullptr)); -} - -} // namespace base diff --git a/memory/ref_counted_unittest.cc b/memory/ref_counted_unittest.cc deleted file mode 100644 index df1c30f84..000000000 --- a/memory/ref_counted_unittest.cc +++ /dev/null @@ -1,671 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/ref_counted.h" - -#include -#include - -#include "base/test/gtest_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -class SelfAssign : public base::RefCounted { - protected: - virtual ~SelfAssign() = default; - - private: - friend class base::RefCounted; -}; - -class Derived : public SelfAssign { - protected: - ~Derived() override = default; - - private: - friend class base::RefCounted; -}; - -class CheckDerivedMemberAccess : public scoped_refptr { - public: - CheckDerivedMemberAccess() { - // This shouldn't compile if we don't have access to the member variable. - SelfAssign** pptr = &ptr_; - EXPECT_EQ(*pptr, ptr_); - } -}; - -class ScopedRefPtrToSelf : public base::RefCounted { - public: - ScopedRefPtrToSelf() : self_ptr_(this) {} - - static bool was_destroyed() { return was_destroyed_; } - - static void reset_was_destroyed() { was_destroyed_ = false; } - - scoped_refptr self_ptr_; - - private: - friend class base::RefCounted; - ~ScopedRefPtrToSelf() { was_destroyed_ = true; } - - static bool was_destroyed_; -}; - -bool ScopedRefPtrToSelf::was_destroyed_ = false; - -class ScopedRefPtrCountBase : public base::RefCounted { - public: - ScopedRefPtrCountBase() { ++constructor_count_; } - - static int constructor_count() { return constructor_count_; } - - static int destructor_count() { return destructor_count_; } - - static void reset_count() { - constructor_count_ = 0; - destructor_count_ = 0; - } - - protected: - virtual ~ScopedRefPtrCountBase() { ++destructor_count_; } - - private: - friend class base::RefCounted; - - static int constructor_count_; - static int destructor_count_; -}; - -int ScopedRefPtrCountBase::constructor_count_ = 0; -int ScopedRefPtrCountBase::destructor_count_ = 0; - -class ScopedRefPtrCountDerived : public ScopedRefPtrCountBase { - public: - ScopedRefPtrCountDerived() { ++constructor_count_; } - - static int constructor_count() { return constructor_count_; } - - static int destructor_count() { return destructor_count_; } - - static void reset_count() { - constructor_count_ = 0; - destructor_count_ = 0; - } - - protected: - ~ScopedRefPtrCountDerived() override { ++destructor_count_; } - - private: - friend class base::RefCounted; - - static int constructor_count_; - static int destructor_count_; -}; - -int ScopedRefPtrCountDerived::constructor_count_ = 0; -int ScopedRefPtrCountDerived::destructor_count_ = 0; - -class Other : public base::RefCounted { - private: - friend class base::RefCounted; - - ~Other() = default; -}; - -class HasPrivateDestructorWithDeleter; - -struct Deleter { - static void Destruct(const HasPrivateDestructorWithDeleter* x); -}; - -class HasPrivateDestructorWithDeleter - : public base::RefCounted { - public: - HasPrivateDestructorWithDeleter() = default; - - private: - friend struct Deleter; - ~HasPrivateDestructorWithDeleter() = default; -}; - -void Deleter::Destruct(const HasPrivateDestructorWithDeleter* x) { - delete x; -} - -scoped_refptr Overloaded(scoped_refptr other) { - return other; -} - -scoped_refptr Overloaded(scoped_refptr self_assign) { - return self_assign; -} - -class InitialRefCountIsOne : public base::RefCounted { - public: - REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); - - InitialRefCountIsOne() = default; - - private: - friend class base::RefCounted; - ~InitialRefCountIsOne() = default; -}; - -// Checks that the scoped_refptr is null before the reference counted object is -// destroyed. -class CheckRefptrNull : public base::RefCounted { - public: - // Set the last scoped_refptr that will have a reference to this object. - void set_scoped_refptr(scoped_refptr* ptr) { ptr_ = ptr; } - - protected: - virtual ~CheckRefptrNull() { - EXPECT_NE(ptr_, nullptr); - EXPECT_EQ(ptr_->get(), nullptr); - } - - private: - friend class base::RefCounted; - - scoped_refptr* ptr_ = nullptr; -}; - -} // end namespace - -TEST(RefCountedUnitTest, TestSelfAssignment) { - SelfAssign* p = new SelfAssign; - scoped_refptr var(p); - var = *&var; // The *& defeats Clang's -Wself-assign warning. - EXPECT_EQ(var.get(), p); - var = std::move(var); - EXPECT_EQ(var.get(), p); - var.swap(var); - EXPECT_EQ(var.get(), p); - swap(var, var); - EXPECT_EQ(var.get(), p); -} - -TEST(RefCountedUnitTest, ScopedRefPtrMemberAccess) { - CheckDerivedMemberAccess check; -} - -TEST(RefCountedUnitTest, ScopedRefPtrToSelfPointerAssignment) { - ScopedRefPtrToSelf::reset_was_destroyed(); - - ScopedRefPtrToSelf* check = new ScopedRefPtrToSelf(); - EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed()); - check->self_ptr_ = nullptr; - EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed()); -} - -TEST(RefCountedUnitTest, ScopedRefPtrToSelfMoveAssignment) { - ScopedRefPtrToSelf::reset_was_destroyed(); - - ScopedRefPtrToSelf* check = new ScopedRefPtrToSelf(); - EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed()); - // Releasing |check->self_ptr_| will delete |check|. - // The move assignment operator must assign |check->self_ptr_| first then - // release |check->self_ptr_|. - check->self_ptr_ = scoped_refptr(); - EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed()); -} - -TEST(RefCountedUnitTest, BooleanTesting) { - scoped_refptr ptr_to_an_instance = new SelfAssign; - EXPECT_TRUE(ptr_to_an_instance); - EXPECT_FALSE(!ptr_to_an_instance); - - if (ptr_to_an_instance) { - } else { - ADD_FAILURE() << "Pointer to an instance should result in true."; - } - - if (!ptr_to_an_instance) { // check for operator!(). - ADD_FAILURE() << "Pointer to an instance should result in !x being false."; - } - - scoped_refptr null_ptr; - EXPECT_FALSE(null_ptr); - EXPECT_TRUE(!null_ptr); - - if (null_ptr) { - ADD_FAILURE() << "Null pointer should result in false."; - } - - if (!null_ptr) { // check for operator!(). - } else { - ADD_FAILURE() << "Null pointer should result in !x being true."; - } -} - -TEST(RefCountedUnitTest, Equality) { - scoped_refptr p1(new SelfAssign); - scoped_refptr p2(new SelfAssign); - - EXPECT_EQ(p1, p1); - EXPECT_EQ(p2, p2); - - EXPECT_NE(p1, p2); - EXPECT_NE(p2, p1); -} - -TEST(RefCountedUnitTest, NullptrEquality) { - scoped_refptr ptr_to_an_instance(new SelfAssign); - scoped_refptr ptr_to_nullptr; - - EXPECT_NE(nullptr, ptr_to_an_instance); - EXPECT_NE(ptr_to_an_instance, nullptr); - EXPECT_EQ(nullptr, ptr_to_nullptr); - EXPECT_EQ(ptr_to_nullptr, nullptr); -} - -TEST(RefCountedUnitTest, ConvertibleEquality) { - scoped_refptr p1(new Derived); - scoped_refptr p2; - - EXPECT_NE(p1, p2); - EXPECT_NE(p2, p1); - - p2 = p1; - - EXPECT_EQ(p1, p2); - EXPECT_EQ(p2, p1); -} - -TEST(RefCountedUnitTest, MoveAssignment1) { - ScopedRefPtrCountBase::reset_count(); - - { - ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); - scoped_refptr p1(raw); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - { - scoped_refptr p2; - - p2 = std::move(p1); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(nullptr, p1.get()); - EXPECT_EQ(raw, p2.get()); - - // p2 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); - - // p1 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); -} - -TEST(RefCountedUnitTest, MoveAssignment2) { - ScopedRefPtrCountBase::reset_count(); - - { - ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); - scoped_refptr p1; - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - { - scoped_refptr p2(raw); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - p1 = std::move(p2); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(raw, p1.get()); - EXPECT_EQ(nullptr, p2.get()); - - // p2 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - // p1 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); -} - -TEST(RefCountedUnitTest, MoveAssignmentSameInstance1) { - ScopedRefPtrCountBase::reset_count(); - - { - ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); - scoped_refptr p1(raw); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - { - scoped_refptr p2(p1); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - p1 = std::move(p2); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(raw, p1.get()); - EXPECT_EQ(nullptr, p2.get()); - - // p2 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - // p1 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); -} - -TEST(RefCountedUnitTest, MoveAssignmentSameInstance2) { - ScopedRefPtrCountBase::reset_count(); - - { - ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); - scoped_refptr p1(raw); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - { - scoped_refptr p2(p1); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - p2 = std::move(p1); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(nullptr, p1.get()); - EXPECT_EQ(raw, p2.get()); - - // p2 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); - - // p1 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); -} - -TEST(RefCountedUnitTest, MoveAssignmentDifferentInstances) { - ScopedRefPtrCountBase::reset_count(); - - { - ScopedRefPtrCountBase *raw1 = new ScopedRefPtrCountBase(); - scoped_refptr p1(raw1); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - { - ScopedRefPtrCountBase *raw2 = new ScopedRefPtrCountBase(); - scoped_refptr p2(raw2); - EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - p1 = std::move(p2); - EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(raw2, p1.get()); - EXPECT_EQ(nullptr, p2.get()); - - // p2 goes out of scope. - } - EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); - - // p1 goes out of scope. - } - EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(2, ScopedRefPtrCountBase::destructor_count()); -} - -TEST(RefCountedUnitTest, MoveAssignmentSelfMove) { - ScopedRefPtrCountBase::reset_count(); - - { - ScopedRefPtrCountBase* raw = new ScopedRefPtrCountBase; - scoped_refptr p1(raw); - scoped_refptr& p1_ref = p1; - - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - p1 = std::move(p1_ref); - - // |p1| is "valid but unspecified", so don't bother inspecting its - // contents, just ensure that we don't crash. - } - - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); -} - -TEST(RefCountedUnitTest, MoveAssignmentDerived) { - ScopedRefPtrCountBase::reset_count(); - ScopedRefPtrCountDerived::reset_count(); - - { - ScopedRefPtrCountBase *raw1 = new ScopedRefPtrCountBase(); - scoped_refptr p1(raw1); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountDerived::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); - - { - ScopedRefPtrCountDerived *raw2 = new ScopedRefPtrCountDerived(); - scoped_refptr p2(raw2); - EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); - - p1 = std::move(p2); - EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); - EXPECT_EQ(raw2, p1.get()); - EXPECT_EQ(nullptr, p2.get()); - - // p2 goes out of scope. - } - EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); - - // p1 goes out of scope. - } - EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(2, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count()); -} - -TEST(RefCountedUnitTest, MoveConstructor) { - ScopedRefPtrCountBase::reset_count(); - - { - ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); - scoped_refptr p1(raw); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - - { - scoped_refptr p2(std::move(p1)); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(nullptr, p1.get()); - EXPECT_EQ(raw, p2.get()); - - // p2 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); - - // p1 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); -} - -TEST(RefCountedUnitTest, MoveConstructorDerived) { - ScopedRefPtrCountBase::reset_count(); - ScopedRefPtrCountDerived::reset_count(); - - { - ScopedRefPtrCountDerived *raw1 = new ScopedRefPtrCountDerived(); - scoped_refptr p1(raw1); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); - - { - scoped_refptr p2(std::move(p1)); - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); - EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); - EXPECT_EQ(nullptr, p1.get()); - EXPECT_EQ(raw1, p2.get()); - - // p2 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count()); - - // p1 goes out of scope. - } - EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); - EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count()); -} - -TEST(RefCountedUnitTest, TestOverloadResolutionCopy) { - const scoped_refptr derived(new Derived); - const scoped_refptr expected(derived); - EXPECT_EQ(expected, Overloaded(derived)); - - const scoped_refptr other(new Other); - EXPECT_EQ(other, Overloaded(other)); -} - -TEST(RefCountedUnitTest, TestOverloadResolutionMove) { - scoped_refptr derived(new Derived); - const scoped_refptr expected(derived); - EXPECT_EQ(expected, Overloaded(std::move(derived))); - - scoped_refptr other(new Other); - const scoped_refptr other2(other); - EXPECT_EQ(other2, Overloaded(std::move(other))); -} - -TEST(RefCountedUnitTest, TestMakeRefCounted) { - scoped_refptr derived = new Derived; - EXPECT_TRUE(derived->HasOneRef()); - derived.reset(); - - scoped_refptr derived2 = base::MakeRefCounted(); - EXPECT_TRUE(derived2->HasOneRef()); - derived2.reset(); -} - -TEST(RefCountedUnitTest, TestInitialRefCountIsOne) { - scoped_refptr obj = - base::MakeRefCounted(); - EXPECT_TRUE(obj->HasOneRef()); - obj.reset(); - - scoped_refptr obj2 = - base::AdoptRef(new InitialRefCountIsOne); - EXPECT_TRUE(obj2->HasOneRef()); - obj2.reset(); - - scoped_refptr obj3 = base::MakeRefCounted(); - EXPECT_TRUE(obj3->HasOneRef()); - obj3.reset(); -} - -TEST(RefCountedUnitTest, TestPrivateDestructorWithDeleter) { - // Ensure that RefCounted doesn't need the access to the pointee dtor when - // a custom deleter is given. - scoped_refptr obj = - base::MakeRefCounted(); -} - -TEST(RefCountedUnitTest, TestReset) { - ScopedRefPtrCountBase::reset_count(); - - // Create ScopedRefPtrCountBase that is referenced by |obj1| and |obj2|. - scoped_refptr obj1 = - base::MakeRefCounted(); - scoped_refptr obj2 = obj1; - EXPECT_NE(obj1.get(), nullptr); - EXPECT_NE(obj2.get(), nullptr); - EXPECT_EQ(ScopedRefPtrCountBase::constructor_count(), 1); - EXPECT_EQ(ScopedRefPtrCountBase::destructor_count(), 0); - - // Check that calling reset() on |obj1| resets it. |obj2| still has a - // reference to the ScopedRefPtrCountBase so it shouldn't be reset. - obj1.reset(); - EXPECT_EQ(obj1.get(), nullptr); - EXPECT_EQ(ScopedRefPtrCountBase::constructor_count(), 1); - EXPECT_EQ(ScopedRefPtrCountBase::destructor_count(), 0); - - // Check that calling reset() on |obj2| resets it and causes the deletion of - // the ScopedRefPtrCountBase. - obj2.reset(); - EXPECT_EQ(obj2.get(), nullptr); - EXPECT_EQ(ScopedRefPtrCountBase::constructor_count(), 1); - EXPECT_EQ(ScopedRefPtrCountBase::destructor_count(), 1); -} - -TEST(RefCountedUnitTest, TestResetAlreadyNull) { - // Check that calling reset() on a null scoped_refptr does nothing. - scoped_refptr obj; - obj.reset(); - // |obj| should still be null after calling reset(). - EXPECT_EQ(obj.get(), nullptr); -} - -TEST(RefCountedUnitTest, CheckScopedRefptrNullBeforeObjectDestruction) { - scoped_refptr obj = base::MakeRefCounted(); - obj->set_scoped_refptr(&obj); - - // Check that when reset() is called the scoped_refptr internal pointer is set - // to null before the reference counted object is destroyed. This check is - // done by the CheckRefptrNull destructor. - obj.reset(); - EXPECT_EQ(obj.get(), nullptr); -} - -TEST(RefCountedDeathTest, TestAdoptRef) { - // Check that WrapRefCounted() DCHECKs if passed a type that defines - // REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE. - EXPECT_DCHECK_DEATH(base::WrapRefCounted(new InitialRefCountIsOne)); - - // Check that AdoptRef() DCHECKs if passed a nullptr. - InitialRefCountIsOne* ptr = nullptr; - EXPECT_DCHECK_DEATH(base::AdoptRef(ptr)); - - // Check that AdoptRef() DCHECKs if passed an object that doesn't need to be - // adopted. - scoped_refptr obj = - base::MakeRefCounted(); - EXPECT_DCHECK_DEATH(base::AdoptRef(obj.get())); -} diff --git a/memory/ref_counted_unittest.nc b/memory/ref_counted_unittest.nc deleted file mode 100644 index b8c371f74..000000000 --- a/memory/ref_counted_unittest.nc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 The Chromium 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/ref_counted.h" - -namespace base { - -class InitialRefCountIsZero : public base::RefCounted { - public: - InitialRefCountIsZero() {} - private: - friend class base::RefCounted; - ~InitialRefCountIsZero() {} -}; - -// TODO(hans): Remove .* and update the static_assert expectations once we roll -// past Clang r313315. https://crbug.com/765692. - -#if defined(NCTEST_ADOPT_REF_TO_ZERO_START) // [r"fatal error: static_assert failed .*\"Use AdoptRef only for the reference count starts from one\.\""] - -void WontCompile() { - AdoptRef(new InitialRefCountIsZero()); -} - -#endif - -} // namespace base diff --git a/memory/scoped_policy.h b/memory/scoped_policy.h deleted file mode 100644 index 5dbf2048d..000000000 --- a/memory/scoped_policy.h +++ /dev/null @@ -1,25 +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_MEMORY_SCOPED_POLICY_H_ -#define BASE_MEMORY_SCOPED_POLICY_H_ - -namespace base { -namespace scoped_policy { - -// Defines the ownership policy for a scoped object. -enum OwnershipPolicy { - // The scoped object takes ownership of an object by taking over an existing - // ownership claim. - ASSUME, - - // The scoped object will retain the the object and any initial ownership is - // not changed. - RETAIN -}; - -} // namespace scoped_policy -} // namespace base - -#endif // BASE_MEMORY_SCOPED_POLICY_H_ diff --git a/memory/scoped_refptr.h b/memory/scoped_refptr.h deleted file mode 100644 index 389d0cbf6..000000000 --- a/memory/scoped_refptr.h +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright 2017 The Chromium 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_REFPTR_H_ -#define BASE_MEMORY_SCOPED_REFPTR_H_ - -#include - -#include -#include -#include - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/macros.h" - -template -class scoped_refptr; - -namespace base { - -template -class RefCounted; -template -class RefCountedThreadSafe; - -template -scoped_refptr AdoptRef(T* t); - -namespace subtle { - -enum AdoptRefTag { kAdoptRefTag }; -enum StartRefCountFromZeroTag { kStartRefCountFromZeroTag }; -enum StartRefCountFromOneTag { kStartRefCountFromOneTag }; - -template -constexpr bool IsRefCountPreferenceOverridden(const T*, - const RefCounted*) { - return !std::is_same, - std::decay_t>::value; -} - -template -constexpr bool IsRefCountPreferenceOverridden( - const T*, - const RefCountedThreadSafe*) { - return !std::is_same, - std::decay_t>::value; -} - -constexpr bool IsRefCountPreferenceOverridden(...) { - return false; -} - -} // namespace subtle - -// Creates a scoped_refptr from a raw pointer without incrementing the reference -// count. Use this only for a newly created object whose reference count starts -// from 1 instead of 0. -template -scoped_refptr AdoptRef(T* obj) { - using Tag = std::decay_t; - static_assert(std::is_same::value, - "Use AdoptRef only for the reference count starts from one."); - - DCHECK(obj); - DCHECK(obj->HasOneRef()); - obj->Adopted(); - return scoped_refptr(obj, subtle::kAdoptRefTag); -} - -namespace subtle { - -template -scoped_refptr AdoptRefIfNeeded(T* obj, StartRefCountFromZeroTag) { - return scoped_refptr(obj); -} - -template -scoped_refptr AdoptRefIfNeeded(T* obj, StartRefCountFromOneTag) { - return AdoptRef(obj); -} - -} // namespace subtle - -// Constructs an instance of T, which is a ref counted type, and wraps the -// object into a scoped_refptr. -template -scoped_refptr MakeRefCounted(Args&&... args) { - T* obj = new T(std::forward(args)...); - return subtle::AdoptRefIfNeeded(obj, T::kRefCountPreference); -} - -// Takes an instance of T, which is a ref counted type, and wraps the object -// into a scoped_refptr. -template -scoped_refptr WrapRefCounted(T* t) { - return scoped_refptr(t); -} - -} // namespace base - -// -// A smart pointer class for reference counted objects. Use this class instead -// of calling AddRef and Release manually on a reference counted object to -// avoid common memory leaks caused by forgetting to Release an object -// reference. Sample usage: -// -// class MyFoo : public RefCounted { -// ... -// private: -// friend class RefCounted; // Allow destruction by RefCounted<>. -// ~MyFoo(); // Destructor must be private/protected. -// }; -// -// void some_function() { -// scoped_refptr foo = MakeRefCounted(); -// foo->Method(param); -// // |foo| is released when this function returns -// } -// -// void some_other_function() { -// scoped_refptr foo = MakeRefCounted(); -// ... -// foo.reset(); // explicitly releases |foo| -// ... -// if (foo) -// foo->Method(param); -// } -// -// The above examples show how scoped_refptr acts like a pointer to T. -// Given two scoped_refptr classes, it is also possible to exchange -// references between the two objects, like so: -// -// { -// scoped_refptr a = MakeRefCounted(); -// scoped_refptr b; -// -// b.swap(a); -// // now, |b| references the MyFoo object, and |a| references nullptr. -// } -// -// To make both |a| and |b| in the above example reference the same MyFoo -// object, simply use the assignment operator: -// -// { -// scoped_refptr a = MakeRefCounted(); -// scoped_refptr b; -// -// b = a; -// // now, |a| and |b| each own a reference to the same MyFoo object. -// } -// -// Also see Chromium's ownership and calling conventions: -// https://chromium.googlesource.com/chromium/src/+/lkgr/styleguide/c++/c++.md#object-ownership-and-calling-conventions -// Specifically: -// If the function (at least sometimes) takes a ref on a refcounted object, -// declare the param as scoped_refptr. The caller can decide whether it -// wishes to transfer ownership (by calling std::move(t) when passing t) or -// retain its ref (by simply passing t directly). -// In other words, use scoped_refptr like you would a std::unique_ptr except -// in the odd case where it's required to hold on to a ref while handing one -// to another component (if a component merely needs to use t on the stack -// without keeping a ref: pass t as a raw T*). -template -class scoped_refptr { - public: - typedef T element_type; - - constexpr scoped_refptr() = default; - - // Constructs from raw pointer. constexpr if |p| is null. - constexpr scoped_refptr(T* p) : ptr_(p) { - if (ptr_) - AddRef(ptr_); - } - - // Copy constructor. This is required in addition to the copy conversion - // constructor below. - scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {} - - // Copy conversion constructor. - template ::value>::type> - scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {} - - // Move constructor. This is required in addition to the move conversion - // constructor below. - scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { r.ptr_ = nullptr; } - - // Move conversion constructor. - template ::value>::type> - scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { - r.ptr_ = nullptr; - } - - ~scoped_refptr() { - static_assert(!base::subtle::IsRefCountPreferenceOverridden( - static_cast(nullptr), static_cast(nullptr)), - "It's unsafe to override the ref count preference." - " Please remove REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE" - " from subclasses."); - if (ptr_) - Release(ptr_); - } - - T* get() const { return ptr_; } - - T& operator*() const { - DCHECK(ptr_); - return *ptr_; - } - - T* operator->() const { - DCHECK(ptr_); - return ptr_; - } - - scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); } - - // Unified assignment operator. - scoped_refptr& operator=(scoped_refptr r) noexcept { - swap(r); - return *this; - } - - // Sets managed object to null and releases reference to the previous managed - // object, if it existed. - void reset() { scoped_refptr().swap(*this); } - - void swap(scoped_refptr& r) noexcept { std::swap(ptr_, r.ptr_); } - - explicit operator bool() const { return ptr_ != nullptr; } - - template - bool operator==(const scoped_refptr& rhs) const { - return ptr_ == rhs.get(); - } - - template - bool operator!=(const scoped_refptr& rhs) const { - return !operator==(rhs); - } - - template - bool operator<(const scoped_refptr& rhs) const { - return ptr_ < rhs.get(); - } - - protected: - T* ptr_ = nullptr; - - private: - template - friend scoped_refptr base::AdoptRef(U*); - - scoped_refptr(T* p, base::subtle::AdoptRefTag) : ptr_(p) {} - - // Friend required for move constructors that set r.ptr_ to null. - template - friend class scoped_refptr; - - // Non-inline helpers to allow: - // class Opaque; - // extern template class scoped_refptr; - // Otherwise the compiler will complain that Opaque is an incomplete type. - static void AddRef(T* ptr); - static void Release(T* ptr); -}; - -// static -template -void scoped_refptr::AddRef(T* ptr) { - ptr->AddRef(); -} - -// static -template -void scoped_refptr::Release(T* ptr) { - ptr->Release(); -} - -template -bool operator==(const scoped_refptr& lhs, const U* rhs) { - return lhs.get() == rhs; -} - -template -bool operator==(const T* lhs, const scoped_refptr& rhs) { - return lhs == rhs.get(); -} - -template -bool operator==(const scoped_refptr& lhs, std::nullptr_t null) { - return !static_cast(lhs); -} - -template -bool operator==(std::nullptr_t null, const scoped_refptr& rhs) { - return !static_cast(rhs); -} - -template -bool operator!=(const scoped_refptr& lhs, const U* rhs) { - return !operator==(lhs, rhs); -} - -template -bool operator!=(const T* lhs, const scoped_refptr& rhs) { - return !operator==(lhs, rhs); -} - -template -bool operator!=(const scoped_refptr& lhs, std::nullptr_t null) { - return !operator==(lhs, null); -} - -template -bool operator!=(std::nullptr_t null, const scoped_refptr& rhs) { - return !operator==(null, rhs); -} - -template -std::ostream& operator<<(std::ostream& out, const scoped_refptr& p) { - return out << p.get(); -} - -template -void swap(scoped_refptr& lhs, scoped_refptr& rhs) noexcept { - lhs.swap(rhs); -} - -#endif // BASE_MEMORY_SCOPED_REFPTR_H_ diff --git a/memory/shared_memory.h b/memory/shared_memory.h deleted file mode 100644 index c573ef725..000000000 --- a/memory/shared_memory.h +++ /dev/null @@ -1,254 +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_MEMORY_SHARED_MEMORY_H_ -#define BASE_MEMORY_SHARED_MEMORY_H_ - -#include - -#include - -#include "base/base_export.h" -#include "base/hash.h" -#include "base/macros.h" -#include "base/memory/shared_memory_handle.h" -#include "base/process/process_handle.h" -#include "base/strings/string16.h" -#include "build/build_config.h" - -#if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#include -#include -#include "base/file_descriptor_posix.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#endif - -#if defined(OS_WIN) -#include "base/win/scoped_handle.h" -#endif - -namespace base { - -class FilePath; - -// Options for creating a shared memory object. -struct BASE_EXPORT SharedMemoryCreateOptions { -#if defined(OS_MACOSX) && !defined(OS_IOS) - // The type of OS primitive that should back the SharedMemory object. - SharedMemoryHandle::Type type = SharedMemoryHandle::MACH; -#elif !defined(OS_FUCHSIA) - // 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_deprecated = nullptr; - - // 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_deprecated is non-NULL. - bool open_existing_deprecated = false; -#endif // defined(OS_MACOSX) && !defined(OS_IOS) - - // Size of the shared memory object to be created. - // When opening an existing object, this has no effect. - size_t size = 0; - - // If true, mappings might need to be made executable later. - bool executable = false; - - // If true, the file can be shared read-only to a process. - bool share_read_only = false; -}; - -// Platform abstraction for shared memory. -// SharedMemory consumes a SharedMemoryHandle [potentially one that it created] -// to map a shared memory OS resource into the virtual address space of the -// current process. -class BASE_EXPORT SharedMemory { - public: - SharedMemory(); - -#if defined(OS_WIN) - // Similar to the default constructor, except that this allows for - // calling LockDeprecated() to acquire the named mutex before either Create or - // Open are called on Windows. - explicit SharedMemory(const string16& name); -#endif - - // Create a new SharedMemory object from an existing, open - // shared memory file. - // - // WARNING: This does not reduce the OS-level permissions on the handle; it - // only affects how the SharedMemory will be mmapped. Use - // GetReadOnlyHandle to drop permissions. TODO(jln,jyasskin): DCHECK - // that |read_only| matches the permissions of the handle. - SharedMemory(const SharedMemoryHandle& handle, bool read_only); - - // Closes any open files. - ~SharedMemory(); - - // Return true iff the given handle is valid (i.e. not the distingished - // invalid value; NULL for a HANDLE and -1 for a file descriptor) - static bool IsHandleValid(const SharedMemoryHandle& handle); - - // Closes a shared memory handle. - static void CloseHandle(const SharedMemoryHandle& handle); - - // Returns the maximum number of handles that can be open at once per process. - static size_t GetHandleLimit(); - - // Duplicates The underlying OS primitive. Returns an invalid handle on - // failure. The caller is responsible for destroying the duplicated OS - // primitive. - static SharedMemoryHandle DuplicateHandle(const SharedMemoryHandle& handle); - -#if defined(OS_POSIX) - // This method requires that the SharedMemoryHandle is backed by a POSIX fd. - static int GetFdFromSharedMemoryHandle(const SharedMemoryHandle& handle); -#endif - - // Creates a shared memory object as described by the options struct. - // Returns true on success and false on failure. - bool Create(const SharedMemoryCreateOptions& options); - - // Creates and maps an anonymous shared memory segment of size size. - // Returns true on success and false on failure. - bool CreateAndMapAnonymous(size_t size); - - // Creates an anonymous shared memory segment of size size. - // Returns true on success and false on failure. - bool CreateAnonymous(size_t size) { - SharedMemoryCreateOptions options; - options.size = size; - return Create(options); - } - -#if (!defined(OS_MACOSX) || defined(OS_IOS)) && !defined(OS_FUCHSIA) - // 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 CreateNamedDeprecated( - const std::string& name, bool open_existing, size_t size) { - SharedMemoryCreateOptions options; - options.name_deprecated = &name; - options.open_existing_deprecated = open_existing; - options.size = size; - return Create(options); - } - - // Deletes resources associated with a shared memory segment based on name. - // Not all platforms require this call. - bool Delete(const std::string& name); - - // Opens a shared memory segment based on a name. - // If read_only is true, opens for read-only access. - // Returns true on success, false on failure. - bool Open(const std::string& name, bool read_only); -#endif // !defined(OS_MACOSX) || defined(OS_IOS) - - // 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. This method will fail - // if this object is currently mapped. - bool Map(size_t bytes) { - return MapAt(0, bytes); - } - - // Same as above, but with |offset| to specify from begining of the shared - // memory block to map. - // |offset| must be alignent to value of |SysInfo::VMAllocationGranularity()|. - bool MapAt(off_t offset, size_t bytes); - enum { MAP_MINIMUM_ALIGNMENT = 32 }; - - // Unmaps the shared memory from the caller's address space. - // Returns true if successful; returns false on error or if the - // memory is not mapped. - bool Unmap(); - - // The size requested when the map is first created. - size_t requested_size() const { return requested_size_; } - - // The actual size of the mapped memory (may be larger than requested). - size_t mapped_size() const { return mapped_size_; } - - // Gets a pointer to the opened memory space if it has been - // Mapped via Map(). Returns NULL if it is not mapped. - void* memory() const { return memory_; } - - // Returns the underlying OS handle for this segment. - // Use of this handle for anything other than an opaque - // identifier is not portable. - SharedMemoryHandle handle() const; - - // Returns the underlying OS handle for this segment. The caller takes - // ownership of the handle and memory is unmapped. This is equivalent to - // duplicating the handle and then calling Unmap() and Close() on this object, - // without the overhead of duplicating the handle. - SharedMemoryHandle TakeHandle(); - - // Closes the open shared memory segment. The memory will remain mapped if - // it was previously mapped. - // It is safe to call Close repeatedly. - void Close(); - - // Returns a read-only handle to this shared memory region. The caller takes - // ownership of the handle. For POSIX handles, CHECK-fails if the region - // wasn't Created or Opened with share_read_only=true, which is required to - // make the handle read-only. When the handle is passed to the IPC subsystem, - // that takes ownership of the handle. As such, it's not valid to pass the - // sample handle to the IPC subsystem twice. Returns an invalid handle on - // failure. - SharedMemoryHandle GetReadOnlyHandle() const; - - // Returns an ID for the mapped region. This is ID of the SharedMemoryHandle - // that was mapped. The ID is valid even after the SharedMemoryHandle is - // Closed, as long as the region is not unmapped. - const UnguessableToken& mapped_id() const { return mapped_id_; } - - private: -#if defined(OS_POSIX) && !defined(OS_NACL) && !defined(OS_ANDROID) && \ - (!defined(OS_MACOSX) || defined(OS_IOS)) - bool FilePathForMemoryName(const std::string& mem_name, FilePath* path); -#endif - -#if defined(OS_WIN) - // If true indicates this came from an external source so needs extra checks - // before being mapped. - bool external_section_ = false; - string16 name_; -#elif !defined(OS_ANDROID) && !defined(OS_FUCHSIA) - // If valid, points to the same memory region as shm_, but with readonly - // permissions. - SharedMemoryHandle readonly_shm_; -#endif - -#if defined(OS_MACOSX) && !defined(OS_IOS) - // The mechanism by which the memory is mapped. Only valid if |memory_| is not - // |nullptr|. - SharedMemoryHandle::Type mapped_memory_mechanism_ = SharedMemoryHandle::MACH; -#endif - - // The OS primitive that backs the shared memory region. - SharedMemoryHandle shm_; - - size_t mapped_size_ = 0; - void* memory_ = nullptr; - bool read_only_ = false; - size_t requested_size_ = 0; - base::UnguessableToken mapped_id_; - - DISALLOW_COPY_AND_ASSIGN(SharedMemory); -}; - -} // namespace base - -#endif // BASE_MEMORY_SHARED_MEMORY_H_ diff --git a/memory/shared_memory_android.cc b/memory/shared_memory_android.cc deleted file mode 100644 index c126767c6..000000000 --- a/memory/shared_memory_android.cc +++ /dev/null @@ -1,75 +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/memory/shared_memory.h" - -#include -#include - -#include "base/logging.h" -#include "third_party/ashmem/ashmem.h" - -namespace base { - -// For Android, we use ashmem to implement SharedMemory. ashmem_create_region -// will automatically pin the region. We never explicitly call pin/unpin. When -// all the file descriptors from different processes associated with the region -// are closed, the memory buffer will go away. - -bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { - DCHECK(!shm_.IsValid()); - - if (options.size > static_cast(std::numeric_limits::max())) - return false; - - // "name" is just a label in ashmem. It is visible in /proc/pid/maps. - int fd = ashmem_create_region( - options.name_deprecated ? options.name_deprecated->c_str() : "", - options.size); - shm_ = SharedMemoryHandle::ImportHandle(fd, options.size); - if (!shm_.IsValid()) { - DLOG(ERROR) << "Shared memory creation failed"; - return false; - } - - int flags = PROT_READ | PROT_WRITE | (options.executable ? PROT_EXEC : 0); - int err = ashmem_set_prot_region(shm_.GetHandle(), flags); - if (err < 0) { - DLOG(ERROR) << "Error " << err << " when setting protection of ashmem"; - return false; - } - - requested_size_ = options.size; - - return true; -} - -bool SharedMemory::Delete(const std::string& name) { - // Like on Windows, this is intentionally returning true as ashmem will - // automatically releases the resource when all FDs on it are closed. - return true; -} - -bool SharedMemory::Open(const std::string& name, bool read_only) { - // ashmem doesn't support name mapping - NOTIMPLEMENTED(); - return false; -} - -void SharedMemory::Close() { - if (shm_.IsValid()) { - shm_.Close(); - shm_ = SharedMemoryHandle(); - } -} - -SharedMemoryHandle SharedMemory::GetReadOnlyHandle() const { - // There are no read-only Ashmem descriptors on Android. - // Instead, the protection mask is a property of the region itself. - SharedMemoryHandle handle = shm_.Duplicate(); - handle.SetReadOnly(); - return handle; -} - -} // namespace base diff --git a/memory/shared_memory_fuchsia.cc b/memory/shared_memory_fuchsia.cc deleted file mode 100644 index 7fb8ceeef..000000000 --- a/memory/shared_memory_fuchsia.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory.h" - -#include - -#include -#include -#include - -#include "base/bits.h" -#include "base/fuchsia/fuchsia_logging.h" -#include "base/memory/shared_memory_tracker.h" -#include "base/process/process_metrics.h" - -namespace base { - -SharedMemory::SharedMemory() {} - -SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) - : shm_(handle), read_only_(read_only) {} - -SharedMemory::~SharedMemory() { - Unmap(); - Close(); -} - -// static -bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { - return handle.IsValid(); -} - -// static -void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { - DCHECK(handle.IsValid()); - handle.Close(); -} - -// static -size_t SharedMemory::GetHandleLimit() { - // Duplicated from the internal Magenta kernel constant kMaxHandleCount - // (kernel/lib/zircon/zircon.cpp). - return 256 * 1024u; -} - -bool SharedMemory::CreateAndMapAnonymous(size_t size) { - return CreateAnonymous(size) && Map(size); -} - -bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { - requested_size_ = options.size; - mapped_size_ = bits::Align(requested_size_, GetPageSize()); - zx::vmo vmo; - zx_status_t status = zx::vmo::create(mapped_size_, 0, &vmo); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "zx_vmo_create"; - return false; - } - - if (!options.executable) { - // If options.executable isn't set, drop that permission by replacement. - const int kNoExecFlags = ZX_DEFAULT_VMO_RIGHTS & ~ZX_RIGHT_EXECUTE; - status = vmo.replace(kNoExecFlags, &vmo); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "zx_handle_replace"; - return false; - } - } - - shm_ = SharedMemoryHandle(vmo.release(), mapped_size_, - UnguessableToken::Create()); - return true; -} - -bool SharedMemory::MapAt(off_t offset, size_t bytes) { - if (!shm_.IsValid()) - return false; - - if (bytes > static_cast(std::numeric_limits::max())) - return false; - - if (memory_) - return false; - - int flags = ZX_VM_FLAG_PERM_READ; - if (!read_only_) - flags |= ZX_VM_FLAG_PERM_WRITE; - uintptr_t addr; - zx_status_t status = zx::vmar::root_self().map( - 0, *zx::unowned_vmo(shm_.GetHandle()), offset, bytes, flags, &addr); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "zx_vmar_map"; - return false; - } - memory_ = reinterpret_cast(addr); - - mapped_size_ = bytes; - mapped_id_ = shm_.GetGUID(); - SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); - return true; -} - -bool SharedMemory::Unmap() { - if (!memory_) - return false; - - SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); - - uintptr_t addr = reinterpret_cast(memory_); - zx_status_t status = zx::vmar::root_self().unmap(addr, mapped_size_); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "zx_vmar_unmap"; - return false; - } - - memory_ = nullptr; - mapped_id_ = UnguessableToken(); - return true; -} - -void SharedMemory::Close() { - if (shm_.IsValid()) { - shm_.Close(); - shm_ = SharedMemoryHandle(); - } -} - -SharedMemoryHandle SharedMemory::handle() const { - return shm_; -} - -SharedMemoryHandle SharedMemory::TakeHandle() { - SharedMemoryHandle handle(shm_); - handle.SetOwnershipPassesToIPC(true); - Unmap(); - shm_ = SharedMemoryHandle(); - return handle; -} - -SharedMemoryHandle SharedMemory::DuplicateHandle( - const SharedMemoryHandle& handle) { - return handle.Duplicate(); -} - -SharedMemoryHandle SharedMemory::GetReadOnlyHandle() const { - zx::vmo duped_handle; - const int kNoWriteOrExec = - ZX_DEFAULT_VMO_RIGHTS & - ~(ZX_RIGHT_WRITE | ZX_RIGHT_EXECUTE | ZX_RIGHT_SET_PROPERTY); - zx_status_t status = zx::unowned_vmo(shm_.GetHandle()) - ->duplicate(kNoWriteOrExec, &duped_handle); - if (status != ZX_OK) - return SharedMemoryHandle(); - - SharedMemoryHandle handle(duped_handle.release(), shm_.GetSize(), - shm_.GetGUID()); - handle.SetOwnershipPassesToIPC(true); - return handle; -} - -} // namespace base diff --git a/memory/shared_memory_handle.cc b/memory/shared_memory_handle.cc deleted file mode 100644 index 085bde46c..000000000 --- a/memory/shared_memory_handle.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory_handle.h" - -namespace base { - -SharedMemoryHandle::SharedMemoryHandle(const SharedMemoryHandle& handle) = - default; - -SharedMemoryHandle& SharedMemoryHandle::operator=( - const SharedMemoryHandle& handle) = default; - -base::UnguessableToken SharedMemoryHandle::GetGUID() const { - return guid_; -} - -size_t SharedMemoryHandle::GetSize() const { - return size_; -} - -} // namespace base diff --git a/memory/shared_memory_handle.h b/memory/shared_memory_handle.h deleted file mode 100644 index dd3d47aa0..000000000 --- a/memory/shared_memory_handle.h +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_SHARED_MEMORY_HANDLE_H_ -#define BASE_MEMORY_SHARED_MEMORY_HANDLE_H_ - -#include - -#include "base/unguessable_token.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/process/process_handle.h" -#include "base/win/windows_types.h" -#elif defined(OS_MACOSX) && !defined(OS_IOS) -#include -#include "base/base_export.h" -#include "base/file_descriptor_posix.h" -#include "base/macros.h" -#include "base/process/process_handle.h" -#elif defined(OS_POSIX) -#include -#include "base/file_descriptor_posix.h" -#elif defined(OS_FUCHSIA) -#include -#endif - -namespace base { - -// SharedMemoryHandle is the smallest possible IPC-transportable "reference" to -// a shared memory OS resource. A "reference" can be consumed exactly once [by -// base::SharedMemory] to map the shared memory OS resource into the virtual -// address space of the current process. -// TODO(erikchen): This class should have strong ownership semantics to prevent -// leaks of the underlying OS resource. https://crbug.com/640840. -class BASE_EXPORT SharedMemoryHandle { - public: - // The default constructor returns an invalid SharedMemoryHandle. - SharedMemoryHandle(); - - // Standard copy constructor. The new instance shares the underlying OS - // primitives. - SharedMemoryHandle(const SharedMemoryHandle& handle); - - // Standard assignment operator. The updated instance shares the underlying - // OS primitives. - SharedMemoryHandle& operator=(const SharedMemoryHandle& handle); - - // Closes the underlying OS resource. - // The fact that this method needs to be "const" is an artifact of the - // original interface for base::SharedMemory::CloseHandle. - // TODO(erikchen): This doesn't clear the underlying reference, which seems - // like a bug, but is how this class has always worked. Fix this: - // https://crbug.com/716072. - void Close() const; - - // Whether ownership of the underlying OS resource is implicitly passed to - // the IPC subsystem during serialization. - void SetOwnershipPassesToIPC(bool ownership_passes); - bool OwnershipPassesToIPC() const; - - // Whether the underlying OS resource is valid. - bool IsValid() const; - - // Duplicates the underlying OS resource. Using the return value as a - // parameter to an IPC message will cause the IPC subsystem to consume the OS - // resource. - SharedMemoryHandle Duplicate() const; - - // Uniques identifies the shared memory region that the underlying OS resource - // points to. Multiple SharedMemoryHandles that point to the same shared - // memory region will have the same GUID. Preserved across IPC. - base::UnguessableToken GetGUID() const; - - // Returns the size of the memory region that SharedMemoryHandle points to. - size_t GetSize() const; - -#if defined(OS_WIN) - // Takes implicit ownership of |h|. - // |guid| uniquely identifies the shared memory region pointed to by the - // underlying OS resource. If the HANDLE is associated with another - // SharedMemoryHandle, the caller must pass the |guid| of that - // SharedMemoryHandle. Otherwise, the caller should generate a new - // UnguessableToken. - // Passing the wrong |size| has no immediate consequence, but may cause errors - // when trying to map the SharedMemoryHandle at a later point in time. - SharedMemoryHandle(HANDLE h, size_t size, const base::UnguessableToken& guid); - HANDLE GetHandle() const; -#elif defined(OS_FUCHSIA) - // Takes implicit ownership of |h|. - // |guid| uniquely identifies the shared memory region pointed to by the - // underlying OS resource. If the zx_handle_t is associated with another - // SharedMemoryHandle, the caller must pass the |guid| of that - // SharedMemoryHandle. Otherwise, the caller should generate a new - // UnguessableToken. - // Passing the wrong |size| has no immediate consequence, but may cause errors - // when trying to map the SharedMemoryHandle at a later point in time. - SharedMemoryHandle(zx_handle_t h, - size_t size, - const base::UnguessableToken& guid); - zx_handle_t GetHandle() const; -#elif defined(OS_MACOSX) && !defined(OS_IOS) - enum Type { - // The SharedMemoryHandle is backed by a POSIX fd. - POSIX, - // The SharedMemoryHandle is backed by the Mach primitive "memory object". - MACH, - }; - - // Makes a Mach-based SharedMemoryHandle of the given size. On error, - // subsequent calls to IsValid() return false. - // Passing the wrong |size| has no immediate consequence, but may cause errors - // when trying to map the SharedMemoryHandle at a later point in time. - SharedMemoryHandle(mach_vm_size_t size, const base::UnguessableToken& guid); - - // Makes a Mach-based SharedMemoryHandle from |memory_object|, a named entry - // in the current task. The memory region has size |size|. - // Passing the wrong |size| has no immediate consequence, but may cause errors - // when trying to map the SharedMemoryHandle at a later point in time. - SharedMemoryHandle(mach_port_t memory_object, - mach_vm_size_t size, - const base::UnguessableToken& guid); - - // Exposed so that the SharedMemoryHandle can be transported between - // processes. - mach_port_t GetMemoryObject() const; - - // The SharedMemoryHandle must be valid. - // Returns whether the SharedMemoryHandle was successfully mapped into memory. - // On success, |memory| is an output variable that contains the start of the - // mapped memory. - bool MapAt(off_t offset, size_t bytes, void** memory, bool read_only); -#elif defined(OS_POSIX) - // Creates a SharedMemoryHandle from an |fd| supplied from an external - // service. - // Passing the wrong |size| has no immediate consequence, but may cause errors - // when trying to map the SharedMemoryHandle at a later point in time. - static SharedMemoryHandle ImportHandle(int fd, size_t size); - - // Returns the underlying OS resource. - int GetHandle() const; - - // Invalidates [but doesn't close] the underlying OS resource. This will leak - // unless the caller is careful. - int Release(); -#endif - -#if defined(OS_ANDROID) - // Marks the current file descriptor as read-only, for the purpose of - // mapping. This is independent of the region's read-only status. - void SetReadOnly() { read_only_ = true; } - - // Returns true iff the descriptor is to be used for read-only - // mappings. - bool IsReadOnly() const { return read_only_; } - - // Returns true iff the corresponding region is read-only. - bool IsRegionReadOnly() const; - - // Try to set the region read-only. This will fail any future attempt - // at read-write mapping. - bool SetRegionReadOnly() const; -#endif - -#if defined(OS_POSIX) - // Constructs a SharedMemoryHandle backed by a FileDescriptor. The newly - // created instance has the same ownership semantics as base::FileDescriptor. - // This typically means that the SharedMemoryHandle takes ownership of the - // |fd| if |auto_close| is true. Unfortunately, it's common for existing code - // to make shallow copies of SharedMemoryHandle, and the one that is finally - // passed into a base::SharedMemory is the one that "consumes" the fd. - // - // |guid| uniquely identifies the shared memory region pointed to by the - // underlying OS resource. If |file_descriptor| is associated with another - // SharedMemoryHandle, the caller must pass the |guid| of that - // SharedMemoryHandle. Otherwise, the caller should generate a new - // UnguessableToken. - // Passing the wrong |size| has no immediate consequence, but may cause errors - // when trying to map the SharedMemoryHandle at a later point in time. - SharedMemoryHandle(const base::FileDescriptor& file_descriptor, - size_t size, - const base::UnguessableToken& guid); -#endif - - private: -#if defined(OS_WIN) - HANDLE handle_ = nullptr; - - // Whether passing this object as a parameter to an IPC message passes - // ownership of |handle_| to the IPC stack. This is meant to mimic the - // behavior of the |auto_close| parameter of FileDescriptor. This member only - // affects attachment-brokered SharedMemoryHandles. - // Defaults to |false|. - bool ownership_passes_to_ipc_ = false; -#elif defined(OS_FUCHSIA) - zx_handle_t handle_ = ZX_HANDLE_INVALID; - bool ownership_passes_to_ipc_ = false; -#elif defined(OS_MACOSX) && !defined(OS_IOS) - friend class SharedMemory; - friend bool CheckReadOnlySharedMemoryHandleForTesting( - SharedMemoryHandle handle); - - Type type_ = MACH; - - // Each instance of a SharedMemoryHandle is backed either by a POSIX fd or a - // mach port. |type_| determines the backing member. - union { - FileDescriptor file_descriptor_; - - struct { - mach_port_t memory_object_ = MACH_PORT_NULL; - - // Whether passing this object as a parameter to an IPC message passes - // ownership of |memory_object_| to the IPC stack. This is meant to mimic - // the behavior of the |auto_close| parameter of FileDescriptor. - // Defaults to |false|. - bool ownership_passes_to_ipc_ = false; - }; - }; -#elif defined(OS_ANDROID) - friend class SharedMemory; - - FileDescriptor file_descriptor_; - bool read_only_ = false; -#elif defined(OS_POSIX) - FileDescriptor file_descriptor_; -#endif - - base::UnguessableToken guid_; - - // The size of the region referenced by the SharedMemoryHandle. - size_t size_ = 0; -}; - -} // namespace base - -#endif // BASE_MEMORY_SHARED_MEMORY_HANDLE_H_ diff --git a/memory/shared_memory_handle_android.cc b/memory/shared_memory_handle_android.cc deleted file mode 100644 index 1b615353b..000000000 --- a/memory/shared_memory_handle_android.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory_handle.h" - -#include -#include - -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "base/posix/unix_domain_socket.h" -#include "base/unguessable_token.h" -#include "third_party/ashmem/ashmem.h" - -namespace base { - -static int GetAshmemRegionProtectionMask(int fd) { - int prot = ashmem_get_prot_region(fd); - if (prot < 0) { - DPLOG(ERROR) << "ashmem_get_prot_region"; - return -1; - } - return prot; -} - -SharedMemoryHandle::SharedMemoryHandle() = default; - -SharedMemoryHandle::SharedMemoryHandle( - const base::FileDescriptor& file_descriptor, - size_t size, - const base::UnguessableToken& guid) - : guid_(guid), size_(size) { - DCHECK_GE(file_descriptor.fd, 0); - file_descriptor_ = file_descriptor; -} - -// static -SharedMemoryHandle SharedMemoryHandle::ImportHandle(int fd, size_t size) { - SharedMemoryHandle handle; - handle.file_descriptor_.fd = fd; - handle.file_descriptor_.auto_close = false; - handle.guid_ = UnguessableToken::Create(); - handle.size_ = size; - return handle; -} - -int SharedMemoryHandle::GetHandle() const { - DCHECK(IsValid()); - return file_descriptor_.fd; -} - -bool SharedMemoryHandle::IsValid() const { - return file_descriptor_.fd >= 0; -} - -void SharedMemoryHandle::Close() const { - DCHECK(IsValid()); - if (IGNORE_EINTR(close(file_descriptor_.fd)) < 0) - PLOG(ERROR) << "close"; -} - -int SharedMemoryHandle::Release() { - int old_fd = file_descriptor_.fd; - file_descriptor_.fd = -1; - return old_fd; -} - -SharedMemoryHandle SharedMemoryHandle::Duplicate() const { - DCHECK(IsValid()); - SharedMemoryHandle result; - int duped_handle = HANDLE_EINTR(dup(file_descriptor_.fd)); - if (duped_handle >= 0) { - result = SharedMemoryHandle(FileDescriptor(duped_handle, true), GetSize(), - GetGUID()); - if (IsReadOnly()) - result.SetReadOnly(); - } - return result; -} - -void SharedMemoryHandle::SetOwnershipPassesToIPC(bool ownership_passes) { - file_descriptor_.auto_close = ownership_passes; -} - -bool SharedMemoryHandle::OwnershipPassesToIPC() const { - return file_descriptor_.auto_close; -} - -bool SharedMemoryHandle::IsRegionReadOnly() const { - int prot = GetAshmemRegionProtectionMask(file_descriptor_.fd); - return (prot >= 0 && (prot & PROT_WRITE) == 0); -} - -bool SharedMemoryHandle::SetRegionReadOnly() const { - int fd = file_descriptor_.fd; - int prot = GetAshmemRegionProtectionMask(fd); - if (prot < 0) - return false; - - if ((prot & PROT_WRITE) == 0) { - // Region is already read-only. - return true; - } - - prot &= ~PROT_WRITE; - int ret = ashmem_set_prot_region(fd, prot); - if (ret != 0) { - DPLOG(ERROR) << "ashmem_set_prot_region"; - return false; - } - return true; -} - -} // namespace base diff --git a/memory/shared_memory_handle_fuchsia.cc b/memory/shared_memory_handle_fuchsia.cc deleted file mode 100644 index eab681f71..000000000 --- a/memory/shared_memory_handle_fuchsia.cc +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory_handle.h" - -#include - -#include "base/logging.h" -#include "base/unguessable_token.h" - -namespace base { - -SharedMemoryHandle::SharedMemoryHandle() {} - -SharedMemoryHandle::SharedMemoryHandle(zx_handle_t h, - size_t size, - const base::UnguessableToken& guid) - : handle_(h), guid_(guid), size_(size) {} - -void SharedMemoryHandle::Close() const { - DCHECK(handle_ != ZX_HANDLE_INVALID); - zx_handle_close(handle_); -} - -bool SharedMemoryHandle::IsValid() const { - return handle_ != ZX_HANDLE_INVALID; -} - -SharedMemoryHandle SharedMemoryHandle::Duplicate() const { - zx_handle_t duped_handle; - zx_status_t status = - zx_handle_duplicate(handle_, ZX_RIGHT_SAME_RIGHTS, &duped_handle); - if (status != ZX_OK) - return SharedMemoryHandle(); - - SharedMemoryHandle handle(duped_handle, GetSize(), GetGUID()); - handle.SetOwnershipPassesToIPC(true); - return handle; -} - -zx_handle_t SharedMemoryHandle::GetHandle() const { - return handle_; -} - -void SharedMemoryHandle::SetOwnershipPassesToIPC(bool ownership_passes) { - ownership_passes_to_ipc_ = ownership_passes; -} - -bool SharedMemoryHandle::OwnershipPassesToIPC() const { - return ownership_passes_to_ipc_; -} - -} // namespace base diff --git a/memory/shared_memory_handle_mac.cc b/memory/shared_memory_handle_mac.cc deleted file mode 100644 index 0e863fa67..000000000 --- a/memory/shared_memory_handle_mac.cc +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory_handle.h" - -#include -#include -#include -#include - -#include "base/mac/mac_util.h" -#include "base/mac/mach_logging.h" -#include "base/posix/eintr_wrapper.h" -#include "base/unguessable_token.h" - -namespace base { - -SharedMemoryHandle::SharedMemoryHandle() {} - -SharedMemoryHandle::SharedMemoryHandle( - const base::FileDescriptor& file_descriptor, - size_t size, - const base::UnguessableToken& guid) - : type_(POSIX), - file_descriptor_(file_descriptor), - guid_(guid), - size_(size) {} - -SharedMemoryHandle::SharedMemoryHandle(mach_vm_size_t size, - const base::UnguessableToken& guid) { - type_ = MACH; - mach_port_t named_right; - kern_return_t kr = mach_make_memory_entry_64( - mach_task_self(), - &size, - 0, // Address. - MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE, - &named_right, - MACH_PORT_NULL); // Parent handle. - if (kr != KERN_SUCCESS) { - memory_object_ = MACH_PORT_NULL; - return; - } - - memory_object_ = named_right; - size_ = size; - ownership_passes_to_ipc_ = false; - guid_ = guid; -} - -SharedMemoryHandle::SharedMemoryHandle(mach_port_t memory_object, - mach_vm_size_t size, - const base::UnguessableToken& guid) - : type_(MACH), - memory_object_(memory_object), - ownership_passes_to_ipc_(false), - guid_(guid), - size_(size) {} - -SharedMemoryHandle SharedMemoryHandle::Duplicate() const { - switch (type_) { - case POSIX: { - if (!IsValid()) - return SharedMemoryHandle(); - int duped_fd = HANDLE_EINTR(dup(file_descriptor_.fd)); - if (duped_fd < 0) - return SharedMemoryHandle(); - return SharedMemoryHandle(FileDescriptor(duped_fd, true), size_, guid_); - } - case MACH: { - if (!IsValid()) - return SharedMemoryHandle(); - - // Increment the ref count. - kern_return_t kr = mach_port_mod_refs(mach_task_self(), memory_object_, - MACH_PORT_RIGHT_SEND, 1); - DCHECK_EQ(kr, KERN_SUCCESS); - SharedMemoryHandle handle(*this); - handle.SetOwnershipPassesToIPC(true); - return handle; - } - } -} - -bool SharedMemoryHandle::IsValid() const { - switch (type_) { - case POSIX: - return file_descriptor_.fd >= 0; - case MACH: - return memory_object_ != MACH_PORT_NULL; - } -} - -mach_port_t SharedMemoryHandle::GetMemoryObject() const { - DCHECK_EQ(type_, MACH); - return memory_object_; -} - -bool SharedMemoryHandle::MapAt(off_t offset, - size_t bytes, - void** memory, - bool read_only) { - DCHECK(IsValid()); - switch (type_) { - case SharedMemoryHandle::POSIX: - *memory = mmap(nullptr, bytes, PROT_READ | (read_only ? 0 : PROT_WRITE), - MAP_SHARED, file_descriptor_.fd, offset); - return *memory != MAP_FAILED; - case SharedMemoryHandle::MACH: - kern_return_t kr = mach_vm_map( - mach_task_self(), - reinterpret_cast(memory), // Output parameter - bytes, - 0, // Alignment mask - VM_FLAGS_ANYWHERE, - memory_object_, - offset, - FALSE, // Copy - VM_PROT_READ | (read_only ? 0 : VM_PROT_WRITE), // Current protection - VM_PROT_WRITE | VM_PROT_READ | VM_PROT_IS_MASK, // Maximum protection - VM_INHERIT_NONE); - return kr == KERN_SUCCESS; - } -} - -void SharedMemoryHandle::Close() const { - if (!IsValid()) - return; - - switch (type_) { - case POSIX: - if (IGNORE_EINTR(close(file_descriptor_.fd)) < 0) - DPLOG(ERROR) << "Error closing fd"; - break; - case MACH: - kern_return_t kr = mach_port_deallocate(mach_task_self(), memory_object_); - if (kr != KERN_SUCCESS) - MACH_DLOG(ERROR, kr) << "Error deallocating mach port"; - break; - } -} - -void SharedMemoryHandle::SetOwnershipPassesToIPC(bool ownership_passes) { - DCHECK_EQ(type_, MACH); - ownership_passes_to_ipc_ = ownership_passes; -} - -bool SharedMemoryHandle::OwnershipPassesToIPC() const { - DCHECK_EQ(type_, MACH); - return ownership_passes_to_ipc_; -} - -} // namespace base diff --git a/memory/shared_memory_handle_posix.cc b/memory/shared_memory_handle_posix.cc deleted file mode 100644 index 09dfb9ca8..000000000 --- a/memory/shared_memory_handle_posix.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory_handle.h" - -#include - -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "base/unguessable_token.h" - -namespace base { - -SharedMemoryHandle::SharedMemoryHandle() = default; - -SharedMemoryHandle::SharedMemoryHandle( - const base::FileDescriptor& file_descriptor, - size_t size, - const base::UnguessableToken& guid) - : file_descriptor_(file_descriptor), guid_(guid), size_(size) {} - -// static -SharedMemoryHandle SharedMemoryHandle::ImportHandle(int fd, size_t size) { - SharedMemoryHandle handle; - handle.file_descriptor_.fd = fd; - handle.file_descriptor_.auto_close = false; - handle.guid_ = UnguessableToken::Create(); - handle.size_ = size; - return handle; -} - -int SharedMemoryHandle::GetHandle() const { - return file_descriptor_.fd; -} - -bool SharedMemoryHandle::IsValid() const { - return file_descriptor_.fd >= 0; -} - -void SharedMemoryHandle::Close() const { - if (IGNORE_EINTR(close(file_descriptor_.fd)) < 0) - PLOG(ERROR) << "close"; -} - -int SharedMemoryHandle::Release() { - int old_fd = file_descriptor_.fd; - file_descriptor_.fd = -1; - return old_fd; -} - -SharedMemoryHandle SharedMemoryHandle::Duplicate() const { - if (!IsValid()) - return SharedMemoryHandle(); - - int duped_handle = HANDLE_EINTR(dup(file_descriptor_.fd)); - if (duped_handle < 0) - return SharedMemoryHandle(); - return SharedMemoryHandle(FileDescriptor(duped_handle, true), GetSize(), - GetGUID()); -} - -void SharedMemoryHandle::SetOwnershipPassesToIPC(bool ownership_passes) { - file_descriptor_.auto_close = ownership_passes; -} - -bool SharedMemoryHandle::OwnershipPassesToIPC() const { - return file_descriptor_.auto_close; -} - -} // namespace base diff --git a/memory/shared_memory_handle_win.cc b/memory/shared_memory_handle_win.cc deleted file mode 100644 index 8c11d39c8..000000000 --- a/memory/shared_memory_handle_win.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory_handle.h" - -#include "base/logging.h" -#include "base/unguessable_token.h" - -#include - -namespace base { - -SharedMemoryHandle::SharedMemoryHandle() {} - -SharedMemoryHandle::SharedMemoryHandle(HANDLE h, - size_t size, - const base::UnguessableToken& guid) - : handle_(h), guid_(guid), size_(size) {} - -void SharedMemoryHandle::Close() const { - DCHECK(handle_ != nullptr); - ::CloseHandle(handle_); -} - -bool SharedMemoryHandle::IsValid() const { - return handle_ != nullptr; -} - -SharedMemoryHandle SharedMemoryHandle::Duplicate() const { - HANDLE duped_handle; - ProcessHandle process = GetCurrentProcess(); - BOOL success = ::DuplicateHandle(process, handle_, process, &duped_handle, 0, - FALSE, DUPLICATE_SAME_ACCESS); - if (!success) - return SharedMemoryHandle(); - - base::SharedMemoryHandle handle(duped_handle, GetSize(), GetGUID()); - handle.SetOwnershipPassesToIPC(true); - return handle; -} - -HANDLE SharedMemoryHandle::GetHandle() const { - return handle_; -} - -void SharedMemoryHandle::SetOwnershipPassesToIPC(bool ownership_passes) { - ownership_passes_to_ipc_ = ownership_passes; -} - -bool SharedMemoryHandle::OwnershipPassesToIPC() const { - return ownership_passes_to_ipc_; -} - -} // namespace base diff --git a/memory/shared_memory_helper.cc b/memory/shared_memory_helper.cc deleted file mode 100644 index f98b734fb..000000000 --- a/memory/shared_memory_helper.cc +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory_helper.h" - -#if defined(OS_CHROMEOS) -#include -#include - -#include "base/debug/alias.h" -#endif // defined(OS_CHROMEOS) - -#include "base/threading/thread_restrictions.h" - -namespace base { - -struct ScopedPathUnlinkerTraits { - static const FilePath* InvalidValue() { return nullptr; } - - static void Free(const FilePath* path) { - if (unlink(path->value().c_str())) - PLOG(WARNING) << "unlink"; - } -}; - -// Unlinks the FilePath when the object is destroyed. -using ScopedPathUnlinker = - ScopedGeneric; - -#if !defined(OS_ANDROID) -bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options, - ScopedFD* fd, - ScopedFD* readonly_fd, - FilePath* path) { -#if defined(OS_LINUX) - // It doesn't make sense to have a open-existing private piece of shmem - DCHECK(!options.open_existing_deprecated); -#endif // defined(OS_LINUX) - // Q: Why not use the shm_open() etc. APIs? - // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU - FilePath directory; - ScopedPathUnlinker path_unlinker; - if (!GetShmemTempDir(options.executable, &directory)) - return false; - - fd->reset(base::CreateAndOpenFdForTemporaryFileInDir(directory, path)); - - if (!fd->is_valid()) - 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 - // closed, it is truly freed). - path_unlinker.reset(path); - - if (options.share_read_only) { - // Also open as readonly so that we can GetReadOnlyHandle. - 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"; - fd->reset(); - return false; - } - } - return true; -} - -bool PrepareMapFile(ScopedFD fd, - ScopedFD readonly_fd, - int* mapped_file, - int* readonly_mapped_file) { - DCHECK_EQ(-1, *mapped_file); - DCHECK_EQ(-1, *readonly_mapped_file); - if (!fd.is_valid()) - return false; - - // This function theoretically can block on the disk, but realistically - // the temporary files we create will just go into the buffer cache - // and be deleted before they ever make it out to disk. - base::ThreadRestrictions::ScopedAllowIO allow_io; - - if (readonly_fd.is_valid()) { - struct stat st = {}; - if (fstat(fd.get(), &st)) - NOTREACHED(); - - struct stat readonly_st = {}; - if (fstat(readonly_fd.get(), &readonly_st)) - NOTREACHED(); - if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) { - LOG(ERROR) << "writable and read-only inodes don't match; bailing"; - return false; - } - } - - *mapped_file = HANDLE_EINTR(dup(fd.get())); - if (*mapped_file == -1) { - NOTREACHED() << "Call to dup failed, errno=" << errno; - -#if defined(OS_CHROMEOS) - if (errno == EMFILE) { - // We're out of file descriptors and are probably about to crash somewhere - // else in Chrome anyway. Let's collect what FD information we can and - // crash. - // Added for debugging crbug.com/733718 - int original_fd_limit = 16384; - struct rlimit rlim; - if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { - original_fd_limit = rlim.rlim_cur; - if (rlim.rlim_max > rlim.rlim_cur) { - // Increase fd limit so breakpad has a chance to write a minidump. - rlim.rlim_cur = rlim.rlim_max; - if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { - PLOG(ERROR) << "setrlimit() failed"; - } - } - } else { - PLOG(ERROR) << "getrlimit() failed"; - } - - const char kFileDataMarker[] = "FDATA"; - char buf[PATH_MAX]; - char fd_path[PATH_MAX]; - char crash_buffer[32 * 1024] = {0}; - char* crash_ptr = crash_buffer; - base::debug::Alias(crash_buffer); - - // Put a marker at the start of our data so we can confirm where it - // begins. - crash_ptr = strncpy(crash_ptr, kFileDataMarker, strlen(kFileDataMarker)); - for (int i = original_fd_limit; i >= 0; --i) { - memset(buf, 0, arraysize(buf)); - memset(fd_path, 0, arraysize(fd_path)); - snprintf(fd_path, arraysize(fd_path) - 1, "/proc/self/fd/%d", i); - ssize_t count = readlink(fd_path, buf, arraysize(buf) - 1); - if (count < 0) { - PLOG(ERROR) << "readlink failed for: " << fd_path; - continue; - } - - if (crash_ptr + count + 1 < crash_buffer + arraysize(crash_buffer)) { - crash_ptr = strncpy(crash_ptr, buf, count + 1); - } - LOG(ERROR) << i << ": " << buf; - } - LOG(FATAL) << "Logged for file descriptor exhaustion, crashing now"; - } -#endif // defined(OS_CHROMEOS) - } - *readonly_mapped_file = readonly_fd.release(); - - return true; -} -#endif // !defined(OS_ANDROID) - -} // namespace base diff --git a/memory/shared_memory_helper.h b/memory/shared_memory_helper.h deleted file mode 100644 index 2c24f869f..000000000 --- a/memory/shared_memory_helper.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_SHARED_MEMORY_HELPER_H_ -#define BASE_MEMORY_SHARED_MEMORY_HELPER_H_ - -#include "base/memory/shared_memory.h" -#include "build/build_config.h" - -#include - -namespace base { - -#if !defined(OS_ANDROID) -// Makes a temporary file, fdopens it, and then unlinks it. |fd| is populated -// with the opened fd. |readonly_fd| is populated with the opened fd if -// options.share_read_only is true. |path| is populated with the location of -// the file before it was unlinked. -// Returns false if there's an unhandled failure. -bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options, - ScopedFD* fd, - ScopedFD* readonly_fd, - FilePath* path); - -// Takes the outputs of CreateAnonymousSharedMemory and maps them properly to -// |mapped_file| or |readonly_mapped_file|, depending on which one is populated. -bool PrepareMapFile(ScopedFD fd, - ScopedFD readonly_fd, - int* mapped_file, - int* readonly_mapped_file); -#endif // !defined(OS_ANDROID) - -} // namespace base - -#endif // BASE_MEMORY_SHARED_MEMORY_HELPER_H_ diff --git a/memory/shared_memory_mac.cc b/memory/shared_memory_mac.cc deleted file mode 100644 index 0a233e5fb..000000000 --- a/memory/shared_memory_mac.cc +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory.h" - -#include -#include -#include -#include -#include -#include - -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/logging.h" -#include "base/mac/foundation_util.h" -#include "base/mac/mac_util.h" -#include "base/mac/scoped_mach_vm.h" -#include "base/memory/shared_memory_helper.h" -#include "base/memory/shared_memory_tracker.h" -#include "base/metrics/field_trial.h" -#include "base/metrics/histogram_macros.h" -#include "base/posix/eintr_wrapper.h" -#include "base/posix/safe_strerror.h" -#include "base/process/process_metrics.h" -#include "base/scoped_generic.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_restrictions.h" -#include "base/unguessable_token.h" -#include "build/build_config.h" - -#if defined(OS_IOS) -#error "MacOS only - iOS uses shared_memory_posix.cc" -#endif - -namespace base { - -namespace { - -// Returns whether the operation succeeded. -// |new_handle| is an output variable, populated on success. The caller takes -// ownership of the underlying memory object. -// |handle| is the handle to copy. -// If |handle| is already mapped, |mapped_addr| is its mapped location. -// Otherwise, |mapped_addr| should be |nullptr|. -bool MakeMachSharedMemoryHandleReadOnly(SharedMemoryHandle* new_handle, - SharedMemoryHandle handle, - void* mapped_addr) { - if (!handle.IsValid()) - return false; - - size_t size = handle.GetSize(); - - // Map if necessary. - void* temp_addr = mapped_addr; - base::mac::ScopedMachVM scoper; - if (!temp_addr) { - // Intentionally lower current prot and max prot to |VM_PROT_READ|. - kern_return_t kr = mach_vm_map( - mach_task_self(), reinterpret_cast(&temp_addr), - size, 0, VM_FLAGS_ANYWHERE, handle.GetMemoryObject(), 0, FALSE, - VM_PROT_READ, VM_PROT_READ, VM_INHERIT_NONE); - if (kr != KERN_SUCCESS) - return false; - scoper.reset(reinterpret_cast(temp_addr), - mach_vm_round_page(size)); - } - - // Make new memory object. - mach_port_t named_right; - kern_return_t kr = mach_make_memory_entry_64( - mach_task_self(), reinterpret_cast(&size), - reinterpret_cast(temp_addr), VM_PROT_READ, - &named_right, MACH_PORT_NULL); - if (kr != KERN_SUCCESS) - return false; - - *new_handle = SharedMemoryHandle(named_right, size, handle.GetGUID()); - return true; -} - -} // namespace - -SharedMemory::SharedMemory() {} - -SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) - : mapped_memory_mechanism_(SharedMemoryHandle::POSIX), - shm_(handle), - read_only_(read_only) {} - -SharedMemory::~SharedMemory() { - Unmap(); - Close(); -} - -// static -bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { - return handle.IsValid(); -} - -// static -void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { - handle.Close(); -} - -// static -size_t SharedMemory::GetHandleLimit() { - return GetMaxFds(); -} - -// static -SharedMemoryHandle SharedMemory::DuplicateHandle( - const SharedMemoryHandle& handle) { - return handle.Duplicate(); -} - -// static -int SharedMemory::GetFdFromSharedMemoryHandle( - const SharedMemoryHandle& handle) { - return handle.file_descriptor_.fd; -} - -bool SharedMemory::CreateAndMapAnonymous(size_t size) { - return CreateAnonymous(size) && Map(size); -} - -// Chromium mostly only uses the unique/private shmem as specified by -// "name == L"". The exception is in the StatsTable. -bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { - DCHECK(!shm_.IsValid()); - if (options.size == 0) - return false; - - if (options.size > static_cast(std::numeric_limits::max())) - return false; - - if (options.type == SharedMemoryHandle::MACH) { - shm_ = SharedMemoryHandle(options.size, UnguessableToken::Create()); - requested_size_ = options.size; - return shm_.IsValid(); - } - - // This function theoretically can block on the disk. Both profiling of real - // users and local instrumentation shows that this is a real problem. - // https://code.google.com/p/chromium/issues/detail?id=466437 - ThreadRestrictions::ScopedAllowIO allow_io; - - ScopedFD fd; - ScopedFD readonly_fd; - - FilePath path; - bool result = CreateAnonymousSharedMemory(options, &fd, &readonly_fd, &path); - if (!result) - return false; - // Should be guaranteed by CreateAnonymousSharedMemory(). - DCHECK(fd.is_valid()); - - // Get current size. - struct stat stat; - if (fstat(fd.get(), &stat) != 0) - return false; - const size_t current_size = stat.st_size; - if (current_size != options.size) { - if (HANDLE_EINTR(ftruncate(fd.get(), options.size)) != 0) - return false; - } - requested_size_ = options.size; - - int mapped_file = -1; - int readonly_mapped_file = -1; - result = PrepareMapFile(std::move(fd), std::move(readonly_fd), &mapped_file, - &readonly_mapped_file); - shm_ = SharedMemoryHandle(FileDescriptor(mapped_file, false), options.size, - UnguessableToken::Create()); - readonly_shm_ = - SharedMemoryHandle(FileDescriptor(readonly_mapped_file, false), - options.size, shm_.GetGUID()); - return result; -} - -bool SharedMemory::MapAt(off_t offset, size_t bytes) { - if (!shm_.IsValid()) - return false; - if (bytes > static_cast(std::numeric_limits::max())) - return false; - if (memory_) - return false; - - bool success = shm_.MapAt(offset, bytes, &memory_, read_only_); - if (success) { - mapped_size_ = bytes; - DCHECK_EQ(0U, reinterpret_cast(memory_) & - (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1)); - mapped_memory_mechanism_ = shm_.type_; - mapped_id_ = shm_.GetGUID(); - SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); - } else { - memory_ = nullptr; - } - - return success; -} - -bool SharedMemory::Unmap() { - if (!memory_) - return false; - - SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); - switch (mapped_memory_mechanism_) { - case SharedMemoryHandle::POSIX: - munmap(memory_, mapped_size_); - break; - case SharedMemoryHandle::MACH: - mach_vm_deallocate(mach_task_self(), - reinterpret_cast(memory_), - mapped_size_); - break; - } - memory_ = nullptr; - mapped_size_ = 0; - mapped_id_ = UnguessableToken(); - return true; -} - -SharedMemoryHandle SharedMemory::handle() const { - return shm_; -} - -SharedMemoryHandle SharedMemory::TakeHandle() { - SharedMemoryHandle dup = DuplicateHandle(handle()); - Unmap(); - Close(); - return dup; -} - -void SharedMemory::Close() { - shm_.Close(); - shm_ = SharedMemoryHandle(); - if (shm_.type_ == SharedMemoryHandle::POSIX) { - if (readonly_shm_.IsValid()) { - readonly_shm_.Close(); - readonly_shm_ = SharedMemoryHandle(); - } - } -} - -SharedMemoryHandle SharedMemory::GetReadOnlyHandle() const { - if (shm_.type_ == SharedMemoryHandle::POSIX) { - // We could imagine re-opening the file from /dev/fd, but that can't make it - // readonly on Mac: https://codereview.chromium.org/27265002/#msg10. - CHECK(readonly_shm_.IsValid()); - return readonly_shm_.Duplicate(); - } - - DCHECK(shm_.IsValid()); - SharedMemoryHandle new_handle; - bool success = MakeMachSharedMemoryHandleReadOnly(&new_handle, shm_, memory_); - if (success) - new_handle.SetOwnershipPassesToIPC(true); - return new_handle; -} - -} // namespace base diff --git a/memory/shared_memory_mac_unittest.cc b/memory/shared_memory_mac_unittest.cc deleted file mode 100644 index b17dab778..000000000 --- a/memory/shared_memory_mac_unittest.cc +++ /dev/null @@ -1,457 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include -#include -#include -#include - -#include "base/command_line.h" -#include "base/mac/mac_util.h" -#include "base/mac/mach_logging.h" -#include "base/mac/scoped_mach_port.h" -#include "base/macros.h" -#include "base/memory/shared_memory.h" -#include "base/process/process_handle.h" -#include "base/rand_util.h" -#include "base/strings/stringprintf.h" -#include "base/sys_info.h" -#include "base/test/multiprocess_test.h" -#include "base/test/test_timeouts.h" -#include "base/unguessable_token.h" -#include "testing/multiprocess_func_list.h" - -namespace base { - -namespace { - -// Gets the current and maximum protection levels of the memory region. -// Returns whether the operation was successful. -// |current| and |max| are output variables only populated on success. -bool GetProtections(void* address, size_t size, int* current, int* max) { - vm_region_info_t region_info; - mach_vm_address_t mem_address = reinterpret_cast(address); - mach_vm_size_t mem_size = size; - vm_region_basic_info_64 basic_info; - - region_info = reinterpret_cast(&basic_info); - vm_region_flavor_t flavor = VM_REGION_BASIC_INFO_64; - memory_object_name_t memory_object; - mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; - - kern_return_t kr = - mach_vm_region(mach_task_self(), &mem_address, &mem_size, flavor, - region_info, &count, &memory_object); - if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "Failed to get region info."; - return false; - } - - *current = basic_info.protection; - *max = basic_info.max_protection; - return true; -} - -// Creates a new SharedMemory with the given |size|, filled with 'a'. -std::unique_ptr CreateSharedMemory(int size) { - SharedMemoryHandle shm(size, UnguessableToken::Create()); - if (!shm.IsValid()) { - LOG(ERROR) << "Failed to make SharedMemoryHandle"; - return nullptr; - } - std::unique_ptr shared_memory(new SharedMemory(shm, false)); - shared_memory->Map(size); - memset(shared_memory->memory(), 'a', size); - return shared_memory; -} - -static const std::string g_service_switch_name = "service_name"; - -// Structs used to pass a mach port from client to server. -struct MachSendPortMessage { - mach_msg_header_t header; - mach_msg_body_t body; - mach_msg_port_descriptor_t data; -}; -struct MachReceivePortMessage { - mach_msg_header_t header; - mach_msg_body_t body; - mach_msg_port_descriptor_t data; - mach_msg_trailer_t trailer; -}; - -// Makes the current process into a Mach Server with the given |service_name|. -mach_port_t BecomeMachServer(const char* service_name) { - mach_port_t port; - kern_return_t kr = bootstrap_check_in(bootstrap_port, service_name, &port); - MACH_CHECK(kr == KERN_SUCCESS, kr) << "BecomeMachServer"; - return port; -} - -// Returns the mach port for the Mach Server with the given |service_name|. -mach_port_t LookupServer(const char* service_name) { - mach_port_t server_port; - kern_return_t kr = - bootstrap_look_up(bootstrap_port, service_name, &server_port); - MACH_CHECK(kr == KERN_SUCCESS, kr) << "LookupServer"; - return server_port; -} - -mach_port_t MakeReceivingPort() { - mach_port_t client_port; - kern_return_t kr = - mach_port_allocate(mach_task_self(), // our task is acquiring - MACH_PORT_RIGHT_RECEIVE, // a new receive right - &client_port); // with this name - MACH_CHECK(kr == KERN_SUCCESS, kr) << "MakeReceivingPort"; - return client_port; -} - -// Blocks until a mach message is sent to |server_port|. This mach message -// must contain a mach port. Returns that mach port. -mach_port_t ReceiveMachPort(mach_port_t port_to_listen_on) { - MachReceivePortMessage recv_msg; - mach_msg_header_t* recv_hdr = &(recv_msg.header); - recv_hdr->msgh_local_port = port_to_listen_on; - recv_hdr->msgh_size = sizeof(recv_msg); - kern_return_t kr = - mach_msg(recv_hdr, // message buffer - MACH_RCV_MSG, // option indicating service - 0, // send size - recv_hdr->msgh_size, // size of header + body - port_to_listen_on, // receive name - MACH_MSG_TIMEOUT_NONE, // no timeout, wait forever - MACH_PORT_NULL); // no notification port - MACH_CHECK(kr == KERN_SUCCESS, kr) << "ReceiveMachPort"; - mach_port_t other_task_port = recv_msg.data.name; - return other_task_port; -} - -// Passes a copy of the send right of |port_to_send| to |receiving_port|. -void SendMachPort(mach_port_t receiving_port, - mach_port_t port_to_send, - int disposition) { - MachSendPortMessage send_msg; - mach_msg_header_t* send_hdr; - send_hdr = &(send_msg.header); - send_hdr->msgh_bits = - MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX; - send_hdr->msgh_size = sizeof(send_msg); - send_hdr->msgh_remote_port = receiving_port; - send_hdr->msgh_local_port = MACH_PORT_NULL; - send_hdr->msgh_reserved = 0; - send_hdr->msgh_id = 0; - send_msg.body.msgh_descriptor_count = 1; - send_msg.data.name = port_to_send; - send_msg.data.disposition = disposition; - send_msg.data.type = MACH_MSG_PORT_DESCRIPTOR; - int kr = mach_msg(send_hdr, // message buffer - MACH_SEND_MSG, // option indicating send - send_hdr->msgh_size, // size of header + body - 0, // receive limit - MACH_PORT_NULL, // receive name - MACH_MSG_TIMEOUT_NONE, // no timeout, wait forever - MACH_PORT_NULL); // no notification port - MACH_CHECK(kr == KERN_SUCCESS, kr) << "SendMachPort"; -} - -std::string CreateRandomServiceName() { - return StringPrintf("SharedMemoryMacMultiProcessTest.%llu", RandUint64()); -} - -// Sets up the mach communication ports with the server. Returns a port to which -// the server will send mach objects. -mach_port_t CommonChildProcessSetUp() { - CommandLine cmd_line = *CommandLine::ForCurrentProcess(); - std::string service_name = - cmd_line.GetSwitchValueASCII(g_service_switch_name); - mac::ScopedMachSendRight server_port(LookupServer(service_name.c_str())); - mach_port_t client_port = MakeReceivingPort(); - - // Send the port that this process is listening on to the server. - SendMachPort(server_port.get(), client_port, MACH_MSG_TYPE_MAKE_SEND); - return client_port; -} - -// The number of active names in the current task's port name space. -mach_msg_type_number_t GetActiveNameCount() { - mach_port_name_array_t name_array; - mach_msg_type_number_t names_count; - mach_port_type_array_t type_array; - mach_msg_type_number_t types_count; - kern_return_t kr = mach_port_names(mach_task_self(), &name_array, - &names_count, &type_array, &types_count); - MACH_CHECK(kr == KERN_SUCCESS, kr) << "GetActiveNameCount"; - return names_count; -} - -} // namespace - -class SharedMemoryMacMultiProcessTest : public MultiProcessTest { - public: - SharedMemoryMacMultiProcessTest() {} - - CommandLine MakeCmdLine(const std::string& procname) override { - CommandLine command_line = MultiProcessTest::MakeCmdLine(procname); - // Pass the service name to the child process. - command_line.AppendSwitchASCII(g_service_switch_name, service_name_); - return command_line; - } - - void SetUpChild(const std::string& name) { - // Make a random service name so that this test doesn't conflict with other - // similar tests. - service_name_ = CreateRandomServiceName(); - server_port_.reset(BecomeMachServer(service_name_.c_str())); - child_process_ = SpawnChild(name); - client_port_.reset(ReceiveMachPort(server_port_.get())); - } - - static const int s_memory_size = 99999; - - protected: - std::string service_name_; - - // A port on which the main process listens for mach messages from the child - // process. - mac::ScopedMachReceiveRight server_port_; - - // A port on which the child process listens for mach messages from the main - // process. - mac::ScopedMachSendRight client_port_; - - base::Process child_process_; - DISALLOW_COPY_AND_ASSIGN(SharedMemoryMacMultiProcessTest); -}; - -// Tests that content written to shared memory in the server process can be read -// by the child process. -TEST_F(SharedMemoryMacMultiProcessTest, MachBasedSharedMemory) { - SetUpChild("MachBasedSharedMemoryClient"); - - std::unique_ptr shared_memory( - CreateSharedMemory(s_memory_size)); - - // Send the underlying memory object to the client process. - SendMachPort(client_port_.get(), shared_memory->handle().GetMemoryObject(), - MACH_MSG_TYPE_COPY_SEND); - int rv = -1; - ASSERT_TRUE(child_process_.WaitForExitWithTimeout( - TestTimeouts::action_timeout(), &rv)); - EXPECT_EQ(0, rv); -} - -MULTIPROCESS_TEST_MAIN(MachBasedSharedMemoryClient) { - mac::ScopedMachReceiveRight client_port(CommonChildProcessSetUp()); - // The next mach port should be for a memory object. - mach_port_t memory_object = ReceiveMachPort(client_port.get()); - SharedMemoryHandle shm(memory_object, - SharedMemoryMacMultiProcessTest::s_memory_size, - UnguessableToken::Create()); - SharedMemory shared_memory(shm, false); - shared_memory.Map(SharedMemoryMacMultiProcessTest::s_memory_size); - const char* start = static_cast(shared_memory.memory()); - for (int i = 0; i < SharedMemoryMacMultiProcessTest::s_memory_size; ++i) { - DCHECK_EQ(start[i], 'a'); - } - return 0; -} - -// Tests that mapping shared memory with an offset works correctly. -TEST_F(SharedMemoryMacMultiProcessTest, MachBasedSharedMemoryWithOffset) { - SetUpChild("MachBasedSharedMemoryWithOffsetClient"); - - SharedMemoryHandle shm(s_memory_size, UnguessableToken::Create()); - ASSERT_TRUE(shm.IsValid()); - SharedMemory shared_memory(shm, false); - shared_memory.Map(s_memory_size); - - size_t page_size = SysInfo::VMAllocationGranularity(); - char* start = static_cast(shared_memory.memory()); - memset(start, 'a', page_size); - memset(start + page_size, 'b', page_size); - memset(start + 2 * page_size, 'c', page_size); - - // Send the underlying memory object to the client process. - SendMachPort( - client_port_.get(), shm.GetMemoryObject(), MACH_MSG_TYPE_COPY_SEND); - int rv = -1; - ASSERT_TRUE(child_process_.WaitForExitWithTimeout( - TestTimeouts::action_timeout(), &rv)); - EXPECT_EQ(0, rv); -} - -MULTIPROCESS_TEST_MAIN(MachBasedSharedMemoryWithOffsetClient) { - mac::ScopedMachReceiveRight client_port(CommonChildProcessSetUp()); - // The next mach port should be for a memory object. - mach_port_t memory_object = ReceiveMachPort(client_port.get()); - SharedMemoryHandle shm(memory_object, - SharedMemoryMacMultiProcessTest::s_memory_size, - UnguessableToken::Create()); - SharedMemory shared_memory(shm, false); - size_t page_size = SysInfo::VMAllocationGranularity(); - shared_memory.MapAt(page_size, 2 * page_size); - const char* start = static_cast(shared_memory.memory()); - for (size_t i = 0; i < page_size; ++i) { - DCHECK_EQ(start[i], 'b'); - } - for (size_t i = page_size; i < 2 * page_size; ++i) { - DCHECK_EQ(start[i], 'c'); - } - return 0; -} - -// Tests that duplication and closing has the right effect on Mach reference -// counts. -TEST_F(SharedMemoryMacMultiProcessTest, MachDuplicateAndClose) { - mach_msg_type_number_t active_name_count = GetActiveNameCount(); - - // Making a new SharedMemoryHandle increments the name count. - SharedMemoryHandle shm(s_memory_size, UnguessableToken::Create()); - ASSERT_TRUE(shm.IsValid()); - EXPECT_EQ(active_name_count + 1, GetActiveNameCount()); - - // Duplicating the SharedMemoryHandle increments the ref count, but doesn't - // make a new name. - shm.Duplicate(); - EXPECT_EQ(active_name_count + 1, GetActiveNameCount()); - - // Closing the SharedMemoryHandle decrements the ref count. The first time has - // no effect. - shm.Close(); - EXPECT_EQ(active_name_count + 1, GetActiveNameCount()); - - // Closing the SharedMemoryHandle decrements the ref count. The second time - // destroys the port. - shm.Close(); - EXPECT_EQ(active_name_count, GetActiveNameCount()); -} - -// Tests that Mach shared memory can be mapped and unmapped. -TEST_F(SharedMemoryMacMultiProcessTest, MachUnmapMap) { - mach_msg_type_number_t active_name_count = GetActiveNameCount(); - - std::unique_ptr shared_memory = - CreateSharedMemory(s_memory_size); - ASSERT_TRUE(shared_memory->Unmap()); - ASSERT_TRUE(shared_memory->Map(s_memory_size)); - shared_memory.reset(); - EXPECT_EQ(active_name_count, GetActiveNameCount()); -} - -// Tests that passing a SharedMemoryHandle to a SharedMemory object also passes -// ownership, and that destroying the SharedMemory closes the SharedMemoryHandle -// as well. -TEST_F(SharedMemoryMacMultiProcessTest, MachSharedMemoryTakesOwnership) { - mach_msg_type_number_t active_name_count = GetActiveNameCount(); - - // Making a new SharedMemoryHandle increments the name count. - SharedMemoryHandle shm(s_memory_size, UnguessableToken::Create()); - ASSERT_TRUE(shm.IsValid()); - EXPECT_EQ(active_name_count + 1, GetActiveNameCount()); - - // Name count doesn't change when mapping the memory. - std::unique_ptr shared_memory(new SharedMemory(shm, false)); - shared_memory->Map(s_memory_size); - EXPECT_EQ(active_name_count + 1, GetActiveNameCount()); - - // Destroying the SharedMemory object frees the resource. - shared_memory.reset(); - EXPECT_EQ(active_name_count, GetActiveNameCount()); -} - -// Tests that the read-only flag works. -TEST_F(SharedMemoryMacMultiProcessTest, MachReadOnly) { - std::unique_ptr shared_memory( - CreateSharedMemory(s_memory_size)); - - SharedMemoryHandle shm2 = shared_memory->handle().Duplicate(); - ASSERT_TRUE(shm2.IsValid()); - SharedMemory shared_memory2(shm2, true); - shared_memory2.Map(s_memory_size); - ASSERT_DEATH(memset(shared_memory2.memory(), 'b', s_memory_size), ""); -} - -// Tests that duplication of the underlying handle works. -TEST_F(SharedMemoryMacMultiProcessTest, MachDuplicate) { - mach_msg_type_number_t active_name_count = GetActiveNameCount(); - - { - std::unique_ptr shared_memory( - CreateSharedMemory(s_memory_size)); - - SharedMemoryHandle shm2 = shared_memory->handle().Duplicate(); - ASSERT_TRUE(shm2.IsValid()); - SharedMemory shared_memory2(shm2, true); - shared_memory2.Map(s_memory_size); - - ASSERT_EQ(0, memcmp(shared_memory->memory(), shared_memory2.memory(), - s_memory_size)); - } - - EXPECT_EQ(active_name_count, GetActiveNameCount()); -} - -// Tests that the method GetReadOnlyHandle() creates a memory object that -// is read only. -TEST_F(SharedMemoryMacMultiProcessTest, MachReadonly) { - std::unique_ptr shared_memory( - CreateSharedMemory(s_memory_size)); - - // Check the protection levels. - int current_prot, max_prot; - ASSERT_TRUE(GetProtections(shared_memory->memory(), - shared_memory->mapped_size(), ¤t_prot, - &max_prot)); - ASSERT_EQ(VM_PROT_READ | VM_PROT_WRITE, current_prot); - ASSERT_EQ(VM_PROT_READ | VM_PROT_WRITE, max_prot); - - // Make a new memory object. - SharedMemoryHandle shm2 = shared_memory->GetReadOnlyHandle(); - ASSERT_TRUE(shm2.IsValid()); - EXPECT_EQ(shared_memory->handle().GetGUID(), shm2.GetGUID()); - - // Mapping with |readonly| set to |false| should fail. - SharedMemory shared_memory2(shm2, false); - shared_memory2.Map(s_memory_size); - ASSERT_EQ(nullptr, shared_memory2.memory()); - - // Now trying mapping with |readonly| set to |true|. - SharedMemory shared_memory3(shm2.Duplicate(), true); - shared_memory3.Map(s_memory_size); - ASSERT_NE(nullptr, shared_memory3.memory()); - - // Check the protection levels. - ASSERT_TRUE(GetProtections(shared_memory3.memory(), - shared_memory3.mapped_size(), ¤t_prot, - &max_prot)); - ASSERT_EQ(VM_PROT_READ, current_prot); - ASSERT_EQ(VM_PROT_READ, max_prot); - - // The memory should still be readonly, since the underlying memory object - // is readonly. - ASSERT_DEATH(memset(shared_memory2.memory(), 'b', s_memory_size), ""); -} - -// Tests that the method GetReadOnlyHandle() doesn't leak. -TEST_F(SharedMemoryMacMultiProcessTest, MachReadonlyLeak) { - mach_msg_type_number_t active_name_count = GetActiveNameCount(); - - { - std::unique_ptr shared_memory( - CreateSharedMemory(s_memory_size)); - - SharedMemoryHandle shm2 = shared_memory->GetReadOnlyHandle(); - ASSERT_TRUE(shm2.IsValid()); - - // Intentionally map with |readonly| set to |false|. - SharedMemory shared_memory2(shm2, false); - shared_memory2.Map(s_memory_size); - } - - EXPECT_EQ(active_name_count, GetActiveNameCount()); -} - -} // namespace base diff --git a/memory/shared_memory_mapping.cc b/memory/shared_memory_mapping.cc deleted file mode 100644 index 005e3fcc3..000000000 --- a/memory/shared_memory_mapping.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory_mapping.h" - -#include - -#include "base/logging.h" -#include "base/memory/shared_memory_tracker.h" -#include "base/unguessable_token.h" -#include "build/build_config.h" - -#if defined(OS_POSIX) -#include -#endif - -#if defined(OS_WIN) -#include -#endif - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#include -#include "base/mac/mach_logging.h" -#endif - -#if defined(OS_FUCHSIA) -#include -#include -#include -#endif - -namespace base { - -SharedMemoryMapping::SharedMemoryMapping() = default; - -SharedMemoryMapping::SharedMemoryMapping(SharedMemoryMapping&& mapping) - : memory_(mapping.memory_), - size_(mapping.size_), - mapped_size_(mapping.mapped_size_), - guid_(mapping.guid_) { - mapping.memory_ = nullptr; -} - -SharedMemoryMapping& SharedMemoryMapping::operator=( - SharedMemoryMapping&& mapping) { - Unmap(); - memory_ = mapping.memory_; - size_ = mapping.size_; - mapped_size_ = mapping.mapped_size_; - guid_ = mapping.guid_; - mapping.memory_ = nullptr; - return *this; -} - -SharedMemoryMapping::~SharedMemoryMapping() { - Unmap(); -} - -SharedMemoryMapping::SharedMemoryMapping(void* memory, - size_t size, - size_t mapped_size, - const UnguessableToken& guid) - : memory_(memory), size_(size), mapped_size_(mapped_size), guid_(guid) { - SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); -} - -void SharedMemoryMapping::Unmap() { - if (!IsValid()) - return; - - SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); -#if defined(OS_WIN) - if (!UnmapViewOfFile(memory_)) - DPLOG(ERROR) << "UnmapViewOfFile"; -#elif defined(OS_FUCHSIA) - uintptr_t addr = reinterpret_cast(memory_); - zx_status_t status = zx_vmar_unmap(zx_vmar_root_self(), addr, size_); - DLOG_IF(ERROR, status != ZX_OK) - << "zx_vmar_unmap failed: " << zx_status_get_string(status); -#elif defined(OS_MACOSX) && !defined(OS_IOS) - kern_return_t kr = mach_vm_deallocate( - mach_task_self(), reinterpret_cast(memory_), size_); - MACH_DLOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_vm_deallocate"; -#else - if (munmap(memory_, size_) < 0) - DPLOG(ERROR) << "munmap"; -#endif -} - -ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping() = default; -ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping( - ReadOnlySharedMemoryMapping&&) = default; -ReadOnlySharedMemoryMapping& ReadOnlySharedMemoryMapping::operator=( - ReadOnlySharedMemoryMapping&&) = default; -ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping( - void* address, - size_t size, - size_t mapped_size, - const UnguessableToken& guid) - : SharedMemoryMapping(address, size, mapped_size, guid) {} - -WritableSharedMemoryMapping::WritableSharedMemoryMapping() = default; -WritableSharedMemoryMapping::WritableSharedMemoryMapping( - WritableSharedMemoryMapping&&) = default; -WritableSharedMemoryMapping& WritableSharedMemoryMapping::operator=( - WritableSharedMemoryMapping&&) = default; -WritableSharedMemoryMapping::WritableSharedMemoryMapping( - void* address, - size_t size, - size_t mapped_size, - const UnguessableToken& guid) - : SharedMemoryMapping(address, size, mapped_size, guid) {} - -} // namespace base diff --git a/memory/shared_memory_mapping.h b/memory/shared_memory_mapping.h deleted file mode 100644 index ace4c1532..000000000 --- a/memory/shared_memory_mapping.h +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_SHARED_MEMORY_MAPPING_H_ -#define BASE_MEMORY_SHARED_MEMORY_MAPPING_H_ - -#include - -#include "base/macros.h" -#include "base/unguessable_token.h" - -namespace base { - -namespace subtle { -class PlatformSharedMemoryRegion; -} // namespace subtle - -// Base class for scoped handles to a shared memory mapping created from a -// shared memory region. Created shared memory mappings remain valid even if the -// creator region is transferred or destroyed. -// -// Each mapping has an UnguessableToken that identifies the shared memory region -// it was created from. This is used for memory metrics, to avoid overcounting -// shared memory. -class BASE_EXPORT SharedMemoryMapping { - public: - // Default constructor initializes an invalid instance. - SharedMemoryMapping(); - - // Move operations are allowed. - SharedMemoryMapping(SharedMemoryMapping&& mapping); - SharedMemoryMapping& operator=(SharedMemoryMapping&& mapping); - - // Unmaps the region if the mapping is valid. - virtual ~SharedMemoryMapping(); - - // Returns true iff the mapping is valid. False means there is no - // corresponding area of memory. - bool IsValid() const { return memory_ != nullptr; } - - // Returns the logical size of the mapping in bytes. This is precisely the - // size requested by whoever created the mapping, and it is always less than - // or equal to |mapped_size()|. This is undefined for invalid instances. - size_t size() const { - DCHECK(IsValid()); - return size_; - } - - // Returns the actual size of the mapping in bytes. This is always at least - // as large as |size()| but may be larger due to platform mapping alignment - // constraints. This is undefined for invalid instances. - size_t mapped_size() const { - DCHECK(IsValid()); - return mapped_size_; - } - - // Returns 128-bit GUID of the region this mapping belongs to. - const UnguessableToken& guid() const { - DCHECK(IsValid()); - return guid_; - } - - protected: - SharedMemoryMapping(void* address, - size_t size, - size_t mapped_size, - const UnguessableToken& guid); - void* raw_memory_ptr() const { return memory_; } - - private: - friend class SharedMemoryTracker; - - void Unmap(); - - void* memory_ = nullptr; - size_t size_ = 0; - size_t mapped_size_ = 0; - UnguessableToken guid_; - - DISALLOW_COPY_AND_ASSIGN(SharedMemoryMapping); -}; - -// Class modeling a read-only mapping of a shared memory region into the -// current process' address space. This is created by ReadOnlySharedMemoryRegion -// instances. -class BASE_EXPORT ReadOnlySharedMemoryMapping : public SharedMemoryMapping { - public: - // Default constructor initializes an invalid instance. - ReadOnlySharedMemoryMapping(); - - // Move operations are allowed. - ReadOnlySharedMemoryMapping(ReadOnlySharedMemoryMapping&&); - ReadOnlySharedMemoryMapping& operator=(ReadOnlySharedMemoryMapping&&); - - // Returns the base address of the mapping. This is read-only memory. This is - // page-aligned. This is nullptr for invalid instances. - const void* memory() const { return raw_memory_ptr(); } - - private: - friend class ReadOnlySharedMemoryRegion; - ReadOnlySharedMemoryMapping(void* address, - size_t size, - size_t mapped_size, - const UnguessableToken& guid); - - DISALLOW_COPY_AND_ASSIGN(ReadOnlySharedMemoryMapping); -}; - -// Class modeling a writable mapping of a shared memory region into the -// current process' address space. This is created by *SharedMemoryRegion -// instances. -class BASE_EXPORT WritableSharedMemoryMapping : public SharedMemoryMapping { - public: - // Default constructor initializes an invalid instance. - WritableSharedMemoryMapping(); - - // Move operations are allowed. - WritableSharedMemoryMapping(WritableSharedMemoryMapping&&); - WritableSharedMemoryMapping& operator=(WritableSharedMemoryMapping&&); - - // Returns the base address of the mapping. This is writable memory. This is - // page-aligned. This is nullptr for invalid instances. - void* memory() const { return raw_memory_ptr(); } - - private: - friend WritableSharedMemoryMapping MapAtForTesting( - subtle::PlatformSharedMemoryRegion* region, - off_t offset, - size_t size); - friend class ReadOnlySharedMemoryRegion; - friend class WritableSharedMemoryRegion; - friend class UnsafeSharedMemoryRegion; - WritableSharedMemoryMapping(void* address, - size_t size, - size_t mapped_size, - const UnguessableToken& guid); - - DISALLOW_COPY_AND_ASSIGN(WritableSharedMemoryMapping); -}; - -} // namespace base - -#endif // BASE_MEMORY_SHARED_MEMORY_MAPPING_H_ diff --git a/memory/shared_memory_nacl.cc b/memory/shared_memory_nacl.cc deleted file mode 100644 index 4bcbb547d..000000000 --- a/memory/shared_memory_nacl.cc +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory.h" - -#include -#include -#include -#include -#include -#include - -#include - -#include "base/logging.h" -#include "base/memory/shared_memory_tracker.h" - -namespace base { - -SharedMemory::SharedMemory() - : mapped_size_(0), memory_(NULL), read_only_(false), requested_size_(0) {} - -SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) - : shm_(handle), - mapped_size_(0), - memory_(NULL), - read_only_(read_only), - requested_size_(0) {} - -SharedMemory::~SharedMemory() { - Unmap(); - Close(); -} - -// static -bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { - return handle.IsValid(); -} - -// static -void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { - DCHECK(handle.IsValid()); - handle.Close(); -} - -// static -SharedMemoryHandle SharedMemory::DuplicateHandle( - const SharedMemoryHandle& handle) { - return handle.Duplicate(); -} - -bool SharedMemory::CreateAndMapAnonymous(size_t size) { - // Untrusted code can't create descriptors or handles. - return false; -} - -bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { - // Untrusted code can't create descriptors or handles. - return false; -} - -bool SharedMemory::Delete(const std::string& name) { - return false; -} - -bool SharedMemory::Open(const std::string& name, bool read_only) { - return false; -} - -bool SharedMemory::MapAt(off_t offset, size_t bytes) { - if (!shm_.IsValid()) - return false; - - if (bytes > static_cast(std::numeric_limits::max())) - return false; - - if (memory_) - return false; - - memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), - MAP_SHARED, shm_.GetHandle(), offset); - - bool mmap_succeeded = memory_ != MAP_FAILED && memory_ != NULL; - if (mmap_succeeded) { - mapped_size_ = bytes; - DCHECK_EQ(0U, reinterpret_cast(memory_) & - (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1)); - mapped_id_ = shm_.GetGUID(); - SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); - } else { - memory_ = NULL; - } - - return mmap_succeeded; -} - -bool SharedMemory::Unmap() { - if (memory_ == NULL) - return false; - - SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); - if (munmap(memory_, mapped_size_) < 0) - DPLOG(ERROR) << "munmap"; - memory_ = NULL; - mapped_size_ = 0; - mapped_id_ = UnguessableToken(); - return true; -} - -SharedMemoryHandle SharedMemory::handle() const { - SharedMemoryHandle handle_copy = shm_; - handle_copy.SetOwnershipPassesToIPC(false); - return handle_copy; -} - -SharedMemoryHandle SharedMemory::TakeHandle() { - SharedMemoryHandle handle_copy = shm_; - handle_copy.SetOwnershipPassesToIPC(true); - Unmap(); - shm_ = SharedMemoryHandle(); - return handle_copy; -} - -void SharedMemory::Close() { - if (shm_.IsValid()) { - shm_.Close(); - shm_ = SharedMemoryHandle(); - } -} - -SharedMemoryHandle SharedMemory::GetReadOnlyHandle() const { - // Untrusted code can't create descriptors or handles, which is needed to - // drop permissions. - return SharedMemoryHandle(); -} - -} // namespace base diff --git a/memory/shared_memory_posix.cc b/memory/shared_memory_posix.cc deleted file mode 100644 index e1289e7e1..000000000 --- a/memory/shared_memory_posix.cc +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory.h" - -#include -#include -#include -#include -#include -#include - -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/shared_memory_helper.h" -#include "base/memory/shared_memory_tracker.h" -#include "base/posix/eintr_wrapper.h" -#include "base/posix/safe_strerror.h" -#include "base/process/process_metrics.h" -#include "base/scoped_generic.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_restrictions.h" -#include "base/trace_event/trace_event.h" -#include "base/unguessable_token.h" -#include "build/build_config.h" - -#if defined(OS_ANDROID) -#include "base/os_compat_android.h" -#include "third_party/ashmem/ashmem.h" -#endif - -#if defined(OS_MACOSX) && !defined(OS_IOS) -#error "MacOS uses shared_memory_mac.cc" -#endif - -namespace base { - -SharedMemory::SharedMemory() = default; - -SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) - : shm_(handle), read_only_(read_only) {} - -SharedMemory::~SharedMemory() { - Unmap(); - Close(); -} - -// static -bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { - return handle.IsValid(); -} - -// static -void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { - DCHECK(handle.IsValid()); - handle.Close(); -} - -// static -size_t SharedMemory::GetHandleLimit() { - return GetMaxFds(); -} - -// static -SharedMemoryHandle SharedMemory::DuplicateHandle( - const SharedMemoryHandle& handle) { - return handle.Duplicate(); -} - -// static -int SharedMemory::GetFdFromSharedMemoryHandle( - const SharedMemoryHandle& handle) { - return handle.GetHandle(); -} - -bool SharedMemory::CreateAndMapAnonymous(size_t size) { - return CreateAnonymous(size) && Map(size); -} - -#if !defined(OS_ANDROID) - -// Chromium mostly only uses the unique/private shmem as specified by -// "name == L"". The exception is in the StatsTable. -// TODO(jrg): there is no way to "clean up" all unused named shmem if -// we restart from a crash. (That isn't a new problem, but it is a problem.) -// In case we want to delete it later, it may be useful to save the value -// of mem_filename after FilePathForMemoryName(). -bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { - DCHECK(!shm_.IsValid()); - if (options.size == 0) return false; - - if (options.size > static_cast(std::numeric_limits::max())) - return false; - - // This function theoretically can block on the disk, but realistically - // the temporary files we create will just go into the buffer cache - // and be deleted before they ever make it out to disk. - ThreadRestrictions::ScopedAllowIO allow_io; - - bool fix_size = true; - ScopedFD fd; - ScopedFD readonly_fd; - FilePath path; - if (!options.name_deprecated || options.name_deprecated->empty()) { - bool result = - CreateAnonymousSharedMemory(options, &fd, &readonly_fd, &path); - if (!result) - return false; - } else { - if (!FilePathForMemoryName(*options.name_deprecated, &path)) - return false; - - // Make sure that the file is opened without any permission - // to other users on the system. - const mode_t kOwnerOnly = S_IRUSR | S_IWUSR; - - // First, try to create the file. - fd.reset(HANDLE_EINTR( - open(path.value().c_str(), O_RDWR | O_CREAT | O_EXCL, kOwnerOnly))); - if (!fd.is_valid() && options.open_existing_deprecated) { - // If this doesn't work, try and open an existing file in append mode. - // Opening an existing file in a world writable directory has two main - // security implications: - // - Attackers could plant a file under their control, so ownership of - // the file is checked below. - // - Attackers could plant a symbolic link so that an unexpected file - // is opened, so O_NOFOLLOW is passed to open(). -#if !defined(OS_AIX) - fd.reset(HANDLE_EINTR( - open(path.value().c_str(), O_RDWR | O_APPEND | O_NOFOLLOW))); -#else - // AIX has no 64-bit support for open flags such as - - // O_CLOEXEC, O_NOFOLLOW and O_TTY_INIT. - fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDWR | O_APPEND))); -#endif - // Check that the current user owns the file. - // If uid != euid, then a more complex permission model is used and this - // API is not appropriate. - const uid_t real_uid = getuid(); - const uid_t effective_uid = geteuid(); - struct stat sb; - if (fd.is_valid() && - (fstat(fd.get(), &sb) != 0 || sb.st_uid != real_uid || - sb.st_uid != effective_uid)) { - LOG(ERROR) << - "Invalid owner when opening existing shared memory file."; - close(fd.get()); - return false; - } - - // An existing file was opened, so its size should not be fixed. - fix_size = false; - } - - if (options.share_read_only) { - // Also open as readonly so that we can GetReadOnlyHandle. - 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.get()); - return false; - } - } - } - if (fd.is_valid() && fix_size) { - // Get current size. - struct stat stat; - if (fstat(fd.get(), &stat) != 0) - return false; - const size_t current_size = stat.st_size; - if (current_size != options.size) { - if (HANDLE_EINTR(ftruncate(fd.get(), options.size)) != 0) - return false; - } - requested_size_ = options.size; - } - if (!fd.is_valid()) { - PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; - FilePath dir = path.DirName(); - if (access(dir.value().c_str(), W_OK | X_OK) < 0) { - PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); - if (dir.value() == "/dev/shm") { - LOG(FATAL) << "This is frequently caused by incorrect permissions on " - << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; - } - } - return false; - } - - int mapped_file = -1; - int readonly_mapped_file = -1; - - bool result = PrepareMapFile(std::move(fd), std::move(readonly_fd), - &mapped_file, &readonly_mapped_file); - shm_ = SharedMemoryHandle(FileDescriptor(mapped_file, false), options.size, - UnguessableToken::Create()); - readonly_shm_ = - SharedMemoryHandle(FileDescriptor(readonly_mapped_file, false), - options.size, shm_.GetGUID()); - return result; -} - -// Our current implementation of shmem is with mmap()ing of files. -// These files need to be deleted explicitly. -// In practice this call is only needed for unit tests. -bool SharedMemory::Delete(const std::string& name) { - FilePath path; - if (!FilePathForMemoryName(name, &path)) - return false; - - if (PathExists(path)) - return DeleteFile(path, false); - - // Doesn't exist, so success. - return true; -} - -bool SharedMemory::Open(const std::string& name, bool read_only) { - FilePath path; - if (!FilePathForMemoryName(name, &path)) - return false; - - read_only_ = read_only; - - int mode = read_only ? O_RDONLY : O_RDWR; - ScopedFD fd(HANDLE_EINTR(open(path.value().c_str(), mode))); - ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); - if (!readonly_fd.is_valid()) { - DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; - return false; - } - int mapped_file = -1; - int readonly_mapped_file = -1; - bool result = PrepareMapFile(std::move(fd), std::move(readonly_fd), - &mapped_file, &readonly_mapped_file); - // This form of sharing shared memory is deprecated. https://crbug.com/345734. - // However, we can't get rid of it without a significant refactor because its - // used to communicate between two versions of the same service process, very - // early in the life cycle. - // Technically, we should also pass the GUID from the original shared memory - // region. We don't do that - this means that we will overcount this memory, - // which thankfully isn't relevant since Chrome only communicates with a - // single version of the service process. - // We pass the size |0|, which is a dummy size and wrong, but otherwise - // harmless. - shm_ = SharedMemoryHandle(FileDescriptor(mapped_file, false), 0u, - UnguessableToken::Create()); - readonly_shm_ = SharedMemoryHandle( - FileDescriptor(readonly_mapped_file, false), 0, shm_.GetGUID()); - return result; -} -#endif // !defined(OS_ANDROID) - -bool SharedMemory::MapAt(off_t offset, size_t bytes) { - if (!shm_.IsValid()) - return false; - - if (bytes > static_cast(std::numeric_limits::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. - if (bytes == 0) { - DCHECK_EQ(0, offset); - int ashmem_bytes = ashmem_get_size_region(shm_.GetHandle()); - if (ashmem_bytes < 0) - return false; - bytes = ashmem_bytes; - } - - // Sanity check. This shall catch invalid uses of the SharedMemory APIs - // but will not protect against direct mmap() attempts. - if (shm_.IsReadOnly()) { - // Use a DCHECK() to call writable mappings with read-only descriptors - // in debug builds immediately. Return an error for release builds - // or during unit-testing (assuming a ScopedLogAssertHandler was installed). - DCHECK(read_only_) - << "Trying to map a region writable with a read-only descriptor."; - if (!read_only_) { - return false; - } - if (!shm_.SetRegionReadOnly()) { // Ensure the region is read-only. - return false; - } - } -#endif - - memory_ = mmap(nullptr, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), - MAP_SHARED, shm_.GetHandle(), offset); - - bool mmap_succeeded = memory_ && memory_ != reinterpret_cast(-1); - if (mmap_succeeded) { - mapped_size_ = bytes; - mapped_id_ = shm_.GetGUID(); - DCHECK_EQ(0U, - reinterpret_cast(memory_) & - (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1)); - SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); - } else { - memory_ = nullptr; - } - - return mmap_succeeded; -} - -bool SharedMemory::Unmap() { - if (!memory_) - return false; - - SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); - munmap(memory_, mapped_size_); - memory_ = nullptr; - mapped_size_ = 0; - mapped_id_ = UnguessableToken(); - return true; -} - -SharedMemoryHandle SharedMemory::handle() const { - return shm_; -} - -SharedMemoryHandle SharedMemory::TakeHandle() { - SharedMemoryHandle handle_copy = shm_; - handle_copy.SetOwnershipPassesToIPC(true); - Unmap(); - shm_ = SharedMemoryHandle(); - return handle_copy; -} - -#if !defined(OS_ANDROID) -void SharedMemory::Close() { - if (shm_.IsValid()) { - shm_.Close(); - shm_ = SharedMemoryHandle(); - } - if (readonly_shm_.IsValid()) { - readonly_shm_.Close(); - readonly_shm_ = SharedMemoryHandle(); - } -} - -// For the given shmem named |mem_name|, return a filename to mmap() -// (and possibly create). Modifies |filename|. Return false on -// error, or true of we are happy. -bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, - FilePath* path) { - // mem_name will be used for a filename; make sure it doesn't - // contain anything which will confuse us. - DCHECK_EQ(std::string::npos, mem_name.find('/')); - DCHECK_EQ(std::string::npos, mem_name.find('\0')); - - FilePath temp_dir; - if (!GetShmemTempDir(false, &temp_dir)) - return false; - -#if defined(GOOGLE_CHROME_BUILD) - static const char kShmem[] = "com.google.Chrome.shmem."; -#else - static const char kShmem[] = "org.chromium.Chromium.shmem."; -#endif - CR_DEFINE_STATIC_LOCAL(const std::string, name_base, (kShmem)); - *path = temp_dir.AppendASCII(name_base + mem_name); - return true; -} - -SharedMemoryHandle SharedMemory::GetReadOnlyHandle() const { - CHECK(readonly_shm_.IsValid()); - return readonly_shm_.Duplicate(); -} -#endif // !defined(OS_ANDROID) - -} // namespace base diff --git a/memory/shared_memory_region_unittest.cc b/memory/shared_memory_region_unittest.cc deleted file mode 100644 index 78ac63089..000000000 --- a/memory/shared_memory_region_unittest.cc +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2018 The Chromium 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 - -#include "base/memory/platform_shared_memory_region.h" -#include "base/memory/read_only_shared_memory_region.h" -#include "base/memory/unsafe_shared_memory_region.h" -#include "base/memory/writable_shared_memory_region.h" -#include "base/sys_info.h" -#include "base/test/test_shared_memory_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -const size_t kRegionSize = 1024; - -bool IsMemoryFilledWithByte(const void* memory, size_t size, char byte) { - const char* start_ptr = static_cast(memory); - const char* end_ptr = start_ptr + size; - for (const char* ptr = start_ptr; ptr < end_ptr; ++ptr) { - if (*ptr != byte) - return false; - } - - return true; -} - -template -class SharedMemoryRegionTest : public ::testing::Test { - public: - void SetUp() override { - std::tie(region_, rw_mapping_) = - CreateMappedRegion(kRegionSize); - ASSERT_TRUE(region_.IsValid()); - ASSERT_TRUE(rw_mapping_.IsValid()); - memset(rw_mapping_.memory(), 'G', kRegionSize); - EXPECT_TRUE(IsMemoryFilledWithByte(rw_mapping_.memory(), kRegionSize, 'G')); - } - - protected: - SharedMemoryRegionType region_; - WritableSharedMemoryMapping rw_mapping_; -}; - -typedef ::testing::Types - AllRegionTypes; -TYPED_TEST_CASE(SharedMemoryRegionTest, AllRegionTypes); - -TYPED_TEST(SharedMemoryRegionTest, NonValidRegion) { - TypeParam region; - EXPECT_FALSE(region.IsValid()); - // We shouldn't crash on Map but should return an invalid mapping. - typename TypeParam::MappingType mapping = region.Map(); - EXPECT_FALSE(mapping.IsValid()); -} - -TYPED_TEST(SharedMemoryRegionTest, MoveRegion) { - TypeParam moved_region = std::move(this->region_); - EXPECT_FALSE(this->region_.IsValid()); - ASSERT_TRUE(moved_region.IsValid()); - - // Check that moved region maps correctly. - typename TypeParam::MappingType mapping = moved_region.Map(); - ASSERT_TRUE(mapping.IsValid()); - EXPECT_NE(this->rw_mapping_.memory(), mapping.memory()); - EXPECT_EQ(memcmp(this->rw_mapping_.memory(), mapping.memory(), kRegionSize), - 0); - - // Verify that the second mapping reflects changes in the first. - memset(this->rw_mapping_.memory(), '#', kRegionSize); - EXPECT_EQ(memcmp(this->rw_mapping_.memory(), mapping.memory(), kRegionSize), - 0); -} - -TYPED_TEST(SharedMemoryRegionTest, MappingValidAfterClose) { - // Check the mapping is still valid after the region is closed. - this->region_ = TypeParam(); - EXPECT_FALSE(this->region_.IsValid()); - ASSERT_TRUE(this->rw_mapping_.IsValid()); - EXPECT_TRUE( - IsMemoryFilledWithByte(this->rw_mapping_.memory(), kRegionSize, 'G')); -} - -TYPED_TEST(SharedMemoryRegionTest, MapTwice) { - // The second mapping is either writable or read-only. - typename TypeParam::MappingType mapping = this->region_.Map(); - ASSERT_TRUE(mapping.IsValid()); - EXPECT_NE(this->rw_mapping_.memory(), mapping.memory()); - EXPECT_EQ(memcmp(this->rw_mapping_.memory(), mapping.memory(), kRegionSize), - 0); - - // Verify that the second mapping reflects changes in the first. - memset(this->rw_mapping_.memory(), '#', kRegionSize); - EXPECT_EQ(memcmp(this->rw_mapping_.memory(), mapping.memory(), kRegionSize), - 0); - - // Close the region and unmap the first memory segment, verify the second - // still has the right data. - this->region_ = TypeParam(); - this->rw_mapping_ = WritableSharedMemoryMapping(); - EXPECT_TRUE(IsMemoryFilledWithByte(mapping.memory(), kRegionSize, '#')); -} - -TYPED_TEST(SharedMemoryRegionTest, MapUnmapMap) { - this->rw_mapping_ = WritableSharedMemoryMapping(); - - typename TypeParam::MappingType mapping = this->region_.Map(); - ASSERT_TRUE(mapping.IsValid()); - EXPECT_TRUE(IsMemoryFilledWithByte(mapping.memory(), kRegionSize, 'G')); -} - -TYPED_TEST(SharedMemoryRegionTest, SerializeAndDeserialize) { - subtle::PlatformSharedMemoryRegion platform_region = - TypeParam::TakeHandleForSerialization(std::move(this->region_)); - EXPECT_EQ(platform_region.GetGUID(), this->rw_mapping_.guid()); - TypeParam region = TypeParam::Deserialize(std::move(platform_region)); - EXPECT_TRUE(region.IsValid()); - EXPECT_FALSE(this->region_.IsValid()); - typename TypeParam::MappingType mapping = region.Map(); - ASSERT_TRUE(mapping.IsValid()); - EXPECT_TRUE(IsMemoryFilledWithByte(mapping.memory(), kRegionSize, 'G')); - - // Verify that the second mapping reflects changes in the first. - memset(this->rw_mapping_.memory(), '#', kRegionSize); - EXPECT_EQ(memcmp(this->rw_mapping_.memory(), mapping.memory(), kRegionSize), - 0); -} - -// Map() will return addresses which are aligned to the platform page size, this -// varies from platform to platform though. Since we'd like to advertise a -// minimum alignment that callers can count on, test for it here. -TYPED_TEST(SharedMemoryRegionTest, MapMinimumAlignment) { - EXPECT_EQ(0U, - reinterpret_cast(this->rw_mapping_.memory()) & - (subtle::PlatformSharedMemoryRegion::kMapMinimumAlignment - 1)); -} - -TYPED_TEST(SharedMemoryRegionTest, MapSize) { - EXPECT_EQ(this->rw_mapping_.size(), kRegionSize); - EXPECT_GE(this->rw_mapping_.mapped_size(), kRegionSize); -} - -TYPED_TEST(SharedMemoryRegionTest, MapGranularity) { - EXPECT_LT(this->rw_mapping_.mapped_size(), - kRegionSize + SysInfo::VMAllocationGranularity()); -} - -TYPED_TEST(SharedMemoryRegionTest, MapAt) { - const size_t kPageSize = SysInfo::VMAllocationGranularity(); - ASSERT_TRUE(kPageSize >= sizeof(uint32_t)); - ASSERT_EQ(kPageSize % sizeof(uint32_t), 0U); - const size_t kDataSize = kPageSize * 2; - const size_t kCount = kDataSize / sizeof(uint32_t); - - TypeParam region; - WritableSharedMemoryMapping rw_mapping; - std::tie(region, rw_mapping) = CreateMappedRegion(kDataSize); - ASSERT_TRUE(region.IsValid()); - ASSERT_TRUE(rw_mapping.IsValid()); - uint32_t* ptr = static_cast(rw_mapping.memory()); - - for (size_t i = 0; i < kCount; ++i) - ptr[i] = i; - - rw_mapping = WritableSharedMemoryMapping(); - off_t bytes_offset = kPageSize; - typename TypeParam::MappingType mapping = - region.MapAt(bytes_offset, kDataSize - bytes_offset); - ASSERT_TRUE(mapping.IsValid()); - - off_t int_offset = bytes_offset / sizeof(uint32_t); - const uint32_t* ptr2 = static_cast(mapping.memory()); - for (size_t i = int_offset; i < kCount; ++i) { - EXPECT_EQ(ptr2[i - int_offset], i); - } -} - -TYPED_TEST(SharedMemoryRegionTest, MapAtNotAlignedOffsetFails) { - const size_t kDataSize = SysInfo::VMAllocationGranularity(); - - TypeParam region; - WritableSharedMemoryMapping rw_mapping; - std::tie(region, rw_mapping) = CreateMappedRegion(kDataSize); - ASSERT_TRUE(region.IsValid()); - ASSERT_TRUE(rw_mapping.IsValid()); - off_t offset = kDataSize / 2; - typename TypeParam::MappingType mapping = - region.MapAt(offset, kDataSize - offset); - EXPECT_FALSE(mapping.IsValid()); -} - -TYPED_TEST(SharedMemoryRegionTest, MapMoreBytesThanRegionSizeFails) { - size_t region_real_size = this->region_.GetSize(); - typename TypeParam::MappingType mapping = - this->region_.MapAt(0, region_real_size + 1); - EXPECT_FALSE(mapping.IsValid()); -} - -template -class DuplicatableSharedMemoryRegionTest - : public SharedMemoryRegionTest {}; - -typedef ::testing::Types - DuplicatableRegionTypes; -TYPED_TEST_CASE(DuplicatableSharedMemoryRegionTest, DuplicatableRegionTypes); - -TYPED_TEST(DuplicatableSharedMemoryRegionTest, Duplicate) { - TypeParam dup_region = this->region_.Duplicate(); - EXPECT_EQ(this->region_.GetGUID(), dup_region.GetGUID()); - typename TypeParam::MappingType mapping = dup_region.Map(); - ASSERT_TRUE(mapping.IsValid()); - EXPECT_NE(this->rw_mapping_.memory(), mapping.memory()); - EXPECT_EQ(this->rw_mapping_.guid(), mapping.guid()); - EXPECT_TRUE(IsMemoryFilledWithByte(mapping.memory(), kRegionSize, 'G')); -} - -class ReadOnlySharedMemoryRegionTest : public ::testing::Test { - public: - ReadOnlySharedMemoryRegion GetInitiallyReadOnlyRegion(size_t size) { - MappedReadOnlyRegion mapped_region = - ReadOnlySharedMemoryRegion::Create(size); - ReadOnlySharedMemoryRegion region = std::move(mapped_region.region); - return region; - } - - ReadOnlySharedMemoryRegion GetConvertedToReadOnlyRegion(size_t size) { - WritableSharedMemoryRegion region = - WritableSharedMemoryRegion::Create(kRegionSize); - ReadOnlySharedMemoryRegion ro_region = - WritableSharedMemoryRegion::ConvertToReadOnly(std::move(region)); - return ro_region; - } -}; - -TEST_F(ReadOnlySharedMemoryRegionTest, - InitiallyReadOnlyRegionCannotBeMappedAsWritable) { - ReadOnlySharedMemoryRegion region = GetInitiallyReadOnlyRegion(kRegionSize); - ASSERT_TRUE(region.IsValid()); - - EXPECT_TRUE(CheckReadOnlyPlatformSharedMemoryRegionForTesting( - ReadOnlySharedMemoryRegion::TakeHandleForSerialization( - std::move(region)))); -} - -TEST_F(ReadOnlySharedMemoryRegionTest, - ConvertedToReadOnlyRegionCannotBeMappedAsWritable) { - ReadOnlySharedMemoryRegion region = GetConvertedToReadOnlyRegion(kRegionSize); - ASSERT_TRUE(region.IsValid()); - - EXPECT_TRUE(CheckReadOnlyPlatformSharedMemoryRegionForTesting( - ReadOnlySharedMemoryRegion::TakeHandleForSerialization( - std::move(region)))); -} - -TEST_F(ReadOnlySharedMemoryRegionTest, - InitiallyReadOnlyRegionProducedMappingWriteDeathTest) { - ReadOnlySharedMemoryRegion region = GetInitiallyReadOnlyRegion(kRegionSize); - ASSERT_TRUE(region.IsValid()); - ReadOnlySharedMemoryMapping mapping = region.Map(); - ASSERT_TRUE(mapping.IsValid()); - void* memory_ptr = const_cast(mapping.memory()); - EXPECT_DEATH_IF_SUPPORTED(memset(memory_ptr, 'G', kRegionSize), ""); -} - -TEST_F(ReadOnlySharedMemoryRegionTest, - ConvertedToReadOnlyRegionProducedMappingWriteDeathTest) { - ReadOnlySharedMemoryRegion region = GetConvertedToReadOnlyRegion(kRegionSize); - ASSERT_TRUE(region.IsValid()); - ReadOnlySharedMemoryMapping mapping = region.Map(); - ASSERT_TRUE(mapping.IsValid()); - void* memory_ptr = const_cast(mapping.memory()); - EXPECT_DEATH_IF_SUPPORTED(memset(memory_ptr, 'G', kRegionSize), ""); -} - -} // namespace base diff --git a/memory/shared_memory_tracker.cc b/memory/shared_memory_tracker.cc deleted file mode 100644 index 5ca7c840b..000000000 --- a/memory/shared_memory_tracker.cc +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory_tracker.h" - -#include "base/memory/shared_memory.h" -#include "base/strings/string_number_conversions.h" -#include "base/trace_event/memory_allocator_dump_guid.h" -#include "base/trace_event/memory_dump_manager.h" -#include "base/trace_event/process_memory_dump.h" - -namespace base { - -const char SharedMemoryTracker::kDumpRootName[] = "shared_memory"; - -// static -SharedMemoryTracker* SharedMemoryTracker::GetInstance() { - static SharedMemoryTracker* instance = new SharedMemoryTracker; - return instance; -} - -// static -std::string SharedMemoryTracker::GetDumpNameForTracing( - const UnguessableToken& id) { - DCHECK(!id.is_empty()); - return std::string(kDumpRootName) + "/" + id.ToString(); -} - -// static -trace_event::MemoryAllocatorDumpGuid -SharedMemoryTracker::GetGlobalDumpIdForTracing(const UnguessableToken& id) { - std::string dump_name = GetDumpNameForTracing(id); - return trace_event::MemoryAllocatorDumpGuid(dump_name); -} - -// static -const trace_event::MemoryAllocatorDump* -SharedMemoryTracker::GetOrCreateSharedMemoryDump( - const SharedMemory* shared_memory, - trace_event::ProcessMemoryDump* pmd) { - return GetOrCreateSharedMemoryDumpInternal(shared_memory->memory(), - shared_memory->mapped_size(), - shared_memory->mapped_id(), pmd); -} - -const trace_event::MemoryAllocatorDump* -SharedMemoryTracker::GetOrCreateSharedMemoryDump( - const SharedMemoryMapping& shared_memory, - trace_event::ProcessMemoryDump* pmd) { - return GetOrCreateSharedMemoryDumpInternal(shared_memory.raw_memory_ptr(), - shared_memory.mapped_size(), - shared_memory.guid(), pmd); -} - -void SharedMemoryTracker::IncrementMemoryUsage( - const SharedMemory& shared_memory) { - AutoLock hold(usages_lock_); - DCHECK(usages_.find(shared_memory.memory()) == usages_.end()); - usages_.emplace(shared_memory.memory(), UsageInfo(shared_memory.mapped_size(), - shared_memory.mapped_id())); -} - -void SharedMemoryTracker::IncrementMemoryUsage( - const SharedMemoryMapping& mapping) { - AutoLock hold(usages_lock_); - DCHECK(usages_.find(mapping.raw_memory_ptr()) == usages_.end()); - usages_.emplace(mapping.raw_memory_ptr(), - UsageInfo(mapping.mapped_size(), mapping.guid())); -} - -void SharedMemoryTracker::DecrementMemoryUsage( - const SharedMemory& shared_memory) { - AutoLock hold(usages_lock_); - DCHECK(usages_.find(shared_memory.memory()) != usages_.end()); - usages_.erase(shared_memory.memory()); -} - -void SharedMemoryTracker::DecrementMemoryUsage( - const SharedMemoryMapping& mapping) { - AutoLock hold(usages_lock_); - DCHECK(usages_.find(mapping.raw_memory_ptr()) != usages_.end()); - usages_.erase(mapping.raw_memory_ptr()); -} - -SharedMemoryTracker::SharedMemoryTracker() { - trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( - this, "SharedMemoryTracker", nullptr); -} - -SharedMemoryTracker::~SharedMemoryTracker() = default; - -bool SharedMemoryTracker::OnMemoryDump(const trace_event::MemoryDumpArgs& args, - trace_event::ProcessMemoryDump* pmd) { - AutoLock hold(usages_lock_); - for (const auto& usage : usages_) { - const trace_event::MemoryAllocatorDump* dump = - GetOrCreateSharedMemoryDumpInternal( - usage.first, usage.second.mapped_size, usage.second.mapped_id, pmd); - DCHECK(dump); - } - return true; -} - -// static -const trace_event::MemoryAllocatorDump* -SharedMemoryTracker::GetOrCreateSharedMemoryDumpInternal( - void* mapped_memory, - size_t mapped_size, - const UnguessableToken& mapped_id, - trace_event::ProcessMemoryDump* pmd) { - const std::string dump_name = GetDumpNameForTracing(mapped_id); - trace_event::MemoryAllocatorDump* local_dump = - pmd->GetAllocatorDump(dump_name); - if (local_dump) - return local_dump; - - size_t virtual_size = mapped_size; - // If resident size is not available, a virtual size is used as fallback. - size_t size = virtual_size; -#if defined(COUNT_RESIDENT_BYTES_SUPPORTED) - base::Optional resident_size = - trace_event::ProcessMemoryDump::CountResidentBytesInSharedMemory( - mapped_memory, mapped_size); - if (resident_size.has_value()) - size = resident_size.value(); -#endif - - local_dump = pmd->CreateAllocatorDump(dump_name); - local_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize, - trace_event::MemoryAllocatorDump::kUnitsBytes, size); - local_dump->AddScalar("virtual_size", - trace_event::MemoryAllocatorDump::kUnitsBytes, - virtual_size); - auto global_dump_guid = GetGlobalDumpIdForTracing(mapped_id); - trace_event::MemoryAllocatorDump* global_dump = - pmd->CreateSharedGlobalAllocatorDump(global_dump_guid); - global_dump->AddScalar(trace_event::MemoryAllocatorDump::kNameSize, - trace_event::MemoryAllocatorDump::kUnitsBytes, size); - - // The edges will be overriden by the clients with correct importance. - pmd->AddOverridableOwnershipEdge(local_dump->guid(), global_dump->guid(), - 0 /* importance */); - return local_dump; -} - -} // namespace diff --git a/memory/shared_memory_tracker.h b/memory/shared_memory_tracker.h deleted file mode 100644 index 499b1728c..000000000 --- a/memory/shared_memory_tracker.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MEMORY_SHARED_MEMORY_TRACKER_H_ -#define BASE_MEMORY_SHARED_MEMORY_TRACKER_H_ - -#include -#include - -#include "base/memory/shared_memory.h" -#include "base/memory/shared_memory_mapping.h" -#include "base/synchronization/lock.h" -#include "base/trace_event/memory_dump_provider.h" - -namespace base { - -namespace trace_event { -class MemoryAllocatorDump; -class MemoryAllocatorDumpGuid; -class ProcessMemoryDump; -} - -// SharedMemoryTracker tracks shared memory usage. -class BASE_EXPORT SharedMemoryTracker : public trace_event::MemoryDumpProvider { - public: - // Returns a singleton instance. - static SharedMemoryTracker* GetInstance(); - - static std::string GetDumpNameForTracing(const UnguessableToken& id); - - static trace_event::MemoryAllocatorDumpGuid GetGlobalDumpIdForTracing( - const UnguessableToken& id); - - // Gets or creates if non-existant, a memory dump for the |shared_memory| - // inside the given |pmd|. Also adds the necessary edges for the dump when - // creating the dump. - static const trace_event::MemoryAllocatorDump* GetOrCreateSharedMemoryDump( - const SharedMemory* shared_memory, - trace_event::ProcessMemoryDump* pmd); - // We're in the middle of a refactor https://crbug.com/795291. Eventually, the - // first call will go away. - static const trace_event::MemoryAllocatorDump* GetOrCreateSharedMemoryDump( - const SharedMemoryMapping& shared_memory, - trace_event::ProcessMemoryDump* pmd); - - // Records shared memory usage on valid mapping. - void IncrementMemoryUsage(const SharedMemory& shared_memory); - void IncrementMemoryUsage(const SharedMemoryMapping& mapping); - - // Records shared memory usage on unmapping. - void DecrementMemoryUsage(const SharedMemory& shared_memory); - void DecrementMemoryUsage(const SharedMemoryMapping& mapping); - - // Root dump name for all shared memory dumps. - static const char kDumpRootName[]; - - private: - SharedMemoryTracker(); - ~SharedMemoryTracker() override; - - // trace_event::MemoryDumpProvider implementation. - bool OnMemoryDump(const trace_event::MemoryDumpArgs& args, - trace_event::ProcessMemoryDump* pmd) override; - - static const trace_event::MemoryAllocatorDump* - GetOrCreateSharedMemoryDumpInternal(void* mapped_memory, - size_t mapped_size, - const UnguessableToken& mapped_id, - trace_event::ProcessMemoryDump* pmd); - - // Information associated with each mapped address. - struct UsageInfo { - UsageInfo(size_t size, const UnguessableToken& id) - : mapped_size(size), mapped_id(id) {} - - size_t mapped_size; - UnguessableToken mapped_id; - }; - - // Used to lock when |usages_| is modified or read. - Lock usages_lock_; - std::map usages_; - - DISALLOW_COPY_AND_ASSIGN(SharedMemoryTracker); -}; - -} // namespace base - -#endif // BASE_MEMORY_SHARED_MEMORY_TRACKER_H_ diff --git a/memory/shared_memory_unittest.cc b/memory/shared_memory_unittest.cc deleted file mode 100644 index 3bccf80df..000000000 --- a/memory/shared_memory_unittest.cc +++ /dev/null @@ -1,974 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/shared_memory.h" - -#include -#include - -#include - -#include "base/atomicops.h" -#include "base/base_switches.h" -#include "base/bind.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/shared_memory_handle.h" -#include "base/process/kill.h" -#include "base/rand_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_util.h" -#include "base/sys_info.h" -#include "base/test/multiprocess_test.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" -#include "base/unguessable_token.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/multiprocess_func_list.h" - -#if defined(OS_ANDROID) -#include "base/callback.h" -#endif - -#if defined(OS_POSIX) -#include -#include -#include -#include -#include -#include -#endif - -#if defined(OS_LINUX) -#include -#endif - -#if defined(OS_WIN) -#include "base/win/scoped_handle.h" -#endif - -#if defined(OS_FUCHSIA) -#include -#include -#endif - -namespace base { - -namespace { - -#if !defined(OS_MACOSX) && !defined(OS_FUCHSIA) -// Each thread will open the shared memory. Each thread will take a different 4 -// byte int pointer, and keep changing it, with some small pauses in between. -// Verify that each thread's value in the shared memory is always correct. -class MultipleThreadMain : public PlatformThread::Delegate { - public: - explicit MultipleThreadMain(int16_t id) : id_(id) {} - ~MultipleThreadMain() override = default; - - static void CleanUp() { - SharedMemory memory; - memory.Delete(s_test_name_); - } - - // PlatformThread::Delegate interface. - void ThreadMain() override { - const uint32_t kDataSize = 1024; - SharedMemory memory; - bool rv = memory.CreateNamedDeprecated(s_test_name_, true, kDataSize); - EXPECT_TRUE(rv); - rv = memory.Map(kDataSize); - EXPECT_TRUE(rv); - int* ptr = static_cast(memory.memory()) + id_; - EXPECT_EQ(0, *ptr); - - for (int idx = 0; idx < 100; idx++) { - *ptr = idx; - PlatformThread::Sleep(TimeDelta::FromMilliseconds(1)); - EXPECT_EQ(*ptr, idx); - } - // Reset back to 0 for the next test that uses the same name. - *ptr = 0; - - memory.Close(); - } - - private: - int16_t id_; - - static const char s_test_name_[]; - - DISALLOW_COPY_AND_ASSIGN(MultipleThreadMain); -}; - -const char MultipleThreadMain::s_test_name_[] = - "SharedMemoryOpenThreadTest"; -#endif // !defined(OS_MACOSX) && !defined(OS_FUCHSIA) - -enum class Mode { - Default, -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - DisableDevShm = 1, -#endif -}; - -class SharedMemoryTest : public ::testing::TestWithParam { - public: - void SetUp() override { - switch (GetParam()) { - case Mode::Default: - break; -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) - case Mode::DisableDevShm: - CommandLine* cmdline = CommandLine::ForCurrentProcess(); - cmdline->AppendSwitch(switches::kDisableDevShmUsage); - break; -#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) - } - } -}; - -} // namespace - -// Android/Mac/Fuchsia doesn't support SharedMemory::Open/Delete/ -// CreateNamedDeprecated(openExisting=true) -#if !defined(OS_ANDROID) && !defined(OS_MACOSX) && !defined(OS_FUCHSIA) - -TEST_P(SharedMemoryTest, OpenClose) { - const uint32_t kDataSize = 1024; - std::string test_name = "SharedMemoryOpenCloseTest"; - - // Open two handles to a memory segment, confirm that they are mapped - // separately yet point to the same space. - SharedMemory memory1; - bool rv = memory1.Delete(test_name); - EXPECT_TRUE(rv); - rv = memory1.Delete(test_name); - EXPECT_TRUE(rv); - rv = memory1.Open(test_name, false); - EXPECT_FALSE(rv); - rv = memory1.CreateNamedDeprecated(test_name, false, kDataSize); - EXPECT_TRUE(rv); - rv = memory1.Map(kDataSize); - EXPECT_TRUE(rv); - SharedMemory memory2; - rv = memory2.Open(test_name, false); - EXPECT_TRUE(rv); - rv = memory2.Map(kDataSize); - EXPECT_TRUE(rv); - EXPECT_NE(memory1.memory(), memory2.memory()); // Compare the pointers. - - // Make sure we don't segfault. (it actually happened!) - ASSERT_NE(memory1.memory(), static_cast(nullptr)); - ASSERT_NE(memory2.memory(), static_cast(nullptr)); - - // Write data to the first memory segment, verify contents of second. - memset(memory1.memory(), '1', kDataSize); - EXPECT_EQ(memcmp(memory1.memory(), memory2.memory(), kDataSize), 0); - - // Close the first memory segment, and verify the second has the right data. - memory1.Close(); - char* start_ptr = static_cast(memory2.memory()); - char* end_ptr = start_ptr + kDataSize; - for (char* ptr = start_ptr; ptr < end_ptr; ptr++) - EXPECT_EQ(*ptr, '1'); - - // Close the second memory segment. - memory2.Close(); - - rv = memory1.Delete(test_name); - EXPECT_TRUE(rv); - rv = memory2.Delete(test_name); - EXPECT_TRUE(rv); -} - -TEST_P(SharedMemoryTest, OpenExclusive) { - const uint32_t kDataSize = 1024; - const uint32_t kDataSize2 = 2048; - std::ostringstream test_name_stream; - test_name_stream << "SharedMemoryOpenExclusiveTest." - << Time::Now().ToDoubleT(); - std::string test_name = test_name_stream.str(); - - // Open two handles to a memory segment and check that - // open_existing_deprecated works as expected. - SharedMemory memory1; - bool rv = memory1.CreateNamedDeprecated(test_name, false, kDataSize); - EXPECT_TRUE(rv); - - // Memory1 knows it's size because it created it. - EXPECT_EQ(memory1.requested_size(), kDataSize); - - rv = memory1.Map(kDataSize); - EXPECT_TRUE(rv); - - // The mapped memory1 must be at least the size we asked for. - EXPECT_GE(memory1.mapped_size(), kDataSize); - - // The mapped memory1 shouldn't exceed rounding for allocation granularity. - EXPECT_LT(memory1.mapped_size(), - kDataSize + SysInfo::VMAllocationGranularity()); - - memset(memory1.memory(), 'G', kDataSize); - - SharedMemory memory2; - // Should not be able to create if openExisting is false. - rv = memory2.CreateNamedDeprecated(test_name, false, kDataSize2); - EXPECT_FALSE(rv); - - // Should be able to create with openExisting true. - rv = memory2.CreateNamedDeprecated(test_name, true, kDataSize2); - EXPECT_TRUE(rv); - - // Memory2 shouldn't know the size because we didn't create it. - EXPECT_EQ(memory2.requested_size(), 0U); - - // We should be able to map the original size. - rv = memory2.Map(kDataSize); - EXPECT_TRUE(rv); - - // The mapped memory2 must be at least the size of the original. - EXPECT_GE(memory2.mapped_size(), kDataSize); - - // The mapped memory2 shouldn't exceed rounding for allocation granularity. - EXPECT_LT(memory2.mapped_size(), - kDataSize2 + SysInfo::VMAllocationGranularity()); - - // Verify that opening memory2 didn't truncate or delete memory 1. - char* start_ptr = static_cast(memory2.memory()); - char* end_ptr = start_ptr + kDataSize; - for (char* ptr = start_ptr; ptr < end_ptr; ptr++) { - EXPECT_EQ(*ptr, 'G'); - } - - memory1.Close(); - memory2.Close(); - - rv = memory1.Delete(test_name); - EXPECT_TRUE(rv); -} -#endif // !defined(OS_ANDROID) && !defined(OS_MACOSX) && !defined(OS_FUCHSIA) - -// Check that memory is still mapped after its closed. -TEST_P(SharedMemoryTest, CloseNoUnmap) { - const size_t kDataSize = 4096; - - SharedMemory memory; - ASSERT_TRUE(memory.CreateAndMapAnonymous(kDataSize)); - char* ptr = static_cast(memory.memory()); - ASSERT_NE(ptr, static_cast(nullptr)); - memset(ptr, 'G', kDataSize); - - memory.Close(); - - EXPECT_EQ(ptr, memory.memory()); - EXPECT_TRUE(!memory.handle().IsValid()); - - for (size_t i = 0; i < kDataSize; i++) { - EXPECT_EQ('G', ptr[i]); - } - - memory.Unmap(); - EXPECT_EQ(nullptr, memory.memory()); -} - -#if !defined(OS_MACOSX) && !defined(OS_FUCHSIA) -// Create a set of N threads to each open a shared memory segment and write to -// it. Verify that they are always reading/writing consistent data. -TEST_P(SharedMemoryTest, MultipleThreads) { - const int kNumThreads = 5; - - MultipleThreadMain::CleanUp(); - // On POSIX we have a problem when 2 threads try to create the shmem - // (a file) at exactly the same time, since create both creates the - // file and zerofills it. We solve the problem for this unit test - // (make it not flaky) by starting with 1 thread, then - // intentionally don't clean up its shmem before running with - // kNumThreads. - - int threadcounts[] = { 1, kNumThreads }; - for (size_t i = 0; i < arraysize(threadcounts); i++) { - int numthreads = threadcounts[i]; - std::unique_ptr thread_handles; - std::unique_ptr thread_delegates; - - thread_handles.reset(new PlatformThreadHandle[numthreads]); - thread_delegates.reset(new MultipleThreadMain*[numthreads]); - - // Spawn the threads. - for (int16_t index = 0; index < numthreads; index++) { - PlatformThreadHandle pth; - thread_delegates[index] = new MultipleThreadMain(index); - EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth)); - thread_handles[index] = pth; - } - - // Wait for the threads to finish. - for (int index = 0; index < numthreads; index++) { - PlatformThread::Join(thread_handles[index]); - delete thread_delegates[index]; - } - } - MultipleThreadMain::CleanUp(); -} -#endif - -// Allocate private (unique) shared memory with an empty string for a -// name. Make sure several of them don't point to the same thing as -// we might expect if the names are equal. -TEST_P(SharedMemoryTest, AnonymousPrivate) { - int i, j; - int count = 4; - bool rv; - const uint32_t kDataSize = 8192; - - std::unique_ptr memories(new SharedMemory[count]); - std::unique_ptr pointers(new int*[count]); - ASSERT_TRUE(memories.get()); - ASSERT_TRUE(pointers.get()); - - for (i = 0; i < count; i++) { - rv = memories[i].CreateAndMapAnonymous(kDataSize); - EXPECT_TRUE(rv); - int* ptr = static_cast(memories[i].memory()); - EXPECT_TRUE(ptr); - pointers[i] = ptr; - } - - for (i = 0; i < count; i++) { - // zero out the first int in each except for i; for that one, make it 100. - for (j = 0; j < count; j++) { - if (i == j) - pointers[j][0] = 100; - else - pointers[j][0] = 0; - } - // make sure there is no bleeding of the 100 into the other pointers - for (j = 0; j < count; j++) { - if (i == j) - EXPECT_EQ(100, pointers[j][0]); - else - EXPECT_EQ(0, pointers[j][0]); - } - } - - for (int i = 0; i < count; i++) { - memories[i].Close(); - } -} - -TEST_P(SharedMemoryTest, GetReadOnlyHandle) { - StringPiece contents = "Hello World"; - - SharedMemory writable_shmem; - SharedMemoryCreateOptions options; - options.size = contents.size(); - options.share_read_only = true; -#if defined(OS_MACOSX) && !defined(OS_IOS) - // The Mach functionality is tested in shared_memory_mac_unittest.cc. - options.type = SharedMemoryHandle::POSIX; -#endif - 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()); - - SharedMemoryHandle readonly_handle = writable_shmem.GetReadOnlyHandle(); - EXPECT_EQ(writable_shmem.handle().GetGUID(), readonly_handle.GetGUID()); - EXPECT_EQ(writable_shmem.handle().GetSize(), readonly_handle.GetSize()); - ASSERT_TRUE(readonly_handle.IsValid()); - SharedMemory readonly_shmem(readonly_handle, /*readonly=*/true); - - ASSERT_TRUE(readonly_shmem.Map(contents.size())); - EXPECT_EQ(contents, - StringPiece(static_cast(readonly_shmem.memory()), - contents.size())); - EXPECT_TRUE(readonly_shmem.Unmap()); - -#if defined(OS_ANDROID) - // On Android, mapping a region through a read-only descriptor makes the - // region read-only. Any writable mapping attempt should fail. - ASSERT_FALSE(writable_shmem.Map(contents.size())); -#else - // Make sure the writable instance is still writable. - ASSERT_TRUE(writable_shmem.Map(contents.size())); - StringPiece new_contents = "Goodbye"; - memcpy(writable_shmem.memory(), new_contents.data(), new_contents.size()); - EXPECT_EQ(new_contents, - StringPiece(static_cast(writable_shmem.memory()), - new_contents.size())); -#endif - - // We'd like to check that if we send the read-only segment to another - // process, then that other process can't reopen it read/write. (Since that - // would be a security hole.) Setting up multiple processes is hard in a - // unittest, so this test checks that the *current* process can't reopen the - // segment read/write. I think the test here is stronger than we actually - // care about, but there's a remote possibility that sending a file over a - // pipe would transform it into read/write. - SharedMemoryHandle handle = readonly_shmem.handle(); - -#if defined(OS_ANDROID) - // The "read-only" handle is still writable on Android: - // http://crbug.com/320865 - (void)handle; -#elif defined(OS_FUCHSIA) - uintptr_t addr; - EXPECT_NE(ZX_OK, zx::vmar::root_self().map( - 0, *zx::unowned_vmo(handle.GetHandle()), 0, - contents.size(), ZX_VM_FLAG_PERM_WRITE, &addr)) - << "Shouldn't be able to map as writable."; - - zx::vmo duped_handle; - EXPECT_NE(ZX_OK, zx::unowned_vmo(handle.GetHandle()) - ->duplicate(ZX_RIGHT_WRITE, &duped_handle)) - << "Shouldn't be able to duplicate the handle into a writable one."; - - EXPECT_EQ(ZX_OK, zx::unowned_vmo(handle.GetHandle()) - ->duplicate(ZX_RIGHT_READ, &duped_handle)) - << "Should be able to duplicate the handle into a readable one."; -#elif defined(OS_POSIX) - int handle_fd = SharedMemory::GetFdFromSharedMemoryHandle(handle); - EXPECT_EQ(O_RDONLY, fcntl(handle_fd, F_GETFL) & O_ACCMODE) - << "The descriptor itself should be read-only."; - - errno = 0; - void* writable = mmap(nullptr, contents.size(), PROT_READ | PROT_WRITE, - MAP_SHARED, handle_fd, 0); - int mmap_errno = errno; - EXPECT_EQ(MAP_FAILED, writable) - << "It shouldn't be possible to re-mmap the descriptor writable."; - EXPECT_EQ(EACCES, mmap_errno) << strerror(mmap_errno); - if (writable != MAP_FAILED) - EXPECT_EQ(0, munmap(writable, readonly_shmem.mapped_size())); - -#elif defined(OS_WIN) - EXPECT_EQ(NULL, MapViewOfFile(handle.GetHandle(), FILE_MAP_WRITE, 0, 0, 0)) - << "Shouldn't be able to map memory writable."; - - HANDLE temp_handle; - BOOL rv = ::DuplicateHandle(GetCurrentProcess(), handle.GetHandle(), - GetCurrentProcess(), &temp_handle, - FILE_MAP_ALL_ACCESS, false, 0); - EXPECT_EQ(FALSE, rv) - << "Shouldn't be able to duplicate the handle into a writable one."; - if (rv) - win::ScopedHandle writable_handle(temp_handle); - rv = ::DuplicateHandle(GetCurrentProcess(), handle.GetHandle(), - GetCurrentProcess(), &temp_handle, FILE_MAP_READ, - false, 0); - EXPECT_EQ(TRUE, rv) - << "Should be able to duplicate the handle into a readable one."; - if (rv) - win::ScopedHandle writable_handle(temp_handle); -#else -#error Unexpected platform; write a test that tries to make 'handle' writable. -#endif // defined(OS_POSIX) || defined(OS_WIN) -} - -TEST_P(SharedMemoryTest, ShareToSelf) { - StringPiece contents = "Hello World"; - - SharedMemory shmem; - ASSERT_TRUE(shmem.CreateAndMapAnonymous(contents.size())); - memcpy(shmem.memory(), contents.data(), contents.size()); - EXPECT_TRUE(shmem.Unmap()); - - SharedMemoryHandle shared_handle = shmem.handle().Duplicate(); - ASSERT_TRUE(shared_handle.IsValid()); - EXPECT_TRUE(shared_handle.OwnershipPassesToIPC()); - EXPECT_EQ(shared_handle.GetGUID(), shmem.handle().GetGUID()); - EXPECT_EQ(shared_handle.GetSize(), shmem.handle().GetSize()); - SharedMemory shared(shared_handle, /*readonly=*/false); - - ASSERT_TRUE(shared.Map(contents.size())); - EXPECT_EQ( - contents, - StringPiece(static_cast(shared.memory()), contents.size())); - - shared_handle = shmem.handle().Duplicate(); - ASSERT_TRUE(shared_handle.IsValid()); - ASSERT_TRUE(shared_handle.OwnershipPassesToIPC()); - SharedMemory readonly(shared_handle, /*readonly=*/true); - - ASSERT_TRUE(readonly.Map(contents.size())); - EXPECT_EQ(contents, - StringPiece(static_cast(readonly.memory()), - contents.size())); -} - -TEST_P(SharedMemoryTest, ShareWithMultipleInstances) { - static const StringPiece kContents = "Hello World"; - - SharedMemory shmem; - ASSERT_TRUE(shmem.CreateAndMapAnonymous(kContents.size())); - // We do not need to unmap |shmem| to let |shared| map. - const StringPiece shmem_contents(static_cast(shmem.memory()), - shmem.requested_size()); - - SharedMemoryHandle shared_handle = shmem.handle().Duplicate(); - ASSERT_TRUE(shared_handle.IsValid()); - SharedMemory shared(shared_handle, /*readonly=*/false); - ASSERT_TRUE(shared.Map(kContents.size())); - // The underlying shared memory is created by |shmem|, so both - // |shared|.requested_size() and |readonly|.requested_size() are zero. - ASSERT_EQ(0U, shared.requested_size()); - const StringPiece shared_contents(static_cast(shared.memory()), - shmem.requested_size()); - - shared_handle = shmem.handle().Duplicate(); - ASSERT_TRUE(shared_handle.IsValid()); - ASSERT_TRUE(shared_handle.OwnershipPassesToIPC()); - SharedMemory readonly(shared_handle, /*readonly=*/true); - ASSERT_TRUE(readonly.Map(kContents.size())); - ASSERT_EQ(0U, readonly.requested_size()); - const StringPiece readonly_contents( - static_cast(readonly.memory()), - shmem.requested_size()); - - // |shmem| should be able to update the content. - memcpy(shmem.memory(), kContents.data(), kContents.size()); - - ASSERT_EQ(kContents, shmem_contents); - ASSERT_EQ(kContents, shared_contents); - ASSERT_EQ(kContents, readonly_contents); - - // |shared| should also be able to update the content. - memcpy(shared.memory(), ToLowerASCII(kContents).c_str(), kContents.size()); - - ASSERT_EQ(StringPiece(ToLowerASCII(kContents)), shmem_contents); - ASSERT_EQ(StringPiece(ToLowerASCII(kContents)), shared_contents); - ASSERT_EQ(StringPiece(ToLowerASCII(kContents)), readonly_contents); -} - -TEST_P(SharedMemoryTest, MapAt) { - ASSERT_TRUE(SysInfo::VMAllocationGranularity() >= sizeof(uint32_t)); - const size_t kCount = SysInfo::VMAllocationGranularity(); - const size_t kDataSize = kCount * sizeof(uint32_t); - - SharedMemory memory; - ASSERT_TRUE(memory.CreateAndMapAnonymous(kDataSize)); - uint32_t* ptr = static_cast(memory.memory()); - ASSERT_NE(ptr, static_cast(nullptr)); - - for (size_t i = 0; i < kCount; ++i) { - ptr[i] = i; - } - - memory.Unmap(); - - off_t offset = SysInfo::VMAllocationGranularity(); - ASSERT_TRUE(memory.MapAt(offset, kDataSize - offset)); - offset /= sizeof(uint32_t); - ptr = static_cast(memory.memory()); - ASSERT_NE(ptr, static_cast(nullptr)); - for (size_t i = offset; i < kCount; ++i) { - EXPECT_EQ(ptr[i - offset], i); - } -} - -TEST_P(SharedMemoryTest, MapTwice) { - const uint32_t 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) -// This test is not applicable for iOS (crbug.com/399384). -#if !defined(OS_IOS) -// Create a shared memory object, mmap it, and mprotect it to PROT_EXEC. -TEST_P(SharedMemoryTest, AnonymousExecutable) { -#if defined(OS_LINUX) - // On Chromecast both /dev/shm and /tmp are mounted with 'noexec' option, - // which makes this test fail. But Chromecast doesn't use NaCL so we don't - // need this. - if (!IsPathExecutable(FilePath("/dev/shm")) && - !IsPathExecutable(FilePath("/tmp"))) { - return; - } -#endif // OS_LINUX - const uint32_t kTestSize = 1 << 16; - - SharedMemory shared_memory; - SharedMemoryCreateOptions options; - options.size = kTestSize; - options.executable = true; -#if defined(OS_MACOSX) && !defined(OS_IOS) - // The Mach functionality is tested in shared_memory_mac_unittest.cc. - options.type = SharedMemoryHandle::POSIX; -#endif - - EXPECT_TRUE(shared_memory.Create(options)); - EXPECT_TRUE(shared_memory.Map(shared_memory.requested_size())); - - EXPECT_EQ(0, mprotect(shared_memory.memory(), shared_memory.requested_size(), - PROT_READ | PROT_EXEC)); -} -#endif // !defined(OS_IOS) - -#if defined(OS_ANDROID) -// This test is restricted to Android since there is no way on other platforms -// to guarantee that a region can never be mapped with PROT_EXEC. E.g. on -// Linux, anonymous shared regions come from /dev/shm which can be mounted -// without 'noexec'. In this case, anything can perform an mprotect() to -// change the protection mask of a given page. -TEST(SharedMemoryTest, AnonymousIsNotExecutableByDefault) { - const uint32_t kTestSize = 1 << 16; - - SharedMemory shared_memory; - SharedMemoryCreateOptions options; - options.size = kTestSize; - - EXPECT_TRUE(shared_memory.Create(options)); - EXPECT_TRUE(shared_memory.Map(shared_memory.requested_size())); - - errno = 0; - EXPECT_EQ(-1, mprotect(shared_memory.memory(), shared_memory.requested_size(), - PROT_READ | PROT_EXEC)); - EXPECT_EQ(EACCES, errno); -} -#endif // OS_ANDROID - -// Android supports a different permission model than POSIX for its "ashmem" -// shared memory implementation. So the tests about file permissions are not -// included on Android. Fuchsia does not use a file-backed shared memory -// implementation. - -#if !defined(OS_ANDROID) && !defined(OS_FUCHSIA) - -// Set a umask and restore the old mask on destruction. -class ScopedUmaskSetter { - public: - explicit ScopedUmaskSetter(mode_t target_mask) { - old_umask_ = umask(target_mask); - } - ~ScopedUmaskSetter() { umask(old_umask_); } - private: - mode_t old_umask_; - DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedUmaskSetter); -}; - -// Create a shared memory object, check its permissions. -TEST_P(SharedMemoryTest, FilePermissionsAnonymous) { - const uint32_t kTestSize = 1 << 8; - - SharedMemory shared_memory; - SharedMemoryCreateOptions options; - options.size = kTestSize; -#if defined(OS_MACOSX) && !defined(OS_IOS) - // The Mach functionality is tested in shared_memory_mac_unittest.cc. - options.type = SharedMemoryHandle::POSIX; -#endif - // Set a file mode creation mask that gives all permissions. - ScopedUmaskSetter permissive_mask(S_IWGRP | S_IWOTH); - - EXPECT_TRUE(shared_memory.Create(options)); - - int shm_fd = - SharedMemory::GetFdFromSharedMemoryHandle(shared_memory.handle()); - struct stat shm_stat; - EXPECT_EQ(0, fstat(shm_fd, &shm_stat)); - // Neither the group, nor others should be able to read the shared memory - // file. - EXPECT_FALSE(shm_stat.st_mode & S_IRWXO); - EXPECT_FALSE(shm_stat.st_mode & S_IRWXG); -} - -// Create a shared memory object, check its permissions. -TEST_P(SharedMemoryTest, FilePermissionsNamed) { - const uint32_t kTestSize = 1 << 8; - - SharedMemory shared_memory; - SharedMemoryCreateOptions options; - options.size = kTestSize; -#if defined(OS_MACOSX) && !defined(OS_IOS) - // The Mach functionality is tested in shared_memory_mac_unittest.cc. - options.type = SharedMemoryHandle::POSIX; -#endif - - // Set a file mode creation mask that gives all permissions. - ScopedUmaskSetter permissive_mask(S_IWGRP | S_IWOTH); - - EXPECT_TRUE(shared_memory.Create(options)); - - int fd = SharedMemory::GetFdFromSharedMemoryHandle(shared_memory.handle()); - struct stat shm_stat; - EXPECT_EQ(0, fstat(fd, &shm_stat)); - // Neither the group, nor others should have been able to open the shared - // memory file while its name existed. - EXPECT_FALSE(shm_stat.st_mode & S_IRWXO); - EXPECT_FALSE(shm_stat.st_mode & S_IRWXG); -} -#endif // !defined(OS_ANDROID) && !defined(OS_FUCHSIA) - -#endif // defined(OS_POSIX) - -// Map() will return addresses which are aligned to the platform page size, this -// varies from platform to platform though. Since we'd like to advertise a -// minimum alignment that callers can count on, test for it here. -TEST_P(SharedMemoryTest, MapMinimumAlignment) { - static const int kDataSize = 8192; - - SharedMemory shared_memory; - ASSERT_TRUE(shared_memory.CreateAndMapAnonymous(kDataSize)); - EXPECT_EQ(0U, reinterpret_cast( - shared_memory.memory()) & (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1)); - shared_memory.Close(); -} - -#if defined(OS_WIN) -TEST_P(SharedMemoryTest, UnsafeImageSection) { - const char kTestSectionName[] = "UnsafeImageSection"; - wchar_t path[MAX_PATH]; - EXPECT_GT(::GetModuleFileName(nullptr, path, arraysize(path)), 0U); - - // Map the current executable image to save us creating a new PE file on disk. - base::win::ScopedHandle file_handle(::CreateFile( - path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr)); - EXPECT_TRUE(file_handle.IsValid()); - base::win::ScopedHandle section_handle( - ::CreateFileMappingA(file_handle.Get(), nullptr, - PAGE_READONLY | SEC_IMAGE, 0, 0, kTestSectionName)); - EXPECT_TRUE(section_handle.IsValid()); - - // Check direct opening by name, from handle and duplicated from handle. - SharedMemory shared_memory_open; - EXPECT_TRUE(shared_memory_open.Open(kTestSectionName, true)); - EXPECT_FALSE(shared_memory_open.Map(1)); - EXPECT_EQ(nullptr, shared_memory_open.memory()); - - SharedMemory shared_memory_handle_local( - SharedMemoryHandle(section_handle.Take(), 1, UnguessableToken::Create()), - true); - EXPECT_FALSE(shared_memory_handle_local.Map(1)); - EXPECT_EQ(nullptr, shared_memory_handle_local.memory()); - - // Check that a handle without SECTION_QUERY also can't be mapped as it can't - // be checked. - SharedMemory shared_memory_handle_dummy; - SharedMemoryCreateOptions options; - options.size = 0x1000; - EXPECT_TRUE(shared_memory_handle_dummy.Create(options)); - HANDLE handle_no_query; - EXPECT_TRUE(::DuplicateHandle( - ::GetCurrentProcess(), shared_memory_handle_dummy.handle().GetHandle(), - ::GetCurrentProcess(), &handle_no_query, FILE_MAP_READ, FALSE, 0)); - SharedMemory shared_memory_handle_no_query( - SharedMemoryHandle(handle_no_query, options.size, - UnguessableToken::Create()), - true); - EXPECT_FALSE(shared_memory_handle_no_query.Map(1)); - EXPECT_EQ(nullptr, shared_memory_handle_no_query.memory()); -} -#endif // defined(OS_WIN) - -// iOS does not allow multiple processes. -// Android ashmem does not support named shared memory. -// Fuchsia SharedMemory does not support named shared memory. -// Mac SharedMemory does not support named shared memory. crbug.com/345734 -#if !defined(OS_IOS) && !defined(OS_ANDROID) && !defined(OS_MACOSX) && \ - !defined(OS_FUCHSIA) -// On POSIX it is especially important we test shmem across processes, -// not just across threads. But the test is enabled on all platforms. -class SharedMemoryProcessTest : public MultiProcessTest { - public: - static void CleanUp() { - SharedMemory memory; - memory.Delete(s_test_name_); - } - - static int TaskTestMain() { - int errors = 0; - SharedMemory memory; - bool rv = memory.CreateNamedDeprecated(s_test_name_, true, s_data_size_); - EXPECT_TRUE(rv); - if (rv != true) - errors++; - rv = memory.Map(s_data_size_); - EXPECT_TRUE(rv); - if (rv != true) - errors++; - int* ptr = static_cast(memory.memory()); - - // This runs concurrently in multiple processes. Writes need to be atomic. - subtle::Barrier_AtomicIncrement(ptr, 1); - memory.Close(); - return errors; - } - - static const char s_test_name_[]; - static const uint32_t s_data_size_; -}; - -const char SharedMemoryProcessTest::s_test_name_[] = "MPMem"; -const uint32_t SharedMemoryProcessTest::s_data_size_ = 1024; - -TEST_F(SharedMemoryProcessTest, SharedMemoryAcrossProcesses) { - const int kNumTasks = 5; - - SharedMemoryProcessTest::CleanUp(); - - // Create a shared memory region. Set the first word to 0. - SharedMemory memory; - bool rv = memory.CreateNamedDeprecated(s_test_name_, true, s_data_size_); - ASSERT_TRUE(rv); - rv = memory.Map(s_data_size_); - ASSERT_TRUE(rv); - int* ptr = static_cast(memory.memory()); - *ptr = 0; - - // Start |kNumTasks| processes, each of which atomically increments the first - // word by 1. - Process processes[kNumTasks]; - for (int index = 0; index < kNumTasks; ++index) { - processes[index] = SpawnChild("SharedMemoryTestMain"); - ASSERT_TRUE(processes[index].IsValid()); - } - - // Check that each process exited correctly. - int exit_code = 0; - for (int index = 0; index < kNumTasks; ++index) { - EXPECT_TRUE(processes[index].WaitForExit(&exit_code)); - EXPECT_EQ(0, exit_code); - } - - // Check that the shared memory region reflects |kNumTasks| increments. - ASSERT_EQ(kNumTasks, *ptr); - - memory.Close(); - SharedMemoryProcessTest::CleanUp(); -} - -MULTIPROCESS_TEST_MAIN(SharedMemoryTestMain) { - return SharedMemoryProcessTest::TaskTestMain(); -} -#endif // !defined(OS_IOS) && !defined(OS_ANDROID) && !defined(OS_MACOSX) && - // !defined(OS_FUCHSIA) - -TEST_P(SharedMemoryTest, MappedId) { - const uint32_t kDataSize = 1024; - SharedMemory memory; - SharedMemoryCreateOptions options; - options.size = kDataSize; -#if defined(OS_MACOSX) && !defined(OS_IOS) - // The Mach functionality is tested in shared_memory_mac_unittest.cc. - options.type = SharedMemoryHandle::POSIX; -#endif - - EXPECT_TRUE(memory.Create(options)); - base::UnguessableToken id = memory.handle().GetGUID(); - EXPECT_FALSE(id.is_empty()); - EXPECT_TRUE(memory.mapped_id().is_empty()); - - EXPECT_TRUE(memory.Map(kDataSize)); - EXPECT_EQ(id, memory.mapped_id()); - - memory.Close(); - EXPECT_EQ(id, memory.mapped_id()); - - memory.Unmap(); - EXPECT_TRUE(memory.mapped_id().is_empty()); -} - -INSTANTIATE_TEST_CASE_P(Default, - SharedMemoryTest, - ::testing::Values(Mode::Default)); -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -INSTANTIATE_TEST_CASE_P(SkipDevShm, - SharedMemoryTest, - ::testing::Values(Mode::DisableDevShm)); -#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) - -#if defined(OS_ANDROID) -TEST(SharedMemoryTest, ReadOnlyRegions) { - const uint32_t kDataSize = 1024; - SharedMemory memory; - SharedMemoryCreateOptions options; - options.size = kDataSize; - EXPECT_TRUE(memory.Create(options)); - - EXPECT_FALSE(memory.handle().IsRegionReadOnly()); - - // Check that it is possible to map the region directly from the fd. - int region_fd = memory.handle().GetHandle(); - EXPECT_GE(region_fd, 0); - void* address = mmap(nullptr, kDataSize, PROT_READ | PROT_WRITE, MAP_SHARED, - region_fd, 0); - bool success = address && address != MAP_FAILED; - ASSERT_TRUE(address); - ASSERT_NE(address, MAP_FAILED); - if (success) { - EXPECT_EQ(0, munmap(address, kDataSize)); - } - - ASSERT_TRUE(memory.handle().SetRegionReadOnly()); - EXPECT_TRUE(memory.handle().IsRegionReadOnly()); - - // Check that it is no longer possible to map the region read/write. - errno = 0; - address = mmap(nullptr, kDataSize, PROT_READ | PROT_WRITE, MAP_SHARED, - region_fd, 0); - success = address && address != MAP_FAILED; - ASSERT_FALSE(success); - ASSERT_EQ(EPERM, errno); - if (success) { - EXPECT_EQ(0, munmap(address, kDataSize)); - } -} - -TEST(SharedMemoryTest, ReadOnlyDescriptors) { - const uint32_t kDataSize = 1024; - SharedMemory memory; - SharedMemoryCreateOptions options; - options.size = kDataSize; - EXPECT_TRUE(memory.Create(options)); - - EXPECT_FALSE(memory.handle().IsRegionReadOnly()); - - // Getting a read-only descriptor should not make the region read-only itself. - SharedMemoryHandle ro_handle = memory.GetReadOnlyHandle(); - EXPECT_FALSE(memory.handle().IsRegionReadOnly()); - - // Mapping a writable region from a read-only descriptor should not - // be possible, it will DCHECK() in debug builds (see test below), - // while returning false on release ones. - { - bool dcheck_fired = false; - logging::ScopedLogAssertHandler log_assert( - base::BindRepeating([](bool* flag, const char*, int, base::StringPiece, - base::StringPiece) { *flag = true; }, - base::Unretained(&dcheck_fired))); - - SharedMemory rw_region(ro_handle.Duplicate(), /* read_only */ false); - EXPECT_FALSE(rw_region.Map(kDataSize)); - EXPECT_EQ(DCHECK_IS_ON() ? true : false, dcheck_fired); - } - - // Nor shall it turn the region read-only itself. - EXPECT_FALSE(ro_handle.IsRegionReadOnly()); - - // Mapping a read-only region from a read-only descriptor should work. - SharedMemory ro_region(ro_handle.Duplicate(), /* read_only */ true); - EXPECT_TRUE(ro_region.Map(kDataSize)); - - // And it should turn the region read-only too. - EXPECT_TRUE(ro_handle.IsRegionReadOnly()); - EXPECT_TRUE(memory.handle().IsRegionReadOnly()); - EXPECT_FALSE(memory.Map(kDataSize)); - - ro_handle.Close(); -} - -#endif // OS_ANDROID - -} // namespace base diff --git a/memory/shared_memory_win.cc b/memory/shared_memory_win.cc deleted file mode 100644 index cf06dd39c..000000000 --- a/memory/shared_memory_win.cc +++ /dev/null @@ -1,381 +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/memory/shared_memory.h" - -#include -#include -#include - -#include "base/allocator/partition_allocator/page_allocator.h" -#include "base/logging.h" -#include "base/memory/shared_memory_tracker.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" -#include "base/rand_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/unguessable_token.h" - -namespace base { -namespace { - -// Errors that can occur during Shared Memory construction. -// These match tools/metrics/histograms/histograms.xml. -// This enum is append-only. -enum CreateError { - SUCCESS = 0, - SIZE_ZERO = 1, - SIZE_TOO_LARGE = 2, - INITIALIZE_ACL_FAILURE = 3, - INITIALIZE_SECURITY_DESC_FAILURE = 4, - SET_SECURITY_DESC_FAILURE = 5, - CREATE_FILE_MAPPING_FAILURE = 6, - REDUCE_PERMISSIONS_FAILURE = 7, - ALREADY_EXISTS = 8, - CREATE_ERROR_LAST = ALREADY_EXISTS -}; - -// Emits UMA metrics about encountered errors. Pass zero (0) for |winerror| -// if there is no associated Windows error. -void LogError(CreateError error, DWORD winerror) { - UMA_HISTOGRAM_ENUMERATION("SharedMemory.CreateError", error, - CREATE_ERROR_LAST + 1); - static_assert(ERROR_SUCCESS == 0, "Windows error code changed!"); - if (winerror != ERROR_SUCCESS) - UmaHistogramSparse("SharedMemory.CreateWinError", winerror); -} - -typedef enum _SECTION_INFORMATION_CLASS { - SectionBasicInformation, -} SECTION_INFORMATION_CLASS; - -typedef struct _SECTION_BASIC_INFORMATION { - PVOID BaseAddress; - ULONG Attributes; - LARGE_INTEGER Size; -} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; - -typedef ULONG(__stdcall* NtQuerySectionType)( - HANDLE SectionHandle, - SECTION_INFORMATION_CLASS SectionInformationClass, - PVOID SectionInformation, - ULONG SectionInformationLength, - PULONG ResultLength); - -// Returns the length of the memory section starting at the supplied address. -size_t GetMemorySectionSize(void* address) { - MEMORY_BASIC_INFORMATION memory_info; - if (!::VirtualQuery(address, &memory_info, sizeof(memory_info))) - return 0; - return memory_info.RegionSize - (static_cast(address) - - static_cast(memory_info.AllocationBase)); -} - -// Checks if the section object is safe to map. At the moment this just means -// it's not an image section. -bool IsSectionSafeToMap(HANDLE handle) { - static NtQuerySectionType nt_query_section_func; - if (!nt_query_section_func) { - nt_query_section_func = reinterpret_cast( - ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection")); - DCHECK(nt_query_section_func); - } - - // The handle must have SECTION_QUERY access for this to succeed. - SECTION_BASIC_INFORMATION basic_information = {}; - ULONG status = - nt_query_section_func(handle, SectionBasicInformation, &basic_information, - sizeof(basic_information), nullptr); - if (status) - return false; - return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE; -} - -// Returns a HANDLE on success and |nullptr| on failure. -// This function is similar to CreateFileMapping, but removes the permissions -// WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE. -// -// A newly created file mapping has two sets of permissions. It has access -// control permissions (WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE) and -// file permissions (FILE_MAP_READ, FILE_MAP_WRITE, etc.). ::DuplicateHandle() -// with the parameter DUPLICATE_SAME_ACCESS copies both sets of permissions. -// -// The Chrome sandbox prevents HANDLEs with the WRITE_DAC permission from being -// duplicated into unprivileged processes. But the only way to copy file -// permissions is with the parameter DUPLICATE_SAME_ACCESS. This means that -// there is no way for a privileged process to duplicate a file mapping into an -// unprivileged process while maintaining the previous file permissions. -// -// By removing all access control permissions of a file mapping immediately -// after creation, ::DuplicateHandle() effectively only copies the file -// permissions. -HANDLE CreateFileMappingWithReducedPermissions(SECURITY_ATTRIBUTES* sa, - size_t rounded_size, - LPCWSTR name) { - HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, sa, PAGE_READWRITE, 0, - static_cast(rounded_size), name); - if (!h) { - LogError(CREATE_FILE_MAPPING_FAILURE, GetLastError()); - return nullptr; - } - - HANDLE h2; - BOOL success = ::DuplicateHandle( - GetCurrentProcess(), h, GetCurrentProcess(), &h2, - FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY, FALSE, 0); - BOOL rv = ::CloseHandle(h); - DCHECK(rv); - - if (!success) { - LogError(REDUCE_PERMISSIONS_FAILURE, GetLastError()); - return nullptr; - } - - return h2; -} - -} // namespace. - -SharedMemory::SharedMemory() {} - -SharedMemory::SharedMemory(const string16& name) : name_(name) {} - -SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) - : external_section_(true), shm_(handle), read_only_(read_only) {} - -SharedMemory::~SharedMemory() { - Unmap(); - Close(); -} - -// static -bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { - return handle.IsValid(); -} - -// static -void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { - handle.Close(); -} - -// static -size_t SharedMemory::GetHandleLimit() { - // Rounded down from value reported here: - // http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx - return static_cast(1 << 23); -} - -// static -SharedMemoryHandle SharedMemory::DuplicateHandle( - const SharedMemoryHandle& handle) { - return handle.Duplicate(); -} - -bool SharedMemory::CreateAndMapAnonymous(size_t size) { - return CreateAnonymous(size) && Map(size); -} - -bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { - // TODO(crbug.com/210609): NaCl forces us to round up 64k here, wasting 32k - // per mapping on average. - static const size_t kSectionMask = 65536 - 1; - DCHECK(!options.executable); - DCHECK(!shm_.IsValid()); - if (options.size == 0) { - LogError(SIZE_ZERO, 0); - return false; - } - - // Check maximum accounting for overflow. - if (options.size > - static_cast(std::numeric_limits::max()) - kSectionMask) { - LogError(SIZE_TOO_LARGE, 0); - return false; - } - - size_t rounded_size = (options.size + kSectionMask) & ~kSectionMask; - name_ = options.name_deprecated ? - ASCIIToUTF16(*options.name_deprecated) : L""; - SECURITY_ATTRIBUTES sa = {sizeof(sa), nullptr, FALSE}; - SECURITY_DESCRIPTOR sd; - ACL dacl; - - if (name_.empty()) { - // Add an empty DACL to enforce anonymous read-only sections. - sa.lpSecurityDescriptor = &sd; - if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) { - LogError(INITIALIZE_ACL_FAILURE, GetLastError()); - return false; - } - if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { - LogError(INITIALIZE_SECURITY_DESC_FAILURE, GetLastError()); - return false; - } - if (!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE)) { - LogError(SET_SECURITY_DESC_FAILURE, GetLastError()); - return false; - } - - // Windows ignores DACLs on certain unnamed objects (like shared sections). - // So, we generate a random name when we need to enforce read-only. - uint64_t rand_values[4]; - RandBytes(&rand_values, sizeof(rand_values)); - name_ = StringPrintf(L"CrSharedMem_%016llx%016llx%016llx%016llx", - rand_values[0], rand_values[1], - rand_values[2], rand_values[3]); - } - DCHECK(!name_.empty()); - shm_ = SharedMemoryHandle( - CreateFileMappingWithReducedPermissions(&sa, rounded_size, name_.c_str()), - rounded_size, UnguessableToken::Create()); - if (!shm_.IsValid()) { - // The error is logged within CreateFileMappingWithReducedPermissions(). - return false; - } - - requested_size_ = options.size; - - // Check if the shared memory pre-exists. - if (GetLastError() == ERROR_ALREADY_EXISTS) { - // If the file already existed, set requested_size_ to 0 to show that - // we don't know the size. - requested_size_ = 0; - external_section_ = true; - if (!options.open_existing_deprecated) { - Close(); - // From "if" above: GetLastError() == ERROR_ALREADY_EXISTS. - LogError(ALREADY_EXISTS, ERROR_ALREADY_EXISTS); - return false; - } - } - - LogError(SUCCESS, ERROR_SUCCESS); - return true; -} - -bool SharedMemory::Delete(const std::string& name) { - // intentionally empty -- there is nothing for us to do on Windows. - return true; -} - -bool SharedMemory::Open(const std::string& name, bool read_only) { - DCHECK(!shm_.IsValid()); - DWORD access = FILE_MAP_READ | SECTION_QUERY; - if (!read_only) - access |= FILE_MAP_WRITE; - name_ = ASCIIToUTF16(name); - read_only_ = read_only; - - // This form of sharing shared memory is deprecated. https://crbug.com/345734. - // However, we can't get rid of it without a significant refactor because its - // used to communicate between two versions of the same service process, very - // early in the life cycle. - // Technically, we should also pass the GUID from the original shared memory - // region. We don't do that - this means that we will overcount this memory, - // which thankfully isn't relevant since Chrome only communicates with a - // single version of the service process. - // We pass the size |0|, which is a dummy size and wrong, but otherwise - // harmless. - shm_ = SharedMemoryHandle( - OpenFileMapping(access, false, name_.empty() ? nullptr : name_.c_str()), - 0u, UnguessableToken::Create()); - if (!shm_.IsValid()) - return false; - // If a name specified assume it's an external section. - if (!name_.empty()) - external_section_ = true; - // Note: size_ is not set in this case. - return true; -} - -bool SharedMemory::MapAt(off_t offset, size_t bytes) { - if (!shm_.IsValid()) { - DLOG(ERROR) << "Invalid SharedMemoryHandle."; - return false; - } - - if (bytes > static_cast(std::numeric_limits::max())) { - DLOG(ERROR) << "Bytes required exceeds the 2G limitation."; - return false; - } - - if (memory_) { - DLOG(ERROR) << "The SharedMemory has been mapped already."; - return false; - } - - if (external_section_ && !IsSectionSafeToMap(shm_.GetHandle())) { - DLOG(ERROR) << "SharedMemoryHandle is not safe to be mapped."; - return false; - } - - // Try to map the shared memory. On the first failure, release any reserved - // address space for a single retry. - for (int i = 0; i < 2; ++i) { - memory_ = MapViewOfFile( - shm_.GetHandle(), - read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, - static_cast(offset) >> 32, static_cast(offset), bytes); - if (memory_) - break; - ReleaseReservation(); - } - if (!memory_) { - DPLOG(ERROR) << "Failed executing MapViewOfFile"; - return false; - } - - DCHECK_EQ(0U, reinterpret_cast(memory_) & - (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1)); - mapped_size_ = GetMemorySectionSize(memory_); - mapped_id_ = shm_.GetGUID(); - SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); - return true; -} - -bool SharedMemory::Unmap() { - if (!memory_) - return false; - - SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); - UnmapViewOfFile(memory_); - memory_ = nullptr; - mapped_id_ = UnguessableToken(); - return true; -} - -SharedMemoryHandle SharedMemory::GetReadOnlyHandle() const { - HANDLE result; - ProcessHandle process = GetCurrentProcess(); - if (!::DuplicateHandle(process, shm_.GetHandle(), process, &result, - FILE_MAP_READ | SECTION_QUERY, FALSE, 0)) { - return SharedMemoryHandle(); - } - SharedMemoryHandle handle = - SharedMemoryHandle(result, shm_.GetSize(), shm_.GetGUID()); - handle.SetOwnershipPassesToIPC(true); - return handle; -} - -void SharedMemory::Close() { - if (shm_.IsValid()) { - shm_.Close(); - shm_ = SharedMemoryHandle(); - } -} - -SharedMemoryHandle SharedMemory::handle() const { - return shm_; -} - -SharedMemoryHandle SharedMemory::TakeHandle() { - SharedMemoryHandle handle(shm_); - handle.SetOwnershipPassesToIPC(true); - Unmap(); - shm_ = SharedMemoryHandle(); - return handle; -} - -} // namespace base diff --git a/memory/shared_memory_win_unittest.cc b/memory/shared_memory_win_unittest.cc deleted file mode 100644 index 5fc132d25..000000000 --- a/memory/shared_memory_win_unittest.cc +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2016 The Chromium 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 -#include - -#include - -#include "base/command_line.h" -#include "base/memory/free_deleter.h" -#include "base/memory/shared_memory.h" -#include "base/process/process.h" -#include "base/rand_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/sys_string_conversions.h" -#include "base/test/multiprocess_test.h" -#include "base/test/test_timeouts.h" -#include "base/win/scoped_handle.h" -#include "base/win/win_util.h" -#include "testing/multiprocess_func_list.h" - -namespace base { -namespace { -const char* kHandleSwitchName = "shared_memory_win_test_switch"; - -// Creates a process token with a low integrity SID. -win::ScopedHandle CreateLowIntegritySID() { - HANDLE process_token_raw = nullptr; - BOOL success = ::OpenProcessToken(GetCurrentProcess(), - TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | - TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, - &process_token_raw); - if (!success) - return base::win::ScopedHandle(); - win::ScopedHandle process_token(process_token_raw); - - HANDLE lowered_process_token_raw = nullptr; - success = - ::DuplicateTokenEx(process_token.Get(), 0, NULL, SecurityImpersonation, - TokenPrimary, &lowered_process_token_raw); - if (!success) - return base::win::ScopedHandle(); - win::ScopedHandle lowered_process_token(lowered_process_token_raw); - - // Low integrity SID - WCHAR integrity_sid_string[20] = L"S-1-16-4096"; - PSID integrity_sid = nullptr; - success = ::ConvertStringSidToSid(integrity_sid_string, &integrity_sid); - if (!success) - return base::win::ScopedHandle(); - - TOKEN_MANDATORY_LABEL TIL = {}; - TIL.Label.Attributes = SE_GROUP_INTEGRITY; - TIL.Label.Sid = integrity_sid; - success = ::SetTokenInformation( - lowered_process_token.Get(), TokenIntegrityLevel, &TIL, - sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(integrity_sid)); - if (!success) - return base::win::ScopedHandle(); - return lowered_process_token; -} - -// Reads a HANDLE from the pipe as a raw int, least significant digit first. -win::ScopedHandle ReadHandleFromPipe(HANDLE pipe) { - // Read from parent pipe. - const size_t buf_size = 1000; - char buffer[buf_size]; - memset(buffer, 0, buf_size); - DWORD bytes_read; - BOOL success = ReadFile(pipe, buffer, buf_size, &bytes_read, NULL); - - if (!success || bytes_read == 0) { - LOG(ERROR) << "Failed to read handle from pipe."; - return win::ScopedHandle(); - } - - int handle_as_int = 0; - int power_of_ten = 1; - for (unsigned int i = 0; i < bytes_read; ++i) { - handle_as_int += buffer[i] * power_of_ten; - power_of_ten *= 10; - } - - return win::ScopedHandle(reinterpret_cast(handle_as_int)); -} - -// Writes a HANDLE to a pipe as a raw int, least significant digit first. -void WriteHandleToPipe(HANDLE pipe, HANDLE handle) { - uint32_t handle_as_int = base::win::HandleToUint32(handle); - - std::unique_ptr buffer( - static_cast(malloc(1000))); - size_t index = 0; - while (handle_as_int > 0) { - buffer.get()[index] = handle_as_int % 10; - handle_as_int /= 10; - ++index; - } - - ::ConnectNamedPipe(pipe, nullptr); - DWORD written; - ASSERT_TRUE(::WriteFile(pipe, buffer.get(), index, &written, NULL)); -} - -// Creates a communication pipe with the given name. -win::ScopedHandle CreateCommunicationPipe(const std::wstring& name) { - return win::ScopedHandle(CreateNamedPipe(name.c_str(), // pipe name - PIPE_ACCESS_DUPLEX, PIPE_WAIT, 255, - 1000, 1000, 0, NULL)); -} - -// Generates a random name for a communication pipe. -std::wstring CreateCommunicationPipeName() { - uint64_t rand_values[4]; - RandBytes(&rand_values, sizeof(rand_values)); - std::wstring child_pipe_name = StringPrintf( - L"\\\\.\\pipe\\SharedMemoryWinTest_%016llx%016llx%016llx%016llx", - rand_values[0], rand_values[1], rand_values[2], rand_values[3]); - return child_pipe_name; -} - -class SharedMemoryWinTest : public base::MultiProcessTest { - protected: - CommandLine MakeCmdLine(const std::string& procname) override { - CommandLine line = base::MultiProcessTest::MakeCmdLine(procname); - line.AppendSwitchASCII(kHandleSwitchName, communication_pipe_name_); - return line; - } - - std::string communication_pipe_name_; -}; - -MULTIPROCESS_TEST_MAIN(LowerPermissions) { - std::string handle_name = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kHandleSwitchName); - std::wstring handle_name16 = SysUTF8ToWide(handle_name); - win::ScopedHandle parent_pipe( - ::CreateFile(handle_name16.c_str(), // pipe name - GENERIC_READ, - 0, // no sharing - NULL, // default security attributes - OPEN_EXISTING, // opens existing pipe - 0, // default attributes - NULL)); // no template file - if (parent_pipe.Get() == INVALID_HANDLE_VALUE) { - LOG(ERROR) << "Failed to open communication pipe."; - return 1; - } - - win::ScopedHandle received_handle = ReadHandleFromPipe(parent_pipe.Get()); - if (!received_handle.Get()) { - LOG(ERROR) << "Failed to read handle from pipe."; - return 1; - } - - // Attempting to add the WRITE_DAC permission should fail. - HANDLE duped_handle; - BOOL success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), - GetCurrentProcess(), &duped_handle, - FILE_MAP_READ | WRITE_DAC, FALSE, 0); - if (success) { - LOG(ERROR) << "Should not have been able to add WRITE_DAC permission."; - return 1; - } - - // Attempting to add the FILE_MAP_WRITE permission should fail. - success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), - GetCurrentProcess(), &duped_handle, - FILE_MAP_READ | FILE_MAP_WRITE, FALSE, 0); - if (success) { - LOG(ERROR) << "Should not have been able to add FILE_MAP_WRITE permission."; - return 1; - } - - // Attempting to duplicate the HANDLE with the same permissions should - // succeed. - success = ::DuplicateHandle(GetCurrentProcess(), received_handle.Get(), - GetCurrentProcess(), &duped_handle, FILE_MAP_READ, - FALSE, 0); - if (!success) { - LOG(ERROR) << "Failed to duplicate handle."; - return 4; - } - ::CloseHandle(duped_handle); - return 0; -} - -TEST_F(SharedMemoryWinTest, LowerPermissions) { - std::wstring communication_pipe_name = CreateCommunicationPipeName(); - communication_pipe_name_ = SysWideToUTF8(communication_pipe_name); - - win::ScopedHandle communication_pipe = - CreateCommunicationPipe(communication_pipe_name); - ASSERT_TRUE(communication_pipe.Get()); - - win::ScopedHandle lowered_process_token = CreateLowIntegritySID(); - ASSERT_TRUE(lowered_process_token.Get()); - - base::LaunchOptions options; - options.as_user = lowered_process_token.Get(); - base::Process process = SpawnChildWithOptions("LowerPermissions", options); - ASSERT_TRUE(process.IsValid()); - - SharedMemory memory; - memory.CreateAndMapAnonymous(1001); - - // Duplicate into child process, giving only FILE_MAP_READ permissions. - HANDLE raw_handle = nullptr; - ::DuplicateHandle(::GetCurrentProcess(), memory.handle().GetHandle(), - process.Handle(), &raw_handle, - FILE_MAP_READ | SECTION_QUERY, FALSE, 0); - ASSERT_TRUE(raw_handle); - - WriteHandleToPipe(communication_pipe.Get(), raw_handle); - - int exit_code; - EXPECT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(), - &exit_code)); - EXPECT_EQ(0, exit_code); -} - -} // namespace -} // namespace base diff --git a/memory/singleton.h b/memory/singleton.h deleted file mode 100644 index 880ef0a58..000000000 --- a/memory/singleton.h +++ /dev/null @@ -1,262 +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. -// -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// PLEASE READ: Do you really need a singleton? If possible, use a -// function-local static of type base::NoDestructor instead: -// -// Factory& Factory::GetInstance() { -// static base::NoDestructor instance; -// return *instance; -// } -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// -// Singletons make it hard to determine the lifetime of an object, which can -// lead to buggy code and spurious crashes. -// -// Instead of adding another singleton into the mix, try to identify either: -// a) An existing singleton that can manage your object's lifetime -// b) Locations where you can deterministically create the object and pass -// into other objects -// -// If you absolutely need a singleton, please keep them as trivial as possible -// and ideally a leaf dependency. Singletons get problematic when they attempt -// to do too much in their destructor or have circular dependencies. - -#ifndef BASE_MEMORY_SINGLETON_H_ -#define BASE_MEMORY_SINGLETON_H_ - -#include "base/at_exit.h" -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/lazy_instance_helpers.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/threading/thread_restrictions.h" - -namespace base { - -// Default traits for Singleton. Calls operator new and operator delete on -// the object. Registers automatic deletion at process exit. -// Overload if you need arguments or another memory allocation function. -template -struct DefaultSingletonTraits { - // Allocates the object. - static Type* New() { - // The parenthesis is very important here; it forces POD type - // initialization. - return new Type(); - } - - // Destroys the object. - static void Delete(Type* x) { - delete x; - } - - // Set to true to automatically register deletion of the object on process - // exit. See below for the required call that makes this happen. - static const bool kRegisterAtExit = true; - -#if DCHECK_IS_ON() - // 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 -}; - - -// Alternate traits for use with the Singleton. Identical to -// DefaultSingletonTraits except that the Singleton will not be cleaned up -// at exit. -template -struct LeakySingletonTraits : public DefaultSingletonTraits { - static const bool kRegisterAtExit = false; -#if DCHECK_IS_ON() - static const bool kAllowedToAccessOnNonjoinableThread = true; -#endif -}; - -// Alternate traits for use with the Singleton. Allocates memory -// for the singleton instance from a static buffer. The singleton will -// be cleaned up at exit, but can't be revived after destruction unless -// the ResurrectForTesting() method is called. -// -// This is useful for a certain category of things, notably logging and -// tracing, where the singleton instance is of a type carefully constructed to -// be safe to access post-destruction. -// In logging and tracing you'll typically get stray calls at odd times, like -// during static destruction, thread teardown and the like, and there's a -// termination race on the heap-based singleton - e.g. if one thread calls -// get(), but then another thread initiates AtExit processing, the first thread -// may call into an object residing in unallocated memory. If the instance is -// allocated from the data segment, then this is survivable. -// -// The destructor is to deallocate system resources, in this case to unregister -// a callback the system will invoke when logging levels change. Note that -// this is also used in e.g. Chrome Frame, where you have to allow for the -// possibility of loading briefly into someone else's process space, and -// so leaking is not an option, as that would sabotage the state of your host -// process once you've unloaded. -template -struct StaticMemorySingletonTraits { - // WARNING: User has to support a New() which returns null. - static Type* New() { - // Only constructs once and returns pointer; otherwise returns null. - if (subtle::NoBarrier_AtomicExchange(&dead_, 1)) - return nullptr; - - return new (buffer_) Type(); - } - - static void Delete(Type* p) { - if (p) - p->Type::~Type(); - } - - static const bool kRegisterAtExit = true; - -#if DCHECK_IS_ON() - static const bool kAllowedToAccessOnNonjoinableThread = true; -#endif - - static void ResurrectForTesting() { subtle::NoBarrier_Store(&dead_, 0); } - - private: - alignas(Type) static char buffer_[sizeof(Type)]; - // Signal the object was already deleted, so it is not revived. - static subtle::Atomic32 dead_; -}; - -template -alignas(Type) char StaticMemorySingletonTraits::buffer_[sizeof(Type)]; -template -subtle::Atomic32 StaticMemorySingletonTraits::dead_ = 0; - -// The Singleton class manages a single -// instance of Type which will be created on first use and will be destroyed at -// normal process exit). The Trait::Delete function will not be called on -// abnormal process exit. -// -// DifferentiatingType is used as a key to differentiate two different -// singletons having the same memory allocation functions but serving a -// different purpose. This is mainly used for Locks serving different purposes. -// -// Example usage: -// -// In your header: -// namespace base { -// template -// struct DefaultSingletonTraits; -// } -// class FooClass { -// public: -// static FooClass* GetInstance(); <-- See comment below on this. -// void Bar() { ... } -// private: -// FooClass() { ... } -// friend struct base::DefaultSingletonTraits; -// -// DISALLOW_COPY_AND_ASSIGN(FooClass); -// }; -// -// In your source file: -// #include "base/memory/singleton.h" -// FooClass* FooClass::GetInstance() { -// return base::Singleton::get(); -// } -// -// Or for leaky singletons: -// #include "base/memory/singleton.h" -// FooClass* FooClass::GetInstance() { -// return base::Singleton< -// FooClass, base::LeakySingletonTraits>::get(); -// } -// -// And to call methods on FooClass: -// FooClass::GetInstance()->Bar(); -// -// NOTE: The method accessing Singleton::get() has to be named as GetInstance -// and it is important that FooClass::GetInstance() is not inlined in the -// header. This makes sure that when source files from multiple targets include -// this header they don't end up with different copies of the inlined code -// creating multiple copies of the singleton. -// -// Singleton<> has no non-static members and doesn't need to actually be -// instantiated. -// -// This class is itself thread-safe. The underlying Type must of course be -// thread-safe if you want to use it concurrently. Two parameters may be tuned -// depending on the user's requirements. -// -// Glossary: -// RAE = kRegisterAtExit -// -// On every platform, if Traits::RAE is true, the singleton will be destroyed at -// process exit. More precisely it uses AtExitManager which requires an -// object of this type to be instantiated. AtExitManager mimics the semantics -// of atexit() such as LIFO order but under Windows is safer to call. For more -// information see at_exit.h. -// -// If Traits::RAE is false, the singleton will not be freed at process exit, -// thus the singleton will be leaked if it is ever accessed. Traits::RAE -// shouldn't be false unless absolutely necessary. Remember that the heap where -// the object is allocated may be destroyed by the CRT anyway. -// -// Caveats: -// (a) Every call to get(), operator->() and operator*() incurs some overhead -// (16ns on my P4/2.8GHz) to check whether the object has already been -// initialized. You may wish to cache the result of get(); it will not -// change. -// -// (b) Your factory function must never throw an exception. This class is not -// exception-safe. -// - -template , - typename DifferentiatingType = Type> -class Singleton { - private: - // Classes using the Singleton pattern should declare a GetInstance() - // method and call Singleton::get() from within that. - friend Type* Type::GetInstance(); - - // This class is safe to be constructed and copy-constructed since it has no - // member. - - // Return a pointer to the one true instance of the class. - static Type* get() { -#if DCHECK_IS_ON() - if (!Traits::kAllowedToAccessOnNonjoinableThread) - ThreadRestrictions::AssertSingletonAllowed(); -#endif - - return subtle::GetOrCreateLazyPointer( - &instance_, &CreatorFunc, nullptr, - Traits::kRegisterAtExit ? OnExit : nullptr, nullptr); - } - - // Internal method used as an adaptor for GetOrCreateLazyPointer(). Do not use - // outside of that use case. - static Type* CreatorFunc(void* /* creator_arg*/) { return Traits::New(); } - - // Adapter function for use with AtExit(). This should be called single - // threaded, so don't use atomic operations. - // Calling OnExit while singleton is in use by other threads is a mistake. - static void OnExit(void* /*unused*/) { - // AtExit should only ever be register after the singleton instance was - // created. We should only ever get here with a valid instance_ pointer. - Traits::Delete(reinterpret_cast(subtle::NoBarrier_Load(&instance_))); - instance_ = 0; - } - static subtle::AtomicWord instance_; -}; - -template -subtle::AtomicWord Singleton::instance_ = 0; - -} // namespace base - -#endif // BASE_MEMORY_SINGLETON_H_ diff --git a/memory/singleton_unittest.cc b/memory/singleton_unittest.cc deleted file mode 100644 index 06e53b24c..000000000 --- a/memory/singleton_unittest.cc +++ /dev/null @@ -1,299 +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 - -#include "base/at_exit.h" -#include "base/memory/singleton.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -static_assert(DefaultSingletonTraits::kRegisterAtExit == true, - "object must be deleted on process exit"); - -typedef void (*CallbackFunc)(); - -template -class AlignedData { - public: - AlignedData() = default; - ~AlignedData() = default; - alignas(alignment) char data_[alignment]; -}; - -class IntSingleton { - public: - static IntSingleton* GetInstance() { - return Singleton::get(); - } - - int value_; -}; - -class Init5Singleton { - public: - struct Trait; - - static Init5Singleton* GetInstance() { - return Singleton::get(); - } - - int value_; -}; - -struct Init5Singleton::Trait : public DefaultSingletonTraits { - static Init5Singleton* New() { - Init5Singleton* instance = new Init5Singleton(); - instance->value_ = 5; - return instance; - } -}; - -int* SingletonInt() { - return &IntSingleton::GetInstance()->value_; -} - -int* SingletonInt5() { - return &Init5Singleton::GetInstance()->value_; -} - -template -struct CallbackTrait : public DefaultSingletonTraits { - static void Delete(Type* instance) { - if (instance->callback_) - (instance->callback_)(); - DefaultSingletonTraits::Delete(instance); - } -}; - -class CallbackSingleton { - public: - CallbackSingleton() : callback_(nullptr) {} - CallbackFunc callback_; -}; - -class CallbackSingletonWithNoLeakTrait : public CallbackSingleton { - public: - struct Trait : public CallbackTrait { }; - - CallbackSingletonWithNoLeakTrait() : CallbackSingleton() { } - - static CallbackSingletonWithNoLeakTrait* GetInstance() { - return Singleton::get(); - } -}; - -class CallbackSingletonWithLeakTrait : public CallbackSingleton { - public: - struct Trait : public CallbackTrait { - static const bool kRegisterAtExit = false; - }; - - CallbackSingletonWithLeakTrait() : CallbackSingleton() { } - - static CallbackSingletonWithLeakTrait* GetInstance() { - return Singleton::get(); - } -}; - -class CallbackSingletonWithStaticTrait : public CallbackSingleton { - public: - struct Trait; - - CallbackSingletonWithStaticTrait() : CallbackSingleton() { } - - static CallbackSingletonWithStaticTrait* GetInstance() { - return Singleton::get(); - } -}; - -struct CallbackSingletonWithStaticTrait::Trait - : public StaticMemorySingletonTraits { - static void Delete(CallbackSingletonWithStaticTrait* instance) { - if (instance->callback_) - (instance->callback_)(); - StaticMemorySingletonTraits::Delete( - instance); - } -}; - -template -class AlignedTestSingleton { - public: - AlignedTestSingleton() = default; - ~AlignedTestSingleton() = default; - static AlignedTestSingleton* GetInstance() { - return Singleton>::get(); - } - - Type type_; -}; - - -void SingletonNoLeak(CallbackFunc CallOnQuit) { - CallbackSingletonWithNoLeakTrait::GetInstance()->callback_ = CallOnQuit; -} - -void SingletonLeak(CallbackFunc CallOnQuit) { - CallbackSingletonWithLeakTrait::GetInstance()->callback_ = CallOnQuit; -} - -CallbackFunc* GetLeakySingleton() { - return &CallbackSingletonWithLeakTrait::GetInstance()->callback_; -} - -void DeleteLeakySingleton() { - DefaultSingletonTraits::Delete( - CallbackSingletonWithLeakTrait::GetInstance()); -} - -void SingletonStatic(CallbackFunc CallOnQuit) { - CallbackSingletonWithStaticTrait::GetInstance()->callback_ = CallOnQuit; -} - -CallbackFunc* GetStaticSingleton() { - return &CallbackSingletonWithStaticTrait::GetInstance()->callback_; -} - - -class SingletonTest : public testing::Test { - public: - SingletonTest() = default; - - void SetUp() override { - non_leak_called_ = false; - leaky_called_ = false; - static_called_ = false; - } - - protected: - void VerifiesCallbacks() { - EXPECT_TRUE(non_leak_called_); - EXPECT_FALSE(leaky_called_); - EXPECT_TRUE(static_called_); - non_leak_called_ = false; - leaky_called_ = false; - static_called_ = false; - } - - void VerifiesCallbacksNotCalled() { - EXPECT_FALSE(non_leak_called_); - EXPECT_FALSE(leaky_called_); - EXPECT_FALSE(static_called_); - non_leak_called_ = false; - leaky_called_ = false; - static_called_ = false; - } - - static void CallbackNoLeak() { - non_leak_called_ = true; - } - - static void CallbackLeak() { - leaky_called_ = true; - } - - static void CallbackStatic() { - static_called_ = true; - } - - private: - static bool non_leak_called_; - static bool leaky_called_; - static bool static_called_; -}; - -bool SingletonTest::non_leak_called_ = false; -bool SingletonTest::leaky_called_ = false; -bool SingletonTest::static_called_ = false; - -TEST_F(SingletonTest, Basic) { - int* singleton_int; - int* singleton_int_5; - CallbackFunc* leaky_singleton; - CallbackFunc* static_singleton; - - { - ShadowingAtExitManager sem; - { - singleton_int = SingletonInt(); - } - // Ensure POD type initialization. - EXPECT_EQ(*singleton_int, 0); - *singleton_int = 1; - - EXPECT_EQ(singleton_int, SingletonInt()); - EXPECT_EQ(*singleton_int, 1); - - { - singleton_int_5 = SingletonInt5(); - } - // Is default initialized to 5. - EXPECT_EQ(*singleton_int_5, 5); - - SingletonNoLeak(&CallbackNoLeak); - SingletonLeak(&CallbackLeak); - SingletonStatic(&CallbackStatic); - static_singleton = GetStaticSingleton(); - leaky_singleton = GetLeakySingleton(); - EXPECT_TRUE(leaky_singleton); - } - - // Verify that only the expected callback has been called. - VerifiesCallbacks(); - // Delete the leaky singleton. - DeleteLeakySingleton(); - - // The static singleton can't be acquired post-atexit. - EXPECT_EQ(nullptr, GetStaticSingleton()); - - { - ShadowingAtExitManager sem; - // Verifiy that the variables were reset. - { - singleton_int = SingletonInt(); - EXPECT_EQ(*singleton_int, 0); - } - { - singleton_int_5 = SingletonInt5(); - EXPECT_EQ(*singleton_int_5, 5); - } - { - // Resurrect the static singleton, and assert that it - // still points to the same (static) memory. - CallbackSingletonWithStaticTrait::Trait::ResurrectForTesting(); - EXPECT_EQ(GetStaticSingleton(), static_singleton); - } - } - // The leaky singleton shouldn't leak since SingletonLeak has not been called. - VerifiesCallbacksNotCalled(); -} - -#define EXPECT_ALIGNED(ptr, align) \ - EXPECT_EQ(0u, reinterpret_cast(ptr) & (align - 1)) - -TEST_F(SingletonTest, Alignment) { - // Create some static singletons with increasing sizes and alignment - // requirements. By ordering this way, the linker will need to do some work to - // ensure proper alignment of the static data. - AlignedTestSingleton* align4 = - AlignedTestSingleton::GetInstance(); - AlignedTestSingleton>* align32 = - AlignedTestSingleton>::GetInstance(); - AlignedTestSingleton>* align128 = - AlignedTestSingleton>::GetInstance(); - AlignedTestSingleton>* align4096 = - AlignedTestSingleton>::GetInstance(); - - EXPECT_ALIGNED(align4, 4); - EXPECT_ALIGNED(align32, 32); - EXPECT_ALIGNED(align128, 128); - EXPECT_ALIGNED(align4096, 4096); -} - -} // namespace -} // namespace base diff --git a/memory/unsafe_shared_memory_region.cc b/memory/unsafe_shared_memory_region.cc deleted file mode 100644 index 422b5a9fe..000000000 --- a/memory/unsafe_shared_memory_region.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2018 The Chromium 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/unsafe_shared_memory_region.h" - -#include - -#include "base/memory/shared_memory.h" - -namespace base { - -// static -UnsafeSharedMemoryRegion UnsafeSharedMemoryRegion::Create(size_t size) { - subtle::PlatformSharedMemoryRegion handle = - subtle::PlatformSharedMemoryRegion::CreateUnsafe(size); - - return UnsafeSharedMemoryRegion(std::move(handle)); -} - -// static -UnsafeSharedMemoryRegion UnsafeSharedMemoryRegion::Deserialize( - subtle::PlatformSharedMemoryRegion handle) { - return UnsafeSharedMemoryRegion(std::move(handle)); -} - -// static -subtle::PlatformSharedMemoryRegion -UnsafeSharedMemoryRegion::TakeHandleForSerialization( - UnsafeSharedMemoryRegion region) { - return std::move(region.handle_); -} - -UnsafeSharedMemoryRegion::UnsafeSharedMemoryRegion() = default; -UnsafeSharedMemoryRegion::UnsafeSharedMemoryRegion( - UnsafeSharedMemoryRegion&& region) = default; -UnsafeSharedMemoryRegion& UnsafeSharedMemoryRegion::operator=( - UnsafeSharedMemoryRegion&& region) = default; -UnsafeSharedMemoryRegion::~UnsafeSharedMemoryRegion() = default; - -UnsafeSharedMemoryRegion UnsafeSharedMemoryRegion::Duplicate() const { - return UnsafeSharedMemoryRegion(handle_.Duplicate()); -} - -WritableSharedMemoryMapping UnsafeSharedMemoryRegion::Map() const { - return MapAt(0, handle_.GetSize()); -} - -WritableSharedMemoryMapping UnsafeSharedMemoryRegion::MapAt(off_t offset, - size_t size) const { - if (!IsValid()) - return {}; - - void* memory = nullptr; - size_t mapped_size = 0; - if (!handle_.MapAt(offset, size, &memory, &mapped_size)) - return {}; - - return WritableSharedMemoryMapping(memory, size, mapped_size, - handle_.GetGUID()); -} - -bool UnsafeSharedMemoryRegion::IsValid() const { - return handle_.IsValid(); -} - -UnsafeSharedMemoryRegion::UnsafeSharedMemoryRegion( - subtle::PlatformSharedMemoryRegion handle) - : handle_(std::move(handle)) { - if (handle_.IsValid()) { - CHECK_EQ(handle_.GetMode(), - subtle::PlatformSharedMemoryRegion::Mode::kUnsafe); - } -} - -} // namespace base diff --git a/memory/unsafe_shared_memory_region.h b/memory/unsafe_shared_memory_region.h deleted file mode 100644 index ea637cd51..000000000 --- a/memory/unsafe_shared_memory_region.h +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018 The Chromium 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_UNSAFE_SHARED_MEMORY_REGION_H_ -#define BASE_MEMORY_UNSAFE_SHARED_MEMORY_REGION_H_ - -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/memory/platform_shared_memory_region.h" -#include "base/memory/shared_memory_mapping.h" - -namespace base { - -// Scoped move-only handle to a region of platform shared memory. The instance -// owns the platform handle it wraps. Mappings created by this region are -// writable. These mappings remain valid even after the region handle is moved -// or destroyed. -// -// NOTE: UnsafeSharedMemoryRegion cannot be converted to a read-only region. Use -// with caution as the region will be writable to any process with a handle to -// the region. -// -// Use this if and only if the following is true: -// - You do not need to share the region as read-only, and, -// - You need to have several instances of the region simultaneously, possibly -// in different processes, that can produce writable mappings. - -class BASE_EXPORT UnsafeSharedMemoryRegion { - public: - using MappingType = WritableSharedMemoryMapping; - // Creates a new UnsafeSharedMemoryRegion instance of a given size that can be - // used for mapping writable shared memory into the virtual address space. - static UnsafeSharedMemoryRegion Create(size_t size); - - // Returns an UnsafeSharedMemoryRegion built from a platform-specific handle - // that was taken from another UnsafeSharedMemoryRegion instance. Returns an - // invalid region iff the |handle| is invalid. CHECK-fails if the |handle| - // isn't unsafe. - // This should be used only by the code passing a handle across - // process boundaries. - static UnsafeSharedMemoryRegion Deserialize( - subtle::PlatformSharedMemoryRegion handle); - - // Extracts a platform handle from the region. Ownership is transferred to the - // returned region object. - // This should be used only for sending the handle from the current - // process to another. - static subtle::PlatformSharedMemoryRegion TakeHandleForSerialization( - UnsafeSharedMemoryRegion region); - - // Default constructor initializes an invalid instance. - UnsafeSharedMemoryRegion(); - - // Move operations are allowed. - UnsafeSharedMemoryRegion(UnsafeSharedMemoryRegion&&); - UnsafeSharedMemoryRegion& operator=(UnsafeSharedMemoryRegion&&); - - // Destructor closes shared memory region if valid. - // All created mappings will remain valid. - ~UnsafeSharedMemoryRegion(); - - // Duplicates the underlying platform handle and creates a new - // UnsafeSharedMemoryRegion instance that owns the newly created handle. - // Returns a valid UnsafeSharedMemoryRegion on success, invalid otherwise. - // The current region instance remains valid in any case. - UnsafeSharedMemoryRegion Duplicate() const; - - // Maps the shared memory region into the caller's address space with write - // access. The mapped address is guaranteed to have an alignment of - // at least |subtle::PlatformSharedMemoryRegion::kMapMinimumAlignment|. - // Returns a valid WritableSharedMemoryMapping instance on success, invalid - // otherwise. - WritableSharedMemoryMapping Map() const; - - // Same as above, but maps only |size| bytes of the shared memory region - // starting with the given |offset|. |offset| must be aligned to value of - // |SysInfo::VMAllocationGranularity()|. Returns an invalid mapping if - // requested bytes are out of the region limits. - WritableSharedMemoryMapping MapAt(off_t offset, size_t size) const; - - // Whether the underlying platform handle is valid. - bool IsValid() const; - - // Returns the maximum mapping size that can be created from this region. - size_t GetSize() const { - DCHECK(IsValid()); - return handle_.GetSize(); - } - - // Returns 128-bit GUID of the region. - const UnguessableToken& GetGUID() const { - DCHECK(IsValid()); - return handle_.GetGUID(); - } - - private: - FRIEND_TEST_ALL_PREFIXES(DiscardableSharedMemoryTest, - LockShouldFailIfPlatformLockPagesFails); - friend class DiscardableSharedMemory; - - explicit UnsafeSharedMemoryRegion(subtle::PlatformSharedMemoryRegion handle); - - // Returns a platform shared memory handle. |this| remains the owner of the - // handle. - subtle::PlatformSharedMemoryRegion::PlatformHandle GetPlatformHandle() const { - DCHECK(IsValid()); - return handle_.GetPlatformHandle(); - } - - subtle::PlatformSharedMemoryRegion handle_; - - DISALLOW_COPY_AND_ASSIGN(UnsafeSharedMemoryRegion); -}; - -} // namespace base - -#endif // BASE_MEMORY_UNSAFE_SHARED_MEMORY_REGION_H_ diff --git a/memory/weak_ptr.cc b/memory/weak_ptr.cc deleted file mode 100644 index d2a7d89e5..000000000 --- a/memory/weak_ptr.cc +++ /dev/null @@ -1,86 +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/memory/weak_ptr.h" - -namespace base { -namespace internal { - -WeakReference::Flag::Flag() : is_valid_(true) { - // Flags only become bound when checked for validity, or invalidated, - // so that we can check that later validity/invalidation operations on - // the same Flag take place on the same sequenced thread. - sequence_checker_.DetachFromSequence(); -} - -void WeakReference::Flag::Invalidate() { - // The flag being invalidated with a single ref implies that there are no - // weak pointers in existence. Allow deletion on other thread in this case. - DCHECK(sequence_checker_.CalledOnValidSequence() || HasOneRef()) - << "WeakPtrs must be invalidated on the same sequenced thread."; - is_valid_ = false; -} - -bool WeakReference::Flag::IsValid() const { - DCHECK(sequence_checker_.CalledOnValidSequence()) - << "WeakPtrs must be checked on the same sequenced thread."; - return is_valid_; -} - -WeakReference::Flag::~Flag() = default; - -WeakReference::WeakReference() = default; - -WeakReference::WeakReference(const scoped_refptr& flag) : flag_(flag) {} - -WeakReference::~WeakReference() = default; - -WeakReference::WeakReference(WeakReference&& other) = default; - -WeakReference::WeakReference(const WeakReference& other) = default; - -bool WeakReference::is_valid() const { - return flag_ && flag_->IsValid(); -} - -WeakReferenceOwner::WeakReferenceOwner() = default; - -WeakReferenceOwner::~WeakReferenceOwner() { - Invalidate(); -} - -WeakReference WeakReferenceOwner::GetRef() const { - // If we hold the last reference to the Flag then create a new one. - if (!HasRefs()) - flag_ = new WeakReference::Flag(); - - return WeakReference(flag_); -} - -void WeakReferenceOwner::Invalidate() { - if (flag_) { - flag_->Invalidate(); - flag_ = nullptr; - } -} - -WeakPtrBase::WeakPtrBase() : ptr_(0) {} - -WeakPtrBase::~WeakPtrBase() = default; - -WeakPtrBase::WeakPtrBase(const WeakReference& ref, uintptr_t ptr) - : ref_(ref), ptr_(ptr) { - DCHECK(ptr_); -} - -WeakPtrFactoryBase::WeakPtrFactoryBase(uintptr_t ptr) : ptr_(ptr) { - DCHECK(ptr_); -} - -WeakPtrFactoryBase::~WeakPtrFactoryBase() { - ptr_ = 0; -} - -} // namespace internal -} // namespace base diff --git a/memory/weak_ptr.h b/memory/weak_ptr.h deleted file mode 100644 index 34e7d2e35..000000000 --- a/memory/weak_ptr.h +++ /dev/null @@ -1,377 +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. - -// Weak pointers are pointers to an object that do not affect its lifetime, -// and which may be invalidated (i.e. reset to nullptr) by the object, or its -// owner, at any time, most commonly when the object is about to be deleted. - -// Weak pointers are useful when an object needs to be accessed safely by one -// or more objects other than its owner, and those callers can cope with the -// object vanishing and e.g. tasks posted to it being silently dropped. -// Reference-counting such an object would complicate the ownership graph and -// make it harder to reason about the object's lifetime. - -// EXAMPLE: -// -// class Controller { -// public: -// Controller() : weak_factory_(this) {} -// void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); } -// void WorkComplete(const Result& result) { ... } -// private: -// // Member variables should appear before the WeakPtrFactory, to ensure -// // that any WeakPtrs to Controller are invalidated before its members -// // variable's destructors are executed, rendering them invalid. -// WeakPtrFactory weak_factory_; -// }; -// -// class Worker { -// public: -// static void StartNew(const WeakPtr& controller) { -// Worker* worker = new Worker(controller); -// // Kick off asynchronous processing... -// } -// private: -// Worker(const WeakPtr& controller) -// : controller_(controller) {} -// void DidCompleteAsynchronousProcessing(const Result& result) { -// if (controller_) -// controller_->WorkComplete(result); -// } -// WeakPtr controller_; -// }; -// -// With this implementation a caller may use SpawnWorker() to dispatch multiple -// Workers and subsequently delete the Controller, without waiting for all -// Workers to have completed. - -// ------------------------- IMPORTANT: Thread-safety ------------------------- - -// Weak pointers may be passed safely between threads, but must always be -// dereferenced and invalidated on the same SequencedTaskRunner otherwise -// checking the pointer would be racey. -// -// To ensure correct use, the first time a WeakPtr issued by a WeakPtrFactory -// is dereferenced, the factory and its WeakPtrs become bound to the calling -// thread or current SequencedWorkerPool token, and cannot be dereferenced or -// invalidated on any other task runner. Bound WeakPtrs can still be handed -// off to other task runners, e.g. to use to post tasks back to object on the -// bound sequence. -// -// If all WeakPtr objects are destroyed or invalidated then the factory is -// unbound from the SequencedTaskRunner/Thread. The WeakPtrFactory may then be -// destroyed, or new WeakPtr objects may be used, from a different sequence. -// -// Thus, at least one WeakPtr object must exist and have been dereferenced on -// the correct thread to enforce that other WeakPtr objects will enforce they -// are used on the desired thread. - -#ifndef BASE_MEMORY_WEAK_PTR_H_ -#define BASE_MEMORY_WEAK_PTR_H_ - -#include -#include - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/sequence_checker.h" - -namespace base { - -template class SupportsWeakPtr; -template class WeakPtr; - -namespace internal { -// These classes are part of the WeakPtr implementation. -// DO NOT USE THESE CLASSES DIRECTLY YOURSELF. - -class BASE_EXPORT WeakReference { - public: - // Although Flag is bound to a specific SequencedTaskRunner, it may be - // deleted from another via base::WeakPtr::~WeakPtr(). - class BASE_EXPORT Flag : public RefCountedThreadSafe { - public: - Flag(); - - void Invalidate(); - bool IsValid() const; - - private: - friend class base::RefCountedThreadSafe; - - ~Flag(); - - SequenceChecker sequence_checker_; - bool is_valid_; - }; - - WeakReference(); - explicit WeakReference(const scoped_refptr& flag); - ~WeakReference(); - - WeakReference(WeakReference&& other); - WeakReference(const WeakReference& other); - WeakReference& operator=(WeakReference&& other) = default; - WeakReference& operator=(const WeakReference& other) = default; - - bool is_valid() const; - - private: - scoped_refptr flag_; -}; - -class BASE_EXPORT WeakReferenceOwner { - public: - WeakReferenceOwner(); - ~WeakReferenceOwner(); - - WeakReference GetRef() const; - - bool HasRefs() const { return flag_ && !flag_->HasOneRef(); } - - void Invalidate(); - - private: - mutable scoped_refptr flag_; -}; - -// This class simplifies the implementation of WeakPtr's type conversion -// constructor by avoiding the need for a public accessor for ref_. A -// WeakPtr cannot access the private members of WeakPtr, so this -// base class gives us a way to access ref_ in a protected fashion. -class BASE_EXPORT WeakPtrBase { - public: - WeakPtrBase(); - ~WeakPtrBase(); - - WeakPtrBase(const WeakPtrBase& other) = default; - WeakPtrBase(WeakPtrBase&& other) = default; - WeakPtrBase& operator=(const WeakPtrBase& other) = default; - WeakPtrBase& operator=(WeakPtrBase&& other) = default; - - void reset() { - ref_ = internal::WeakReference(); - ptr_ = 0; - } - - protected: - WeakPtrBase(const WeakReference& ref, uintptr_t ptr); - - WeakReference ref_; - - // This pointer is only valid when ref_.is_valid() is true. Otherwise, its - // value is undefined (as opposed to nullptr). - uintptr_t ptr_; -}; - -// This class provides a common implementation of common functions that would -// otherwise get instantiated separately for each distinct instantiation of -// SupportsWeakPtr<>. -class SupportsWeakPtrBase { - public: - // A safe static downcast of a WeakPtr to WeakPtr. This - // conversion will only compile if there is exists a Base which inherits - // from SupportsWeakPtr. See base::AsWeakPtr() below for a helper - // function that makes calling this easier. - // - // Precondition: t != nullptr - template - static WeakPtr StaticAsWeakPtr(Derived* t) { - static_assert( - std::is_base_of::value, - "AsWeakPtr argument must inherit from SupportsWeakPtr"); - return AsWeakPtrImpl(t); - } - - private: - // This template function uses type inference to find a Base of Derived - // which is an instance of SupportsWeakPtr. We can then safely - // static_cast the Base* to a Derived*. - template - static WeakPtr AsWeakPtrImpl(SupportsWeakPtr* t) { - WeakPtr ptr = t->AsWeakPtr(); - return WeakPtr( - ptr.ref_, static_cast(reinterpret_cast(ptr.ptr_))); - } -}; - -} // namespace internal - -template class WeakPtrFactory; - -// The WeakPtr class holds a weak reference to |T*|. -// -// This class is designed to be used like a normal pointer. You should always -// null-test an object of this class before using it or invoking a method that -// may result in the underlying object being destroyed. -// -// EXAMPLE: -// -// class Foo { ... }; -// WeakPtr foo; -// if (foo) -// foo->method(); -// -template -class WeakPtr : public internal::WeakPtrBase { - public: - WeakPtr() = default; - - WeakPtr(std::nullptr_t) {} - - // Allow conversion from U to T provided U "is a" T. Note that this - // is separate from the (implicit) copy and move constructors. - template - WeakPtr(const WeakPtr& other) : WeakPtrBase(other) { - // Need to cast from U* to T* to do pointer adjustment in case of multiple - // inheritance. This also enforces the "U is a T" rule. - T* t = reinterpret_cast(other.ptr_); - ptr_ = reinterpret_cast(t); - } - template - WeakPtr(WeakPtr&& other) : WeakPtrBase(std::move(other)) { - // Need to cast from U* to T* to do pointer adjustment in case of multiple - // inheritance. This also enforces the "U is a T" rule. - T* t = reinterpret_cast(other.ptr_); - ptr_ = reinterpret_cast(t); - } - - T* get() const { - return ref_.is_valid() ? reinterpret_cast(ptr_) : nullptr; - } - - T& operator*() const { - DCHECK(get() != nullptr); - return *get(); - } - T* operator->() const { - DCHECK(get() != nullptr); - return get(); - } - - // Allow conditionals to test validity, e.g. if (weak_ptr) {...}; - explicit operator bool() const { return get() != nullptr; } - - private: - friend class internal::SupportsWeakPtrBase; - template friend class WeakPtr; - friend class SupportsWeakPtr; - friend class WeakPtrFactory; - - WeakPtr(const internal::WeakReference& ref, T* ptr) - : WeakPtrBase(ref, reinterpret_cast(ptr)) {} -}; - -// Allow callers to compare WeakPtrs against nullptr to test validity. -template -bool operator!=(const WeakPtr& weak_ptr, std::nullptr_t) { - return !(weak_ptr == nullptr); -} -template -bool operator!=(std::nullptr_t, const WeakPtr& weak_ptr) { - return weak_ptr != nullptr; -} -template -bool operator==(const WeakPtr& weak_ptr, std::nullptr_t) { - return weak_ptr.get() == nullptr; -} -template -bool operator==(std::nullptr_t, const WeakPtr& weak_ptr) { - return weak_ptr == nullptr; -} - -namespace internal { -class BASE_EXPORT WeakPtrFactoryBase { - protected: - WeakPtrFactoryBase(uintptr_t ptr); - ~WeakPtrFactoryBase(); - internal::WeakReferenceOwner weak_reference_owner_; - uintptr_t ptr_; -}; -} // namespace internal - -// A class may be composed of a WeakPtrFactory and thereby -// control how it exposes weak pointers to itself. This is helpful if you only -// need weak pointers within the implementation of a class. This class is also -// useful when working with primitive types. For example, you could have a -// WeakPtrFactory that is used to pass around a weak reference to a bool. -template -class WeakPtrFactory : public internal::WeakPtrFactoryBase { - public: - explicit WeakPtrFactory(T* ptr) - : WeakPtrFactoryBase(reinterpret_cast(ptr)) {} - - ~WeakPtrFactory() = default; - - WeakPtr GetWeakPtr() { - return WeakPtr(weak_reference_owner_.GetRef(), - reinterpret_cast(ptr_)); - } - - // Call this method to invalidate all existing weak pointers. - void InvalidateWeakPtrs() { - DCHECK(ptr_); - weak_reference_owner_.Invalidate(); - } - - // Call this method to determine if any weak pointers exist. - bool HasWeakPtrs() const { - DCHECK(ptr_); - return weak_reference_owner_.HasRefs(); - } - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(WeakPtrFactory); -}; - -// A class may extend from SupportsWeakPtr to let others take weak pointers to -// it. This avoids the class itself implementing boilerplate to dispense weak -// pointers. However, since SupportsWeakPtr's destructor won't invalidate -// weak pointers to the class until after the derived class' members have been -// destroyed, its use can lead to subtle use-after-destroy issues. -template -class SupportsWeakPtr : public internal::SupportsWeakPtrBase { - public: - SupportsWeakPtr() = default; - - WeakPtr AsWeakPtr() { - return WeakPtr(weak_reference_owner_.GetRef(), static_cast(this)); - } - - protected: - ~SupportsWeakPtr() = default; - - private: - internal::WeakReferenceOwner weak_reference_owner_; - DISALLOW_COPY_AND_ASSIGN(SupportsWeakPtr); -}; - -// Helper function that uses type deduction to safely return a WeakPtr -// when Derived doesn't directly extend SupportsWeakPtr, instead it -// extends a Base that extends SupportsWeakPtr. -// -// EXAMPLE: -// class Base : public base::SupportsWeakPtr {}; -// class Derived : public Base {}; -// -// Derived derived; -// base::WeakPtr ptr = base::AsWeakPtr(&derived); -// -// Note that the following doesn't work (invalid type conversion) since -// Derived::AsWeakPtr() is WeakPtr SupportsWeakPtr::AsWeakPtr(), -// and there's no way to safely cast WeakPtr to WeakPtr at -// the caller. -// -// base::WeakPtr ptr = derived.AsWeakPtr(); // Fails. - -template -WeakPtr AsWeakPtr(Derived* t) { - return internal::SupportsWeakPtrBase::StaticAsWeakPtr(t); -} - -} // namespace base - -#endif // BASE_MEMORY_WEAK_PTR_H_ diff --git a/memory/weak_ptr_unittest.cc b/memory/weak_ptr_unittest.cc deleted file mode 100644 index f8dfb7c0f..000000000 --- a/memory/weak_ptr_unittest.cc +++ /dev/null @@ -1,716 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/weak_ptr.h" - -#include -#include - -#include "base/bind.h" -#include "base/debug/leak_annotations.h" -#include "base/location.h" -#include "base/single_thread_task_runner.h" -#include "base/synchronization/waitable_event.h" -#include "base/test/gtest_util.h" -#include "base/threading/thread.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -WeakPtr PassThru(WeakPtr ptr) { - return ptr; -} - -template -class OffThreadObjectCreator { - public: - static T* NewObject() { - T* result; - { - Thread creator_thread("creator_thread"); - creator_thread.Start(); - creator_thread.task_runner()->PostTask( - FROM_HERE, - base::BindOnce(OffThreadObjectCreator::CreateObject, &result)); - } - DCHECK(result); // We synchronized on thread destruction above. - return result; - } - private: - static void CreateObject(T** result) { - *result = new T; - } -}; - -struct Base { - std::string member; -}; -struct Derived : public Base {}; - -struct TargetBase {}; -struct Target : public TargetBase, public SupportsWeakPtr { - virtual ~Target() = default; -}; - -struct DerivedTarget : public Target {}; - -// A class inheriting from Target and defining a nested type called 'Base'. -// To guard against strange compilation errors. -struct DerivedTargetWithNestedBase : public Target { - using Base = void; -}; - -// A struct with a virtual destructor. -struct VirtualDestructor { - virtual ~VirtualDestructor() = default; -}; - -// A class inheriting from Target where Target is not the first base, and where -// the first base has a virtual method table. This creates a structure where the -// Target base is not positioned at the beginning of -// DerivedTargetMultipleInheritance. -struct DerivedTargetMultipleInheritance : public VirtualDestructor, - public Target {}; - -struct Arrow { - WeakPtr target; -}; -struct TargetWithFactory : public Target { - TargetWithFactory() : factory(this) {} - WeakPtrFactory factory; -}; - -// Helper class to create and destroy weak pointer copies -// and delete objects on a background thread. -class BackgroundThread : public Thread { - public: - BackgroundThread() : Thread("owner_thread") {} - - ~BackgroundThread() override { Stop(); } - - void CreateArrowFromTarget(Arrow** arrow, Target* target) { - WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - task_runner()->PostTask( - FROM_HERE, base::BindOnce(&BackgroundThread::DoCreateArrowFromTarget, - arrow, target, &completion)); - completion.Wait(); - } - - void CreateArrowFromArrow(Arrow** arrow, const Arrow* other) { - WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - task_runner()->PostTask( - FROM_HERE, base::BindOnce(&BackgroundThread::DoCreateArrowFromArrow, - arrow, other, &completion)); - completion.Wait(); - } - - void DeleteTarget(Target* object) { - WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&BackgroundThread::DoDeleteTarget, object, &completion)); - completion.Wait(); - } - - void CopyAndAssignArrow(Arrow* object) { - WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - task_runner()->PostTask( - FROM_HERE, base::BindOnce(&BackgroundThread::DoCopyAndAssignArrow, - object, &completion)); - completion.Wait(); - } - - void CopyAndAssignArrowBase(Arrow* object) { - WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - task_runner()->PostTask( - FROM_HERE, base::BindOnce(&BackgroundThread::DoCopyAndAssignArrowBase, - object, &completion)); - completion.Wait(); - } - - void DeleteArrow(Arrow* object) { - WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&BackgroundThread::DoDeleteArrow, object, &completion)); - completion.Wait(); - } - - Target* DeRef(const Arrow* arrow) { - WaitableEvent completion(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - Target* result = nullptr; - task_runner()->PostTask( - FROM_HERE, base::BindOnce(&BackgroundThread::DoDeRef, arrow, &result, - &completion)); - completion.Wait(); - return result; - } - - protected: - static void DoCreateArrowFromArrow(Arrow** arrow, - const Arrow* other, - WaitableEvent* completion) { - *arrow = new Arrow; - **arrow = *other; - completion->Signal(); - } - - static void DoCreateArrowFromTarget(Arrow** arrow, - Target* target, - WaitableEvent* completion) { - *arrow = new Arrow; - (*arrow)->target = target->AsWeakPtr(); - completion->Signal(); - } - - static void DoDeRef(const Arrow* arrow, - Target** result, - WaitableEvent* completion) { - *result = arrow->target.get(); - completion->Signal(); - } - - static void DoDeleteTarget(Target* object, WaitableEvent* completion) { - delete object; - completion->Signal(); - } - - static void DoCopyAndAssignArrow(Arrow* object, WaitableEvent* completion) { - // Copy constructor. - Arrow a = *object; - // Assignment operator. - *object = a; - completion->Signal(); - } - - static void DoCopyAndAssignArrowBase( - Arrow* object, - WaitableEvent* completion) { - // Copy constructor. - WeakPtr b = object->target; - // Assignment operator. - WeakPtr c; - c = object->target; - completion->Signal(); - } - - static void DoDeleteArrow(Arrow* object, WaitableEvent* completion) { - delete object; - completion->Signal(); - } -}; - -} // namespace - -TEST(WeakPtrFactoryTest, Basic) { - int data; - WeakPtrFactory factory(&data); - WeakPtr ptr = factory.GetWeakPtr(); - EXPECT_EQ(&data, ptr.get()); -} - -TEST(WeakPtrFactoryTest, Comparison) { - int data; - WeakPtrFactory factory(&data); - WeakPtr ptr = factory.GetWeakPtr(); - WeakPtr ptr2 = ptr; - EXPECT_EQ(ptr.get(), ptr2.get()); -} - -TEST(WeakPtrFactoryTest, Move) { - int data; - WeakPtrFactory factory(&data); - WeakPtr ptr = factory.GetWeakPtr(); - WeakPtr ptr2 = factory.GetWeakPtr(); - WeakPtr ptr3 = std::move(ptr2); - EXPECT_NE(ptr.get(), ptr2.get()); - EXPECT_EQ(ptr.get(), ptr3.get()); -} - -TEST(WeakPtrFactoryTest, OutOfScope) { - WeakPtr ptr; - EXPECT_EQ(nullptr, ptr.get()); - { - int data; - WeakPtrFactory factory(&data); - ptr = factory.GetWeakPtr(); - } - EXPECT_EQ(nullptr, ptr.get()); -} - -TEST(WeakPtrFactoryTest, Multiple) { - WeakPtr a, b; - { - int data; - WeakPtrFactory factory(&data); - a = factory.GetWeakPtr(); - b = factory.GetWeakPtr(); - EXPECT_EQ(&data, a.get()); - EXPECT_EQ(&data, b.get()); - } - EXPECT_EQ(nullptr, a.get()); - EXPECT_EQ(nullptr, b.get()); -} - -TEST(WeakPtrFactoryTest, MultipleStaged) { - WeakPtr a; - { - int data; - WeakPtrFactory factory(&data); - a = factory.GetWeakPtr(); - { - WeakPtr b = factory.GetWeakPtr(); - } - EXPECT_NE(nullptr, a.get()); - } - EXPECT_EQ(nullptr, a.get()); -} - -TEST(WeakPtrFactoryTest, Dereference) { - Base data; - data.member = "123456"; - WeakPtrFactory factory(&data); - WeakPtr ptr = factory.GetWeakPtr(); - EXPECT_EQ(&data, ptr.get()); - EXPECT_EQ(data.member, (*ptr).member); - EXPECT_EQ(data.member, ptr->member); -} - -TEST(WeakPtrFactoryTest, UpCast) { - Derived data; - WeakPtrFactory factory(&data); - WeakPtr ptr = factory.GetWeakPtr(); - ptr = factory.GetWeakPtr(); - EXPECT_EQ(ptr.get(), &data); -} - -TEST(WeakPtrTest, ConstructFromNullptr) { - WeakPtr ptr = PassThru(nullptr); - EXPECT_EQ(nullptr, ptr.get()); -} - -TEST(WeakPtrTest, SupportsWeakPtr) { - Target target; - WeakPtr ptr = target.AsWeakPtr(); - EXPECT_EQ(&target, ptr.get()); -} - -TEST(WeakPtrTest, DerivedTarget) { - DerivedTarget target; - WeakPtr ptr = AsWeakPtr(&target); - EXPECT_EQ(&target, ptr.get()); -} - -TEST(WeakPtrTest, DerivedTargetWithNestedBase) { - DerivedTargetWithNestedBase target; - WeakPtr ptr = AsWeakPtr(&target); - EXPECT_EQ(&target, ptr.get()); -} - -TEST(WeakPtrTest, DerivedTargetMultipleInheritance) { - DerivedTargetMultipleInheritance d; - Target& b = d; - EXPECT_NE(static_cast(&d), static_cast(&b)); - const WeakPtr pb = AsWeakPtr(&b); - EXPECT_EQ(pb.get(), &b); - const WeakPtr pd = AsWeakPtr(&d); - EXPECT_EQ(pd.get(), &d); -} - -TEST(WeakPtrFactoryTest, BooleanTesting) { - int data; - WeakPtrFactory factory(&data); - - WeakPtr ptr_to_an_instance = factory.GetWeakPtr(); - EXPECT_TRUE(ptr_to_an_instance); - EXPECT_FALSE(!ptr_to_an_instance); - - if (ptr_to_an_instance) { - } else { - ADD_FAILURE() << "Pointer to an instance should result in true."; - } - - if (!ptr_to_an_instance) { // check for operator!(). - ADD_FAILURE() << "Pointer to an instance should result in !x being false."; - } - - WeakPtr null_ptr; - EXPECT_FALSE(null_ptr); - EXPECT_TRUE(!null_ptr); - - if (null_ptr) { - ADD_FAILURE() << "Null pointer should result in false."; - } - - if (!null_ptr) { // check for operator!(). - } else { - ADD_FAILURE() << "Null pointer should result in !x being true."; - } -} - -TEST(WeakPtrFactoryTest, ComparisonToNull) { - int data; - WeakPtrFactory factory(&data); - - WeakPtr ptr_to_an_instance = factory.GetWeakPtr(); - EXPECT_NE(nullptr, ptr_to_an_instance); - EXPECT_NE(ptr_to_an_instance, nullptr); - - WeakPtr null_ptr; - EXPECT_EQ(null_ptr, nullptr); - EXPECT_EQ(nullptr, null_ptr); -} - -TEST(WeakPtrTest, InvalidateWeakPtrs) { - int data; - WeakPtrFactory factory(&data); - WeakPtr ptr = factory.GetWeakPtr(); - EXPECT_EQ(&data, ptr.get()); - EXPECT_TRUE(factory.HasWeakPtrs()); - factory.InvalidateWeakPtrs(); - EXPECT_EQ(nullptr, ptr.get()); - EXPECT_FALSE(factory.HasWeakPtrs()); - - // Test that the factory can create new weak pointers after a - // InvalidateWeakPtrs call, and they remain valid until the next - // InvalidateWeakPtrs call. - WeakPtr ptr2 = factory.GetWeakPtr(); - EXPECT_EQ(&data, ptr2.get()); - EXPECT_TRUE(factory.HasWeakPtrs()); - factory.InvalidateWeakPtrs(); - EXPECT_EQ(nullptr, ptr2.get()); - EXPECT_FALSE(factory.HasWeakPtrs()); -} - -TEST(WeakPtrTest, HasWeakPtrs) { - int data; - WeakPtrFactory factory(&data); - { - WeakPtr ptr = factory.GetWeakPtr(); - EXPECT_TRUE(factory.HasWeakPtrs()); - } - EXPECT_FALSE(factory.HasWeakPtrs()); -} - -TEST(WeakPtrTest, ObjectAndWeakPtrOnDifferentThreads) { - // Test that it is OK to create an object that supports WeakPtr on one thread, - // but use it on another. This tests that we do not trip runtime checks that - // ensure that a WeakPtr is not used by multiple threads. - std::unique_ptr target(OffThreadObjectCreator::NewObject()); - WeakPtr weak_ptr = target->AsWeakPtr(); - EXPECT_EQ(target.get(), weak_ptr.get()); -} - -TEST(WeakPtrTest, WeakPtrInitiateAndUseOnDifferentThreads) { - // Test that it is OK to create an object that has a WeakPtr member on one - // thread, but use it on another. This tests that we do not trip runtime - // checks that ensure that a WeakPtr is not used by multiple threads. - std::unique_ptr arrow(OffThreadObjectCreator::NewObject()); - Target target; - arrow->target = target.AsWeakPtr(); - EXPECT_EQ(&target, arrow->target.get()); -} - -TEST(WeakPtrTest, MoveOwnershipImplicitly) { - // Move object ownership to another thread by releasing all weak pointers - // on the original thread first, and then establish WeakPtr on a different - // thread. - BackgroundThread background; - background.Start(); - - Target* target = new Target(); - { - WeakPtr weak_ptr = target->AsWeakPtr(); - // Main thread deletes the WeakPtr, then the thread ownership of the - // object can be implicitly moved. - } - Arrow* arrow; - - // Background thread creates WeakPtr(and implicitly owns the object). - background.CreateArrowFromTarget(&arrow, target); - EXPECT_EQ(background.DeRef(arrow), target); - - { - // Main thread creates another WeakPtr, but this does not trigger implicitly - // thread ownership move. - Arrow arrow; - arrow.target = target->AsWeakPtr(); - - // The new WeakPtr is owned by background thread. - EXPECT_EQ(target, background.DeRef(&arrow)); - } - - // Target can only be deleted on background thread. - background.DeleteTarget(target); - background.DeleteArrow(arrow); -} - -TEST(WeakPtrTest, MoveOwnershipOfUnreferencedObject) { - BackgroundThread background; - background.Start(); - - Arrow* arrow; - { - Target target; - // Background thread creates WeakPtr. - background.CreateArrowFromTarget(&arrow, &target); - - // Bind to background thread. - EXPECT_EQ(&target, background.DeRef(arrow)); - - // Release the only WeakPtr. - arrow->target.reset(); - - // Now we should be able to create a new reference from this thread. - arrow->target = target.AsWeakPtr(); - - // Re-bind to main thread. - EXPECT_EQ(&target, arrow->target.get()); - - // And the main thread can now delete the target. - } - - delete arrow; -} - -TEST(WeakPtrTest, MoveOwnershipAfterInvalidate) { - BackgroundThread background; - background.Start(); - - Arrow arrow; - std::unique_ptr target(new TargetWithFactory); - - // Bind to main thread. - arrow.target = target->factory.GetWeakPtr(); - EXPECT_EQ(target.get(), arrow.target.get()); - - target->factory.InvalidateWeakPtrs(); - EXPECT_EQ(nullptr, arrow.target.get()); - - arrow.target = target->factory.GetWeakPtr(); - // Re-bind to background thread. - EXPECT_EQ(target.get(), background.DeRef(&arrow)); - - // And the background thread can now delete the target. - background.DeleteTarget(target.release()); -} - -TEST(WeakPtrTest, MainThreadRefOutlivesBackgroundThreadRef) { - // Originating thread has a WeakPtr that outlives others. - // - Main thread creates a WeakPtr - // - Background thread creates a WeakPtr copy from the one in main thread - // - Destruct the WeakPtr on background thread - // - Destruct the WeakPtr on main thread - BackgroundThread background; - background.Start(); - - Target target; - Arrow arrow; - arrow.target = target.AsWeakPtr(); - - Arrow* arrow_copy; - background.CreateArrowFromArrow(&arrow_copy, &arrow); - EXPECT_EQ(arrow_copy->target.get(), &target); - background.DeleteArrow(arrow_copy); -} - -TEST(WeakPtrTest, BackgroundThreadRefOutlivesMainThreadRef) { - // Originating thread drops all references before another thread. - // - Main thread creates a WeakPtr and passes copy to background thread - // - Destruct the pointer on main thread - // - Destruct the pointer on background thread - BackgroundThread background; - background.Start(); - - Target target; - Arrow* arrow_copy; - { - Arrow arrow; - arrow.target = target.AsWeakPtr(); - background.CreateArrowFromArrow(&arrow_copy, &arrow); - } - EXPECT_EQ(arrow_copy->target.get(), &target); - background.DeleteArrow(arrow_copy); -} - -TEST(WeakPtrTest, OwnerThreadDeletesObject) { - // Originating thread invalidates WeakPtrs while its held by other thread. - // - Main thread creates WeakPtr and passes Copy to background thread - // - Object gets destroyed on main thread - // (invalidates WeakPtr on background thread) - // - WeakPtr gets destroyed on Thread B - BackgroundThread background; - background.Start(); - Arrow* arrow_copy; - { - Target target; - Arrow arrow; - arrow.target = target.AsWeakPtr(); - background.CreateArrowFromArrow(&arrow_copy, &arrow); - } - EXPECT_EQ(nullptr, arrow_copy->target.get()); - background.DeleteArrow(arrow_copy); -} - -TEST(WeakPtrTest, NonOwnerThreadCanCopyAndAssignWeakPtr) { - // Main thread creates a Target object. - Target target; - // Main thread creates an arrow referencing the Target. - Arrow *arrow = new Arrow(); - arrow->target = target.AsWeakPtr(); - - // Background can copy and assign arrow (as well as the WeakPtr inside). - BackgroundThread background; - background.Start(); - background.CopyAndAssignArrow(arrow); - background.DeleteArrow(arrow); -} - -TEST(WeakPtrTest, NonOwnerThreadCanCopyAndAssignWeakPtrBase) { - // Main thread creates a Target object. - Target target; - // Main thread creates an arrow referencing the Target. - Arrow *arrow = new Arrow(); - arrow->target = target.AsWeakPtr(); - - // Background can copy and assign arrow's WeakPtr to a base class WeakPtr. - BackgroundThread background; - background.Start(); - background.CopyAndAssignArrowBase(arrow); - background.DeleteArrow(arrow); -} - -TEST(WeakPtrTest, NonOwnerThreadCanDeleteWeakPtr) { - // Main thread creates a Target object. - Target target; - // Main thread creates an arrow referencing the Target. - Arrow* arrow = new Arrow(); - arrow->target = target.AsWeakPtr(); - - // Background can delete arrow (as well as the WeakPtr inside). - BackgroundThread background; - background.Start(); - background.DeleteArrow(arrow); -} - -TEST(WeakPtrDeathTest, WeakPtrCopyDoesNotChangeThreadBinding) { - // The default style "fast" does not support multi-threaded tests - // (introduces deadlock on Linux). - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - - BackgroundThread background; - background.Start(); - - // Main thread creates a Target object. - Target target; - // Main thread creates an arrow referencing the Target. - Arrow arrow; - arrow.target = target.AsWeakPtr(); - - // Background copies the WeakPtr. - Arrow* arrow_copy; - background.CreateArrowFromArrow(&arrow_copy, &arrow); - - // The copy is still bound to main thread so I can deref. - EXPECT_EQ(arrow.target.get(), arrow_copy->target.get()); - - // Although background thread created the copy, it can not deref the copied - // WeakPtr. - ASSERT_DCHECK_DEATH(background.DeRef(arrow_copy)); - - background.DeleteArrow(arrow_copy); -} - -TEST(WeakPtrDeathTest, NonOwnerThreadDereferencesWeakPtrAfterReference) { - // The default style "fast" does not support multi-threaded tests - // (introduces deadlock on Linux). - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - - // Main thread creates a Target object. - Target target; - - // Main thread creates an arrow referencing the Target (so target's - // thread ownership can not be implicitly moved). - Arrow arrow; - arrow.target = target.AsWeakPtr(); - arrow.target.get(); - - // Background thread tries to deref target, which violates thread ownership. - BackgroundThread background; - background.Start(); - ASSERT_DCHECK_DEATH(background.DeRef(&arrow)); -} - -TEST(WeakPtrDeathTest, NonOwnerThreadDeletesWeakPtrAfterReference) { - // The default style "fast" does not support multi-threaded tests - // (introduces deadlock on Linux). - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - - std::unique_ptr target(new Target()); - - // Main thread creates an arrow referencing the Target. - Arrow arrow; - arrow.target = target->AsWeakPtr(); - - // Background thread tries to deref target, binding it to the thread. - BackgroundThread background; - background.Start(); - background.DeRef(&arrow); - - // Main thread deletes Target, violating thread binding. - ASSERT_DCHECK_DEATH(target.reset()); - - // |target.reset()| died so |target| still holds the object, so we - // must pass it to the background thread to teardown. - background.DeleteTarget(target.release()); -} - -TEST(WeakPtrDeathTest, NonOwnerThreadDeletesObjectAfterReference) { - // The default style "fast" does not support multi-threaded tests - // (introduces deadlock on Linux). - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - - std::unique_ptr target(new Target()); - - // Main thread creates an arrow referencing the Target, and references it, so - // that it becomes bound to the thread. - Arrow arrow; - arrow.target = target->AsWeakPtr(); - arrow.target.get(); - - // Background thread tries to delete target, volating thread binding. - BackgroundThread background; - background.Start(); - ASSERT_DCHECK_DEATH(background.DeleteTarget(target.release())); -} - -TEST(WeakPtrDeathTest, NonOwnerThreadReferencesObjectAfterDeletion) { - // The default style "fast" does not support multi-threaded tests - // (introduces deadlock on Linux). - ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - - std::unique_ptr target(new Target()); - - // Main thread creates an arrow referencing the Target. - Arrow arrow; - arrow.target = target->AsWeakPtr(); - - // Background thread tries to delete target, binding the object to the thread. - BackgroundThread background; - background.Start(); - background.DeleteTarget(target.release()); - - // Main thread attempts to dereference the target, violating thread binding. - ASSERT_DCHECK_DEATH(arrow.target.get()); -} - -} // namespace base diff --git a/memory/weak_ptr_unittest.nc b/memory/weak_ptr_unittest.nc deleted file mode 100644 index b96b033d3..000000000 --- a/memory/weak_ptr_unittest.nc +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a "No Compile Test" suite. -// http://dev.chromium.org/developers/testing/no-compile-tests - -#include "base/memory/weak_ptr.h" - -namespace base { - -struct Producer : SupportsWeakPtr {}; -struct DerivedProducer : Producer {}; -struct OtherDerivedProducer : Producer {}; -struct MultiplyDerivedProducer : Producer, - SupportsWeakPtr {}; -struct Unrelated {}; -struct DerivedUnrelated : Unrelated {}; - -#if defined(NCTEST_AUTO_DOWNCAST) // [r"cannot initialize a variable of type 'base::DerivedProducer \*' with an rvalue of type 'base::Producer \*'"] - -void WontCompile() { - Producer f; - WeakPtr ptr = f.AsWeakPtr(); - WeakPtr derived_ptr = ptr; -} - -#elif defined(NCTEST_STATIC_DOWNCAST) // [r"cannot initialize a variable of type 'base::DerivedProducer \*' with an rvalue of type 'base::Producer \*'"] - -void WontCompile() { - Producer f; - WeakPtr ptr = f.AsWeakPtr(); - WeakPtr derived_ptr = - static_cast >(ptr); -} - -#elif defined(NCTEST_AUTO_REF_DOWNCAST) // [r"fatal error: non-const lvalue reference to type 'WeakPtr' cannot bind to a value of unrelated type 'WeakPtr'"] - -void WontCompile() { - Producer f; - WeakPtr ptr = f.AsWeakPtr(); - WeakPtr& derived_ptr = ptr; -} - -#elif defined(NCTEST_STATIC_REF_DOWNCAST) // [r"fatal error: non-const lvalue reference to type 'WeakPtr' cannot bind to a value of unrelated type 'WeakPtr'"] - -void WontCompile() { - Producer f; - WeakPtr ptr = f.AsWeakPtr(); - WeakPtr& derived_ptr = - static_cast&>(ptr); -} - -#elif defined(NCTEST_STATIC_ASWEAKPTR_DOWNCAST) // [r"no matching function"] - -void WontCompile() { - Producer f; - WeakPtr ptr = - SupportsWeakPtr::StaticAsWeakPtr(&f); -} - -#elif defined(NCTEST_UNSAFE_HELPER_DOWNCAST) // [r"cannot initialize a variable of type 'base::DerivedProducer \*' with an rvalue of type 'base::Producer \*'"] - -void WontCompile() { - Producer f; - WeakPtr ptr = AsWeakPtr(&f); -} - -#elif defined(NCTEST_UNSAFE_INSTANTIATED_HELPER_DOWNCAST) // [r"no matching function"] - -void WontCompile() { - Producer f; - WeakPtr ptr = AsWeakPtr(&f); -} - -#elif defined(NCTEST_UNSAFE_WRONG_INSANTIATED_HELPER_DOWNCAST) // [r"cannot initialize a variable of type 'base::DerivedProducer \*' with an rvalue of type 'base::Producer \*'"] - -void WontCompile() { - Producer f; - WeakPtr ptr = AsWeakPtr(&f); -} - -#elif defined(NCTEST_UNSAFE_HELPER_CAST) // [r"cannot initialize a variable of type 'base::OtherDerivedProducer \*' with an rvalue of type 'base::DerivedProducer \*'"] - -void WontCompile() { - DerivedProducer f; - WeakPtr ptr = AsWeakPtr(&f); -} - -#elif defined(NCTEST_UNSAFE_INSTANTIATED_HELPER_SIDECAST) // [r"fatal error: no matching function for call to 'AsWeakPtr'"] - -void WontCompile() { - DerivedProducer f; - WeakPtr ptr = AsWeakPtr(&f); -} - -#elif defined(NCTEST_UNSAFE_WRONG_INSTANTIATED_HELPER_SIDECAST) // [r"cannot initialize a variable of type 'base::OtherDerivedProducer \*' with an rvalue of type 'base::DerivedProducer \*'"] - -void WontCompile() { - DerivedProducer f; - WeakPtr ptr = AsWeakPtr(&f); -} - -#elif defined(NCTEST_UNRELATED_HELPER) // [r"cannot initialize a variable of type 'base::Unrelated \*' with an rvalue of type 'base::DerivedProducer \*'"] - -void WontCompile() { - DerivedProducer f; - WeakPtr ptr = AsWeakPtr(&f); -} - -#elif defined(NCTEST_UNRELATED_INSTANTIATED_HELPER) // [r"no matching function"] - -void WontCompile() { - DerivedProducer f; - WeakPtr ptr = AsWeakPtr(&f); -} - -// TODO(hans): Remove .* and update the static_assert expectations once we roll -// past Clang r313315. https://crbug.com/765692. - -#elif defined(NCTEST_COMPLETELY_UNRELATED_HELPER) // [r"fatal error: static_assert failed .*\"AsWeakPtr argument must inherit from SupportsWeakPtr\""] - -void WontCompile() { - Unrelated f; - WeakPtr ptr = AsWeakPtr(&f); -} - -#elif defined(NCTEST_DERIVED_COMPLETELY_UNRELATED_HELPER) // [r"fatal error: static_assert failed .*\"AsWeakPtr argument must inherit from SupportsWeakPtr\""] - -void WontCompile() { - DerivedUnrelated f; - WeakPtr ptr = AsWeakPtr(&f); -} - -#elif defined(NCTEST_AMBIGUOUS_ANCESTORS) // [r"fatal error: use of undeclared identifier 'AsWeakPtrImpl'"] - -void WontCompile() { - MultiplyDerivedProducer f; - WeakPtr ptr = AsWeakPtr(&f); -} - -#endif - -} diff --git a/memory/writable_shared_memory_region.cc b/memory/writable_shared_memory_region.cc deleted file mode 100644 index 063e6720c..000000000 --- a/memory/writable_shared_memory_region.cc +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2018 The Chromium 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/writable_shared_memory_region.h" - -#include - -#include "base/memory/shared_memory.h" -#include "build/build_config.h" - -namespace base { - -// static -WritableSharedMemoryRegion WritableSharedMemoryRegion::Create(size_t size) { - subtle::PlatformSharedMemoryRegion handle = - subtle::PlatformSharedMemoryRegion::CreateWritable(size); - - return WritableSharedMemoryRegion(std::move(handle)); -} - -// static -WritableSharedMemoryRegion WritableSharedMemoryRegion::Deserialize( - subtle::PlatformSharedMemoryRegion handle) { - return WritableSharedMemoryRegion(std::move(handle)); -} - -// static -subtle::PlatformSharedMemoryRegion -WritableSharedMemoryRegion::TakeHandleForSerialization( - WritableSharedMemoryRegion region) { - return std::move(region.handle_); -} - -// static -ReadOnlySharedMemoryRegion WritableSharedMemoryRegion::ConvertToReadOnly( - WritableSharedMemoryRegion region) { - subtle::PlatformSharedMemoryRegion handle = std::move(region.handle_); - if (!handle.ConvertToReadOnly()) - return {}; - - return ReadOnlySharedMemoryRegion::Deserialize(std::move(handle)); -} - -UnsafeSharedMemoryRegion WritableSharedMemoryRegion::ConvertToUnsafe( - WritableSharedMemoryRegion region) { - subtle::PlatformSharedMemoryRegion handle = std::move(region.handle_); - if (!handle.ConvertToUnsafe()) - return {}; - - return UnsafeSharedMemoryRegion::Deserialize(std::move(handle)); -} - -WritableSharedMemoryRegion::WritableSharedMemoryRegion() = default; -WritableSharedMemoryRegion::WritableSharedMemoryRegion( - WritableSharedMemoryRegion&& region) = default; -WritableSharedMemoryRegion& WritableSharedMemoryRegion::operator=( - WritableSharedMemoryRegion&& region) = default; -WritableSharedMemoryRegion::~WritableSharedMemoryRegion() = default; - -WritableSharedMemoryMapping WritableSharedMemoryRegion::Map() const { - return MapAt(0, handle_.GetSize()); -} - -WritableSharedMemoryMapping WritableSharedMemoryRegion::MapAt( - off_t offset, - size_t size) const { - if (!IsValid()) - return {}; - - void* memory = nullptr; - size_t mapped_size = 0; - if (!handle_.MapAt(offset, size, &memory, &mapped_size)) - return {}; - - return WritableSharedMemoryMapping(memory, size, mapped_size, - handle_.GetGUID()); -} - -bool WritableSharedMemoryRegion::IsValid() const { - return handle_.IsValid(); -} - -WritableSharedMemoryRegion::WritableSharedMemoryRegion( - subtle::PlatformSharedMemoryRegion handle) - : handle_(std::move(handle)) { - if (handle_.IsValid()) { - CHECK_EQ(handle_.GetMode(), - subtle::PlatformSharedMemoryRegion::Mode::kWritable); - } -} - -} // namespace base diff --git a/memory/writable_shared_memory_region.h b/memory/writable_shared_memory_region.h deleted file mode 100644 index f656db1ef..000000000 --- a/memory/writable_shared_memory_region.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2018 The Chromium 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_WRITABLE_SHARED_MEMORY_REGION_H_ -#define BASE_MEMORY_WRITABLE_SHARED_MEMORY_REGION_H_ - -#include "base/macros.h" -#include "base/memory/platform_shared_memory_region.h" -#include "base/memory/read_only_shared_memory_region.h" -#include "base/memory/shared_memory_mapping.h" -#include "base/memory/unsafe_shared_memory_region.h" - -namespace base { - -// Scoped move-only handle to a region of platform shared memory. The instance -// owns the platform handle it wraps. Mappings created by this region are -// writable. These mappings remain valid even after the region handle is moved -// or destroyed. -// -// This region can be locked to read-only access by converting it to a -// ReadOnlySharedMemoryRegion. However, unlike ReadOnlySharedMemoryRegion and -// UnsafeSharedMemoryRegion, ownership of this region (while writable) is unique -// and may only be transferred, not duplicated. -class BASE_EXPORT WritableSharedMemoryRegion { - public: - using MappingType = WritableSharedMemoryMapping; - // Creates a new WritableSharedMemoryRegion instance of a given - // size that can be used for mapping writable shared memory into the virtual - // address space. - static WritableSharedMemoryRegion Create(size_t size); - - // Returns a WritableSharedMemoryRegion built from a platform handle that was - // taken from another WritableSharedMemoryRegion instance. Returns an invalid - // region iff the |handle| is invalid. CHECK-fails if the |handle| isn't - // writable. - // This should be used only by the code passing handles across process - // boundaries. - static WritableSharedMemoryRegion Deserialize( - subtle::PlatformSharedMemoryRegion handle); - - // Extracts a platform handle from the region. Ownership is transferred to the - // returned region object. - // This should be used only for sending the handle from the current - // process to another. - static subtle::PlatformSharedMemoryRegion TakeHandleForSerialization( - WritableSharedMemoryRegion region); - - // Makes the region read-only. No new writable mappings of the region can be - // created after this call. Returns an invalid region on failure. - static ReadOnlySharedMemoryRegion ConvertToReadOnly( - WritableSharedMemoryRegion region); - - // Makes the region unsafe. The region cannot be converted to read-only after - // this call. Returns an invalid region on failure. - static UnsafeSharedMemoryRegion ConvertToUnsafe( - WritableSharedMemoryRegion region); - - // Default constructor initializes an invalid instance. - WritableSharedMemoryRegion(); - - // Move operations are allowed. - WritableSharedMemoryRegion(WritableSharedMemoryRegion&&); - WritableSharedMemoryRegion& operator=(WritableSharedMemoryRegion&&); - - // Destructor closes shared memory region if valid. - // All created mappings will remain valid. - ~WritableSharedMemoryRegion(); - - // Maps the shared memory region into the caller's address space with write - // access. The mapped address is guaranteed to have an alignment of - // at least |subtle::PlatformSharedMemoryRegion::kMapMinimumAlignment|. - // Returns a valid WritableSharedMemoryMapping instance on success, invalid - // otherwise. - WritableSharedMemoryMapping Map() const; - - // Same as above, but maps only |size| bytes of the shared memory block - // starting with the given |offset|. |offset| must be aligned to value of - // |SysInfo::VMAllocationGranularity()|. Returns an invalid mapping if - // requested bytes are out of the region limits. - WritableSharedMemoryMapping MapAt(off_t offset, size_t size) const; - - // Whether underlying platform handles are valid. - bool IsValid() const; - - // Returns the maximum mapping size that can be created from this region. - size_t GetSize() const { - DCHECK(IsValid()); - return handle_.GetSize(); - } - - // Returns 128-bit GUID of the region. - const UnguessableToken& GetGUID() const { - DCHECK(IsValid()); - return handle_.GetGUID(); - } - - private: - explicit WritableSharedMemoryRegion( - subtle::PlatformSharedMemoryRegion handle); - - subtle::PlatformSharedMemoryRegion handle_; - - DISALLOW_COPY_AND_ASSIGN(WritableSharedMemoryRegion); -}; - -} // namespace base - -#endif // BASE_MEMORY_WRITABLE_SHARED_MEMORY_REGION_H_ diff --git a/message_loop/incoming_task_queue.cc b/message_loop/incoming_task_queue.cc deleted file mode 100644 index 9576c3108..000000000 --- a/message_loop/incoming_task_queue.cc +++ /dev/null @@ -1,306 +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/message_loop/incoming_task_queue.h" - -#include -#include - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/location.h" -#include "base/metrics/histogram_macros.h" -#include "base/synchronization/waitable_event.h" -#include "base/time/time.h" -#include "build/build_config.h" - -namespace base { -namespace internal { - -namespace { - -#if DCHECK_IS_ON() -// Delays larger than this are often bogus, and a warning should be emitted in -// debug builds to warn developers. http://crbug.com/450045 -constexpr TimeDelta kTaskDelayWarningThreshold = TimeDelta::FromDays(14); -#endif - -TimeTicks CalculateDelayedRuntime(TimeDelta delay) { - TimeTicks delayed_run_time; - if (delay > TimeDelta()) - delayed_run_time = TimeTicks::Now() + delay; - else - DCHECK_EQ(delay.InMilliseconds(), 0) << "delay should not be negative"; - return delayed_run_time; -} - -} // namespace - -IncomingTaskQueue::IncomingTaskQueue( - std::unique_ptr task_queue_observer) - : task_queue_observer_(std::move(task_queue_observer)), - triage_tasks_(this) { - // The constructing sequence is not necessarily the running sequence, e.g. in - // the case of a MessageLoop created unbound. - DETACH_FROM_SEQUENCE(sequence_checker_); -} - -IncomingTaskQueue::~IncomingTaskQueue() = default; - -bool IncomingTaskQueue::AddToIncomingQueue(const Location& from_here, - OnceClosure task, - TimeDelta delay, - Nestable nestable) { - // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167 - // for details. - CHECK(task); - DLOG_IF(WARNING, delay > kTaskDelayWarningThreshold) - << "Requesting super-long task delay period of " << delay.InSeconds() - << " seconds from here: " << from_here.ToString(); - - PendingTask pending_task(from_here, std::move(task), - CalculateDelayedRuntime(delay), nestable); -#if defined(OS_WIN) - // We consider the task needs a high resolution timer if the delay is - // more than 0 and less than 32ms. This caps the relative error to - // less than 50% : a 33ms wait can wake at 48ms since the default - // resolution on Windows is between 10 and 15ms. - if (delay > TimeDelta() && - delay.InMilliseconds() < (2 * Time::kMinLowResolutionThresholdMs)) { - pending_task.is_high_res = true; - } -#endif - - if (!delay.is_zero()) - UMA_HISTOGRAM_LONG_TIMES("MessageLoop.DelayedTaskQueue.PostedDelay", delay); - - return PostPendingTask(&pending_task); -} - -void IncomingTaskQueue::Shutdown() { - AutoLock auto_lock(incoming_queue_lock_); - accept_new_tasks_ = false; -} - -void IncomingTaskQueue::ReportMetricsOnIdle() const { - UMA_HISTOGRAM_COUNTS_1M( - "MessageLoop.DelayedTaskQueue.PendingTasksCountOnIdle", - delayed_tasks_.Size()); -} - -IncomingTaskQueue::TriageQueue::TriageQueue(IncomingTaskQueue* outer) - : outer_(outer) {} - -IncomingTaskQueue::TriageQueue::~TriageQueue() = default; - -const PendingTask& IncomingTaskQueue::TriageQueue::Peek() { - DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); - ReloadFromIncomingQueueIfEmpty(); - DCHECK(!queue_.empty()); - return queue_.front(); -} - -PendingTask IncomingTaskQueue::TriageQueue::Pop() { - DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); - ReloadFromIncomingQueueIfEmpty(); - DCHECK(!queue_.empty()); - PendingTask pending_task = std::move(queue_.front()); - queue_.pop(); - return pending_task; -} - -bool IncomingTaskQueue::TriageQueue::HasTasks() { - DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); - ReloadFromIncomingQueueIfEmpty(); - return !queue_.empty(); -} - -void IncomingTaskQueue::TriageQueue::Clear() { - DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); - - // Clear() should be invoked before WillDestroyCurrentMessageLoop(). - DCHECK(outer_->accept_new_tasks_); - - // Delete all currently pending tasks but not tasks potentially posted from - // their destructors. See ~MessageLoop() for the full logic mitigating against - // infite loops when clearing pending tasks. The ScopedClosureRunner below - // will be bound to a task posted at the end of the queue. After it is posted, - // tasks will be deleted one by one, when the bound ScopedClosureRunner is - // deleted and sets |deleted_all_originally_pending|, we know we've deleted - // all originally pending tasks. - bool deleted_all_originally_pending = false; - ScopedClosureRunner capture_deleted_all_originally_pending(BindOnce( - [](bool* deleted_all_originally_pending) { - *deleted_all_originally_pending = true; - }, - Unretained(&deleted_all_originally_pending))); - outer_->AddToIncomingQueue( - FROM_HERE, - BindOnce([](ScopedClosureRunner) {}, - std::move(capture_deleted_all_originally_pending)), - TimeDelta(), Nestable::kNestable); - - while (!deleted_all_originally_pending) { - PendingTask pending_task = Pop(); - - if (!pending_task.delayed_run_time.is_null()) { - outer_->delayed_tasks().Push(std::move(pending_task)); - } - } -} - -void IncomingTaskQueue::TriageQueue::ReloadFromIncomingQueueIfEmpty() { - DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); - if (queue_.empty()) { - outer_->ReloadWorkQueue(&queue_); - } -} - -IncomingTaskQueue::DelayedQueue::DelayedQueue() { - DETACH_FROM_SEQUENCE(sequence_checker_); -} - -IncomingTaskQueue::DelayedQueue::~DelayedQueue() = default; - -void IncomingTaskQueue::DelayedQueue::Push(PendingTask pending_task) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - if (pending_task.is_high_res) - ++pending_high_res_tasks_; - - queue_.push(std::move(pending_task)); -} - -const PendingTask& IncomingTaskQueue::DelayedQueue::Peek() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!queue_.empty()); - return queue_.top(); -} - -PendingTask IncomingTaskQueue::DelayedQueue::Pop() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!queue_.empty()); - PendingTask delayed_task = std::move(const_cast(queue_.top())); - queue_.pop(); - - if (delayed_task.is_high_res) - --pending_high_res_tasks_; - DCHECK_GE(pending_high_res_tasks_, 0); - - return delayed_task; -} - -bool IncomingTaskQueue::DelayedQueue::HasTasks() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // TODO(robliao): The other queues don't check for IsCancelled(). Should they? - while (!queue_.empty() && Peek().task.IsCancelled()) - Pop(); - - return !queue_.empty(); -} - -void IncomingTaskQueue::DelayedQueue::Clear() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - while (!queue_.empty()) - Pop(); -} - -size_t IncomingTaskQueue::DelayedQueue::Size() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return queue_.size(); -} - -IncomingTaskQueue::DeferredQueue::DeferredQueue() { - DETACH_FROM_SEQUENCE(sequence_checker_); -} - -IncomingTaskQueue::DeferredQueue::~DeferredQueue() = default; - -void IncomingTaskQueue::DeferredQueue::Push(PendingTask pending_task) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - queue_.push(std::move(pending_task)); -} - -const PendingTask& IncomingTaskQueue::DeferredQueue::Peek() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!queue_.empty()); - return queue_.front(); -} - -PendingTask IncomingTaskQueue::DeferredQueue::Pop() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!queue_.empty()); - PendingTask deferred_task = std::move(queue_.front()); - queue_.pop(); - return deferred_task; -} - -bool IncomingTaskQueue::DeferredQueue::HasTasks() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return !queue_.empty(); -} - -void IncomingTaskQueue::DeferredQueue::Clear() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - while (!queue_.empty()) - Pop(); -} - -bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { - // Warning: Don't try to short-circuit, and handle this thread's tasks more - // directly, as it could starve handling of foreign threads. Put every task - // into this queue. - bool accept_new_tasks; - bool was_empty = false; - { - AutoLock auto_lock(incoming_queue_lock_); - accept_new_tasks = accept_new_tasks_; - if (accept_new_tasks) - was_empty = PostPendingTaskLockRequired(pending_task); - } - - if (!accept_new_tasks) { - // Clear the pending task outside of |incoming_queue_lock_| to prevent any - // chance of self-deadlock if destroying a task also posts a task to this - // queue. - pending_task->task.Reset(); - return false; - } - - // Let |task_queue_observer_| know of the queued task. This is done outside - // |incoming_queue_lock_| to avoid conflating locks (DidQueueTask() can also - // use a lock). - task_queue_observer_->DidQueueTask(was_empty); - - return true; -} - -bool IncomingTaskQueue::PostPendingTaskLockRequired(PendingTask* pending_task) { - incoming_queue_lock_.AssertAcquired(); - - // Initialize the sequence number. The sequence number is used for delayed - // tasks (to facilitate FIFO sorting when two tasks have the same - // delayed_run_time value) and for identifying the task in about:tracing. - pending_task->sequence_num = next_sequence_num_++; - - task_queue_observer_->WillQueueTask(pending_task); - - bool was_empty = incoming_queue_.empty(); - incoming_queue_.push(std::move(*pending_task)); - return was_empty; -} - -void IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - // Make sure no tasks are lost. - DCHECK(work_queue->empty()); - - // Acquire all we can from the inter-thread queue with one lock acquisition. - AutoLock lock(incoming_queue_lock_); - incoming_queue_.swap(*work_queue); -} - -} // namespace internal -} // namespace base diff --git a/message_loop/incoming_task_queue.h b/message_loop/incoming_task_queue.h deleted file mode 100644 index cf09df320..000000000 --- a/message_loop/incoming_task_queue.h +++ /dev/null @@ -1,272 +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_MESSAGE_LOOP_INCOMING_TASK_QUEUE_H_ -#define BASE_MESSAGE_LOOP_INCOMING_TASK_QUEUE_H_ - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/pending_task.h" -#include "base/sequence_checker.h" -#include "base/synchronization/lock.h" -#include "base/time/time.h" - -namespace base { - -class BasicPostTaskPerfTest; - -namespace internal { - -// Implements a queue of tasks posted to the message loop running on the current -// thread. This class takes care of synchronizing posting tasks from different -// threads and together with MessageLoop ensures clean shutdown. -class BASE_EXPORT IncomingTaskQueue - : public RefCountedThreadSafe { - public: - // TODO(gab): Move this to SequencedTaskSource::Observer in - // https://chromium-review.googlesource.com/c/chromium/src/+/1088762. - class Observer { - public: - virtual ~Observer() = default; - - // Notifies this Observer that it is about to enqueue |task|. The Observer - // may alter |task| as a result (e.g. add metadata to the PendingTask - // struct). This may be called while holding a lock and shouldn't perform - // logic requiring synchronization (override DidQueueTask() for that). - virtual void WillQueueTask(PendingTask* task) = 0; - - // Notifies this Observer that a task was queued in the IncomingTaskQueue it - // observes. |was_empty| is true if the task source was empty (i.e. - // |!HasTasks()|) before this task was posted. DidQueueTask() can be invoked - // from any thread. - virtual void DidQueueTask(bool was_empty) = 0; - }; - - // Provides a read and remove only view into a task queue. - class ReadAndRemoveOnlyQueue { - public: - ReadAndRemoveOnlyQueue() = default; - virtual ~ReadAndRemoveOnlyQueue() = default; - - // Returns the next task. HasTasks() is assumed to be true. - virtual const PendingTask& Peek() = 0; - - // Removes and returns the next task. HasTasks() is assumed to be true. - virtual PendingTask Pop() = 0; - - // Whether this queue has tasks. - virtual bool HasTasks() = 0; - - // Removes all tasks. - virtual void Clear() = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(ReadAndRemoveOnlyQueue); - }; - - // Provides a read-write task queue. - class Queue : public ReadAndRemoveOnlyQueue { - public: - Queue() = default; - ~Queue() override = default; - - // Adds the task to the end of the queue. - virtual void Push(PendingTask pending_task) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(Queue); - }; - - // Constructs an IncomingTaskQueue which will invoke |task_queue_observer| - // when tasks are queued. |task_queue_observer| will be bound to this - // IncomingTaskQueue's lifetime. Ownership is required as opposed to a raw - // pointer since IncomingTaskQueue is ref-counted. For the same reasons, - // |task_queue_observer| needs to support being invoked racily during - // shutdown). - explicit IncomingTaskQueue(std::unique_ptr task_queue_observer); - - // Appends a task to the incoming queue. Posting of all tasks is routed though - // AddToIncomingQueue() or TryAddToIncomingQueue() to make sure that posting - // task is properly synchronized between different threads. - // - // Returns true if the task was successfully added to the queue, otherwise - // returns false. In all cases, the ownership of |task| is transferred to the - // called method. - bool AddToIncomingQueue(const Location& from_here, - OnceClosure task, - TimeDelta delay, - Nestable nestable); - - // Instructs this IncomingTaskQueue to stop accepting tasks, this cannot be - // undone. Note that the registered IncomingTaskQueue::Observer may still - // racily receive a few DidQueueTask() calls while the Shutdown() signal - // propagates to other threads and it needs to support that. - void Shutdown(); - - ReadAndRemoveOnlyQueue& triage_tasks() { return triage_tasks_; } - - Queue& delayed_tasks() { return delayed_tasks_; } - - Queue& deferred_tasks() { return deferred_tasks_; } - - bool HasPendingHighResolutionTasks() const { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return delayed_tasks_.HasPendingHighResolutionTasks(); - } - - // Reports UMA metrics about its queues before the MessageLoop goes to sleep - // per being idle. - void ReportMetricsOnIdle() const; - - private: - friend class base::BasicPostTaskPerfTest; - friend class RefCountedThreadSafe; - - // These queues below support the previous MessageLoop behavior of - // maintaining three queue queues to process tasks: - // - // TriageQueue - // The first queue to receive all tasks for the processing sequence (when - // reloading from the thread-safe |incoming_queue_|). Tasks are generally - // either dispatched immediately or sent to the queues below. - // - // DelayedQueue - // The queue for holding tasks that should be run later and sorted by expected - // run time. - // - // DeferredQueue - // The queue for holding tasks that couldn't be run while the MessageLoop was - // nested. These are generally processed during the idle stage. - // - // Many of these do not share implementations even though they look like they - // could because of small quirks (reloading semantics) or differing underlying - // data strucutre (TaskQueue vs DelayedTaskQueue). - - // The starting point for all tasks on the sequence processing the tasks. - class TriageQueue : public ReadAndRemoveOnlyQueue { - public: - TriageQueue(IncomingTaskQueue* outer); - ~TriageQueue() override; - - // ReadAndRemoveOnlyQueue: - // The methods below will attempt to reload from the incoming queue if the - // queue itself is empty (Clear() has special logic to reload only once - // should destructors post more tasks). - const PendingTask& Peek() override; - PendingTask Pop() override; - // Whether this queue has tasks after reloading from the incoming queue. - bool HasTasks() override; - void Clear() override; - - private: - void ReloadFromIncomingQueueIfEmpty(); - - IncomingTaskQueue* const outer_; - TaskQueue queue_; - - DISALLOW_COPY_AND_ASSIGN(TriageQueue); - }; - - class DelayedQueue : public Queue { - public: - DelayedQueue(); - ~DelayedQueue() override; - - // Queue: - const PendingTask& Peek() override; - PendingTask Pop() override; - // Whether this queue has tasks after sweeping the cancelled ones in front. - bool HasTasks() override; - void Clear() override; - void Push(PendingTask pending_task) override; - - size_t Size() const; - bool HasPendingHighResolutionTasks() const { - return pending_high_res_tasks_ > 0; - } - - private: - DelayedTaskQueue queue_; - - // Number of high resolution tasks in |queue_|. - int pending_high_res_tasks_ = 0; - - SEQUENCE_CHECKER(sequence_checker_); - - DISALLOW_COPY_AND_ASSIGN(DelayedQueue); - }; - - class DeferredQueue : public Queue { - public: - DeferredQueue(); - ~DeferredQueue() override; - - // Queue: - const PendingTask& Peek() override; - PendingTask Pop() override; - bool HasTasks() override; - void Clear() override; - void Push(PendingTask pending_task) override; - - private: - TaskQueue queue_; - - SEQUENCE_CHECKER(sequence_checker_); - - DISALLOW_COPY_AND_ASSIGN(DeferredQueue); - }; - - virtual ~IncomingTaskQueue(); - - // Adds a task to |incoming_queue_|. The caller retains ownership of - // |pending_task|, but this function will reset the value of - // |pending_task->task|. This is needed to ensure that the posting call stack - // does not retain |pending_task->task| beyond this function call. - bool PostPendingTask(PendingTask* pending_task); - - // Does the real work of posting a pending task. Returns true if - // |incoming_queue_| was empty before |pending_task| was posted. - bool PostPendingTaskLockRequired(PendingTask* pending_task); - - // Loads tasks from the |incoming_queue_| into |*work_queue|. Must be called - // from the sequence processing the tasks. - void ReloadWorkQueue(TaskQueue* work_queue); - - // Checks calls made only on the MessageLoop thread. - SEQUENCE_CHECKER(sequence_checker_); - - const std::unique_ptr task_queue_observer_; - - // Queue for initial triaging of tasks on the |sequence_checker_| sequence. - TriageQueue triage_tasks_; - - // Queue for delayed tasks on the |sequence_checker_| sequence. - DelayedQueue delayed_tasks_; - - // Queue for non-nestable deferred tasks on the |sequence_checker_| sequence. - DeferredQueue deferred_tasks_; - - // Synchronizes access to all members below this line. - base::Lock incoming_queue_lock_; - - // An incoming queue of tasks that are acquired under a mutex for processing - // on this instance's thread. These tasks have not yet been been pushed to - // |triage_tasks_|. - TaskQueue incoming_queue_; - - // True if new tasks should be accepted. - bool accept_new_tasks_ = true; - - // The next sequence number to use for delayed tasks. - int next_sequence_num_ = 0; - - DISALLOW_COPY_AND_ASSIGN(IncomingTaskQueue); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_MESSAGE_LOOP_INCOMING_TASK_QUEUE_H_ diff --git a/message_loop/message_loop.cc b/message_loop/message_loop.cc deleted file mode 100644 index 3c715c8e6..000000000 --- a/message_loop/message_loop.cc +++ /dev/null @@ -1,756 +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/message_loop/message_loop.h" - -#include -#include - -#include "base/bind.h" -#include "base/compiler_specific.h" -#include "base/debug/task_annotator.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_pump_default.h" -#include "base/message_loop/message_pump_for_io.h" -#include "base/message_loop/message_pump_for_ui.h" -#include "base/metrics/histogram_macros.h" -#include "base/run_loop.h" -#include "base/third_party/dynamic_annotations/dynamic_annotations.h" -#include "base/threading/thread_id_name_manager.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/trace_event/trace_event.h" - -#if defined(OS_MACOSX) -#include "base/message_loop/message_pump_mac.h" -#endif - -namespace base { - -namespace { - -MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = nullptr; - -std::unique_ptr ReturnPump(std::unique_ptr pump) { - return pump; -} - -enum class ScheduledWakeupResult { - // The MessageLoop went to sleep with a timeout and woke up because of that - // timeout. - kCompleted, - // The MessageLoop went to sleep with a timeout but was woken up before it - // fired. - kInterrupted, -}; - -// Reports a ScheduledWakeup's result when waking up from a non-infinite sleep. -// Reports are using a 14 day spread (maximum examined delay for -// https://crbug.com/850450#c3), with 50 buckets that still yields 7 buckets -// under 16ms and hence plenty of resolution. -void ReportScheduledWakeupResult(ScheduledWakeupResult result, - TimeDelta intended_sleep) { - switch (result) { - case ScheduledWakeupResult::kCompleted: - UMA_HISTOGRAM_CUSTOM_TIMES("MessageLoop.ScheduledSleep.Completed", - intended_sleep, - base::TimeDelta::FromMilliseconds(1), - base::TimeDelta::FromDays(14), 50); - break; - case ScheduledWakeupResult::kInterrupted: - UMA_HISTOGRAM_CUSTOM_TIMES("MessageLoop.ScheduledSleep.Interrupted", - intended_sleep, - base::TimeDelta::FromMilliseconds(1), - base::TimeDelta::FromDays(14), 50); - break; - } -} - -} // namespace - -class MessageLoop::Controller : public internal::IncomingTaskQueue::Observer { - public: - // Constructs a MessageLoopController which controls |message_loop|, notifying - // |task_annotator_| when tasks are queued scheduling work on |message_loop| - // as fits. |message_loop| and |task_annotator_| will not be used after - // DisconnectFromParent() returns. - Controller(MessageLoop* message_loop); - - ~Controller() override; - - // IncomingTaskQueue::Observer: - void WillQueueTask(PendingTask* task) final; - void DidQueueTask(bool was_empty) final; - - void StartScheduling(); - - // Disconnects |message_loop_| from this Controller instance (DidQueueTask() - // will no-op from this point forward). - void DisconnectFromParent(); - - // Shares this Controller's TaskAnnotator with MessageLoop as TaskAnnotator - // requires DidQueueTask(x)/RunTask(x) to be invoked on the same TaskAnnotator - // instance. - debug::TaskAnnotator& task_annotator() { return task_annotator_; } - - private: - // A TaskAnnotator which is owned by this Controller to be able to use it - // without locking |message_loop_lock_|. It cannot be owned by MessageLoop - // because this Controller cannot access |message_loop_| safely without the - // lock. Note: the TaskAnnotator API itself is thread-safe. - debug::TaskAnnotator task_annotator_; - - // Lock that serializes |message_loop_->ScheduleWork()| and access to all - // members below. - base::Lock message_loop_lock_; - - // Points to this Controller's outer MessageLoop instance. Null after - // DisconnectFromParent(). - MessageLoop* message_loop_; - - // False until StartScheduling() is called. - bool is_ready_for_scheduling_ = false; - - // True if DidQueueTask() has been called before StartScheduling(); letting it - // know whether it needs to ScheduleWork() right away or not. - bool pending_schedule_work_ = false; - - DISALLOW_COPY_AND_ASSIGN(Controller); -}; - -MessageLoop::Controller::Controller(MessageLoop* message_loop) - : message_loop_(message_loop) {} - -MessageLoop::Controller::~Controller() { - DCHECK(!message_loop_) - << "DisconnectFromParent() needs to be invoked before destruction."; -} - -void MessageLoop::Controller::WillQueueTask(PendingTask* task) { - task_annotator_.WillQueueTask("MessageLoop::PostTask", task); -} - -void MessageLoop::Controller::DidQueueTask(bool was_empty) { - // Avoid locking if we don't need to schedule. - if (!was_empty) - return; - - AutoLock auto_lock(message_loop_lock_); - - if (message_loop_ && is_ready_for_scheduling_) - message_loop_->ScheduleWork(); - else - pending_schedule_work_ = true; -} - -void MessageLoop::Controller::StartScheduling() { - AutoLock lock(message_loop_lock_); - DCHECK(message_loop_); - DCHECK(!is_ready_for_scheduling_); - is_ready_for_scheduling_ = true; - if (pending_schedule_work_) - message_loop_->ScheduleWork(); -} - -void MessageLoop::Controller::DisconnectFromParent() { - AutoLock lock(message_loop_lock_); - message_loop_ = nullptr; -} - -//------------------------------------------------------------------------------ - -MessageLoop::MessageLoop(Type type) - : MessageLoop(type, MessagePumpFactoryCallback()) { - BindToCurrentThread(); -} - -MessageLoop::MessageLoop(std::unique_ptr pump) - : MessageLoop(TYPE_CUSTOM, BindOnce(&ReturnPump, std::move(pump))) { - BindToCurrentThread(); -} - -MessageLoop::~MessageLoop() { - // If |pump_| is non-null, this message loop has been bound and should be the - // current one on this thread. Otherwise, this loop is being destructed before - // it was bound to a thread, so a different message loop (or no loop at all) - // may be current. - DCHECK((pump_ && MessageLoopCurrent::IsBoundToCurrentThreadInternal(this)) || - (!pump_ && !MessageLoopCurrent::IsBoundToCurrentThreadInternal(this))); - - // iOS just attaches to the loop, it doesn't Run it. - // TODO(stuartmorgan): Consider wiring up a Detach(). -#if !defined(OS_IOS) - // There should be no active RunLoops on this thread, unless this MessageLoop - // isn't bound to the current thread (see other condition at the top of this - // method). - DCHECK( - (!pump_ && !MessageLoopCurrent::IsBoundToCurrentThreadInternal(this)) || - !RunLoop::IsRunningOnCurrentThread()); -#endif // !defined(OS_IOS) - -#if defined(OS_WIN) - if (in_high_res_mode_) - Time::ActivateHighResolutionTimer(false); -#endif // defined (OS_WIN) - - // Clean up any unprocessed tasks, but take care: deleting a task could - // result in the addition of more tasks (e.g., via DeleteSoon). We set a - // limit on the number of times we will allow a deleted task to generate more - // tasks. Normally, we should only pass through this loop once or twice. If - // we end up hitting the loop limit, then it is probably due to one task that - // is being stubborn. Inspect the queues to see who is left. - bool tasks_remain; - for (int i = 0; i < 100; ++i) { - DeletePendingTasks(); - // If we end up with empty queues, then break out of the loop. - tasks_remain = incoming_task_queue_->triage_tasks().HasTasks(); - if (!tasks_remain) - break; - } - DCHECK(!tasks_remain); - - // Let interested parties have one last shot at accessing this. - for (auto& observer : destruction_observers_) - observer.WillDestroyCurrentMessageLoop(); - - thread_task_runner_handle_.reset(); - - // Tell the incoming queue that we are dying. - message_loop_controller_->DisconnectFromParent(); - incoming_task_queue_->Shutdown(); - incoming_task_queue_ = nullptr; - unbound_task_runner_ = nullptr; - task_runner_ = nullptr; - - // OK, now make it so that no one can find us. - if (MessageLoopCurrent::IsBoundToCurrentThreadInternal(this)) - MessageLoopCurrent::UnbindFromCurrentThreadInternal(this); -} - -// static -MessageLoopCurrent MessageLoop::current() { - return MessageLoopCurrent::Get(); -} - -// static -bool MessageLoop::InitMessagePumpForUIFactory(MessagePumpFactory* factory) { - if (message_pump_for_ui_factory_) - return false; - - message_pump_for_ui_factory_ = factory; - return true; -} - -// static -std::unique_ptr MessageLoop::CreateMessagePumpForType(Type type) { - if (type == MessageLoop::TYPE_UI) { - if (message_pump_for_ui_factory_) - return message_pump_for_ui_factory_(); -#if defined(OS_IOS) || defined(OS_MACOSX) - return MessagePumpMac::Create(); -#elif defined(OS_NACL) || defined(OS_AIX) - // Currently NaCl and AIX don't have a UI MessageLoop. - // TODO(abarth): Figure out if we need this. - NOTREACHED(); - return nullptr; -#else - return std::make_unique(); -#endif - } - - if (type == MessageLoop::TYPE_IO) - return std::unique_ptr(new MessagePumpForIO()); - -#if defined(OS_ANDROID) - if (type == MessageLoop::TYPE_JAVA) - return std::unique_ptr(new MessagePumpForUI()); -#endif - - DCHECK_EQ(MessageLoop::TYPE_DEFAULT, type); -#if defined(OS_IOS) - // On iOS, a native runloop is always required to pump system work. - return std::make_unique(); -#else - return std::make_unique(); -#endif -} - -bool MessageLoop::IsType(Type type) const { - return type_ == type; -} - -// TODO(gab): Migrate TaskObservers to RunLoop as part of separating concerns -// between MessageLoop and RunLoop and making MessageLoop a swappable -// implementation detail. http://crbug.com/703346 -void MessageLoop::AddTaskObserver(TaskObserver* task_observer) { - DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); - task_observers_.AddObserver(task_observer); -} - -void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) { - DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); - task_observers_.RemoveObserver(task_observer); -} - -bool MessageLoop::IsIdleForTesting() { - // Have unprocessed tasks? (this reloads the work queue if necessary) - if (incoming_task_queue_->triage_tasks().HasTasks()) - return false; - - // Have unprocessed deferred tasks which can be processed at this run-level? - if (incoming_task_queue_->deferred_tasks().HasTasks() && - !RunLoop::IsNestedOnCurrentThread()) { - return false; - } - - return true; -} - -//------------------------------------------------------------------------------ - -// static -std::unique_ptr MessageLoop::CreateUnbound( - Type type, - MessagePumpFactoryCallback pump_factory) { - return WrapUnique(new MessageLoop(type, std::move(pump_factory))); -} - -// TODO(gab): Avoid bare new + WrapUnique below when introducing -// SequencedTaskSource in follow-up @ -// https://chromium-review.googlesource.com/c/chromium/src/+/1088762. -MessageLoop::MessageLoop(Type type, MessagePumpFactoryCallback pump_factory) - : MessageLoopCurrent(this), - type_(type), - pump_factory_(std::move(pump_factory)), - message_loop_controller_(new Controller(this)), - incoming_task_queue_(MakeRefCounted( - WrapUnique(message_loop_controller_))), - unbound_task_runner_(MakeRefCounted( - incoming_task_queue_)), - task_runner_(unbound_task_runner_) { - // If type is TYPE_CUSTOM non-null pump_factory must be given. - DCHECK(type_ != TYPE_CUSTOM || !pump_factory_.is_null()); - - // Bound in BindToCurrentThread(); - DETACH_FROM_THREAD(bound_thread_checker_); -} - -void MessageLoop::BindToCurrentThread() { - DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); - - DCHECK(!pump_); - if (!pump_factory_.is_null()) - pump_ = std::move(pump_factory_).Run(); - else - pump_ = CreateMessagePumpForType(type_); - - DCHECK(!MessageLoopCurrent::IsSet()) - << "should only have one message loop per thread"; - MessageLoopCurrent::BindToCurrentThreadInternal(this); - - message_loop_controller_->StartScheduling(); - unbound_task_runner_->BindToCurrentThread(); - unbound_task_runner_ = nullptr; - SetThreadTaskRunnerHandle(); - thread_id_ = PlatformThread::CurrentId(); - - scoped_set_sequence_local_storage_map_for_current_thread_ = std::make_unique< - internal::ScopedSetSequenceLocalStorageMapForCurrentThread>( - &sequence_local_storage_map_); - - RunLoop::RegisterDelegateForCurrentThread(this); - -#if defined(OS_ANDROID) - // On Android, attach to the native loop when there is one. - if (type_ == TYPE_UI || type_ == TYPE_JAVA) - static_cast(pump_.get())->Attach(this); -#endif -} - -std::string MessageLoop::GetThreadName() const { - DCHECK_NE(kInvalidThreadId, thread_id_) - << "GetThreadName() must only be called after BindToCurrentThread()'s " - << "side-effects have been synchronized with this thread."; - return ThreadIdNameManager::GetInstance()->GetName(thread_id_); -} - -void MessageLoop::SetTaskRunner( - scoped_refptr task_runner) { - DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); - - DCHECK(task_runner); - DCHECK(task_runner->BelongsToCurrentThread()); - DCHECK(!unbound_task_runner_); - task_runner_ = std::move(task_runner); - SetThreadTaskRunnerHandle(); -} - -void MessageLoop::ClearTaskRunnerForTesting() { - DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); - - DCHECK(!unbound_task_runner_); - task_runner_ = nullptr; - thread_task_runner_handle_.reset(); -} - -void MessageLoop::Run(bool application_tasks_allowed) { - DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); - if (application_tasks_allowed && !task_execution_allowed_) { - // Allow nested task execution as explicitly requested. - DCHECK(RunLoop::IsNestedOnCurrentThread()); - task_execution_allowed_ = true; - pump_->Run(this); - task_execution_allowed_ = false; - } else { - pump_->Run(this); - } -} - -void MessageLoop::Quit() { - DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); - pump_->Quit(); -} - -void MessageLoop::EnsureWorkScheduled() { - DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); - if (incoming_task_queue_->triage_tasks().HasTasks()) - pump_->ScheduleWork(); -} - -void MessageLoop::SetThreadTaskRunnerHandle() { - DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_); - // Clear the previous thread task runner first, because only one can exist at - // a time. - thread_task_runner_handle_.reset(); - thread_task_runner_handle_.reset(new ThreadTaskRunnerHandle(task_runner_)); -} - -bool MessageLoop::ProcessNextDelayedNonNestableTask() { - if (RunLoop::IsNestedOnCurrentThread()) - return false; - - while (incoming_task_queue_->deferred_tasks().HasTasks()) { - PendingTask pending_task = incoming_task_queue_->deferred_tasks().Pop(); - if (!pending_task.task.IsCancelled()) { - RunTask(&pending_task); - return true; - } - } - - return false; -} - -void MessageLoop::RunTask(PendingTask* pending_task) { - DCHECK(task_execution_allowed_); - - // Execute the task and assume the worst: It is probably not reentrant. - task_execution_allowed_ = false; - - TRACE_TASK_EXECUTION("MessageLoop::RunTask", *pending_task); - - for (auto& observer : task_observers_) - observer.WillProcessTask(*pending_task); - message_loop_controller_->task_annotator().RunTask("MessageLoop::PostTask", - pending_task); - for (auto& observer : task_observers_) - observer.DidProcessTask(*pending_task); - - task_execution_allowed_ = true; -} - -bool MessageLoop::DeferOrRunPendingTask(PendingTask pending_task) { - if (pending_task.nestable == Nestable::kNestable || - !RunLoop::IsNestedOnCurrentThread()) { - RunTask(&pending_task); - // Show that we ran a task (Note: a new one might arrive as a - // consequence!). - return true; - } - - // We couldn't run the task now because we're in a nested run loop - // and the task isn't nestable. - incoming_task_queue_->deferred_tasks().Push(std::move(pending_task)); - return false; -} - -void MessageLoop::DeletePendingTasks() { - incoming_task_queue_->triage_tasks().Clear(); - incoming_task_queue_->deferred_tasks().Clear(); - // TODO(robliao): Determine if we can move delayed task destruction before - // deferred tasks to maintain the MessagePump DoWork, DoDelayedWork, and - // DoIdleWork processing order. - incoming_task_queue_->delayed_tasks().Clear(); -} - -void MessageLoop::ScheduleWork() { - pump_->ScheduleWork(); -} - -bool MessageLoop::DoWork() { - if (!task_execution_allowed_) - return false; - -#if defined(OS_WIN) - // Raising timer frequency has a system-wide effect. Ensure this thread's vote - // to raise the frequency is only active while it is sleeping (with pending - // hi-res tasks) to avoid unnecessarily keeping the entire system in a high - // frequency mode while this thread isn't sleeping (even if it has pending - // hi-res delayed tasks). Ref. https://crbug.com/854237#c3 - if (in_high_res_mode_) { - in_high_res_mode_ = false; - Time::ActivateHighResolutionTimer(false); - } -#endif // defined (OS_WIN) - - // Execute oldest task. - while (incoming_task_queue_->triage_tasks().HasTasks()) { - if (!scheduled_wakeup_.next_run_time.is_null()) { - // While the frontmost task may racily be ripe. The MessageLoop was awaken - // without needing the timeout anyways. Since this metric is about - // determining whether sleeping for long periods ever succeeds: it's - // easier to just consider any untriaged task as an interrupt (this also - // makes the logic simpler for untriaged delayed tasks which may alter the - // top of the task queue prior to DoDelayedWork() but did cause a wakeup - // regardless -- per currently requiring this immediate triage step even - // for long delays). - ReportScheduledWakeupResult(ScheduledWakeupResult::kInterrupted, - scheduled_wakeup_.intended_sleep); - scheduled_wakeup_ = ScheduledWakeup(); - } - - PendingTask pending_task = incoming_task_queue_->triage_tasks().Pop(); - if (pending_task.task.IsCancelled()) - continue; - - if (!pending_task.delayed_run_time.is_null()) { - int sequence_num = pending_task.sequence_num; - TimeTicks delayed_run_time = pending_task.delayed_run_time; - incoming_task_queue_->delayed_tasks().Push(std::move(pending_task)); - // If we changed the topmost task, then it is time to reschedule. - if (incoming_task_queue_->delayed_tasks().Peek().sequence_num == - sequence_num) { - pump_->ScheduleDelayedWork(delayed_run_time); - } - } else if (DeferOrRunPendingTask(std::move(pending_task))) { - return true; - } - } - - // Nothing happened. - return false; -} - -bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) { - if (!task_execution_allowed_) { - *next_delayed_work_time = TimeTicks(); - // |scheduled_wakeup_| isn't used in nested loops that don't process - // application tasks. - DCHECK(scheduled_wakeup_.next_run_time.is_null()); - return false; - } - - if (!incoming_task_queue_->delayed_tasks().HasTasks()) { - *next_delayed_work_time = TimeTicks(); - - // It's possible to be woken up by a system event and have it cancel the - // upcoming delayed task from under us before DoDelayedWork() -- see comment - // under |next_run_time > recent_time_|. This condition covers the special - // case where such a system event cancelled *all* pending delayed tasks. - if (!scheduled_wakeup_.next_run_time.is_null()) { - ReportScheduledWakeupResult(ScheduledWakeupResult::kInterrupted, - scheduled_wakeup_.intended_sleep); - scheduled_wakeup_ = ScheduledWakeup(); - } - - return false; - } - - // When we "fall behind", there will be a lot of tasks in the delayed work - // queue that are ready to run. To increase efficiency when we fall behind, - // we will only call Time::Now() intermittently, and then process all tasks - // that are ready to run before calling it again. As a result, the more we - // fall behind (and have a lot of ready-to-run delayed tasks), the more - // efficient we'll be at handling the tasks. - - TimeTicks next_run_time = - incoming_task_queue_->delayed_tasks().Peek().delayed_run_time; - - if (next_run_time > recent_time_) { - recent_time_ = TimeTicks::Now(); // Get a better view of Now(); - if (next_run_time > recent_time_) { - *next_delayed_work_time = next_run_time; - - // If the loop was woken up early by an untriaged task: - // |scheduled_wakeup_| will have been handled already in DoWork(). If it - // wasn't, it means the early wake up was caused by a system event (e.g. - // MessageLoopForUI or IO). - if (!scheduled_wakeup_.next_run_time.is_null()) { - // Handling the system event may have resulted in cancelling the - // upcoming delayed task (and then it being pruned by - // DelayedTaskQueue::HasTasks()); hence, we cannot check for strict - // equality here. We can however check that the pending task is either - // still there or that a later delay replaced it in front of the queue. - // There shouldn't have been new tasks added in |delayed_tasks()| per - // DoWork() not having triaged new tasks since the last DoIdleWork(). - DCHECK_GE(next_run_time, scheduled_wakeup_.next_run_time); - - ReportScheduledWakeupResult(ScheduledWakeupResult::kInterrupted, - scheduled_wakeup_.intended_sleep); - scheduled_wakeup_ = ScheduledWakeup(); - } - - return false; - } - } - - if (next_run_time == scheduled_wakeup_.next_run_time) { - ReportScheduledWakeupResult(ScheduledWakeupResult::kCompleted, - scheduled_wakeup_.intended_sleep); - scheduled_wakeup_ = ScheduledWakeup(); - } - - PendingTask pending_task = incoming_task_queue_->delayed_tasks().Pop(); - - if (incoming_task_queue_->delayed_tasks().HasTasks()) { - *next_delayed_work_time = - incoming_task_queue_->delayed_tasks().Peek().delayed_run_time; - } - - return DeferOrRunPendingTask(std::move(pending_task)); -} - -bool MessageLoop::DoIdleWork() { - if (ProcessNextDelayedNonNestableTask()) - return true; - -#if defined(OS_WIN) - bool need_high_res_timers = false; -#endif - - // Do not report idle metrics nor do any logic related to delayed tasks if - // about to quit the loop and/or in a nested loop where - // |!task_execution_allowed_|. In the former case, the loop isn't going to - // sleep and in the latter case DoDelayedWork() will not actually do the work - // this is prepping for. - if (ShouldQuitWhenIdle()) { - pump_->Quit(); - } else if (task_execution_allowed_) { - incoming_task_queue_->ReportMetricsOnIdle(); - - if (incoming_task_queue_->delayed_tasks().HasTasks()) { - TimeTicks scheduled_wakeup_time = - incoming_task_queue_->delayed_tasks().Peek().delayed_run_time; - - if (!scheduled_wakeup_.next_run_time.is_null()) { - // It's possible for DoIdleWork() to be invoked twice in a row (e.g. if - // the MessagePump processed system work and became idle twice in a row - // without application tasks in between -- some pumps with a native - // message loop do not invoke DoWork() / DoDelayedWork() when awaken for - // system work only). As in DoDelayedWork(), we cannot check for strict - // equality below as the system work may have cancelled the frontmost - // task. - DCHECK_GE(scheduled_wakeup_time, scheduled_wakeup_.next_run_time); - - ReportScheduledWakeupResult(ScheduledWakeupResult::kInterrupted, - scheduled_wakeup_.intended_sleep); - scheduled_wakeup_ = ScheduledWakeup(); - } - - // Store the remaining delay as well as the programmed wakeup time in - // order to know next time this MessageLoop wakes up whether it woke up - // because of this pending task (is it still the frontmost task in the - // queue?) and be able to report the slept delta (which is lost if not - // saved here). - scheduled_wakeup_ = ScheduledWakeup{ - scheduled_wakeup_time, scheduled_wakeup_time - TimeTicks::Now()}; - } - -#if defined(OS_WIN) - // On Windows we activate the high resolution timer so that the wait - // _if_ triggered by the timer happens with good resolution. If we don't - // do this the default resolution is 15ms which might not be acceptable - // for some tasks. - need_high_res_timers = - incoming_task_queue_->HasPendingHighResolutionTasks(); -#endif // defined (OS_WIN) - } - -#if defined(OS_WIN) - if (need_high_res_timers) { - DCHECK(!in_high_res_mode_); - in_high_res_mode_ = true; - Time::ActivateHighResolutionTimer(true); - } -#endif // defined (OS_WIN) - - // When we return we will do a kernel wait for more tasks. - return false; -} - -#if !defined(OS_NACL) - -//------------------------------------------------------------------------------ -// MessageLoopForUI - -MessageLoopForUI::MessageLoopForUI(Type type) : MessageLoop(type) { -#if defined(OS_ANDROID) - DCHECK(type == TYPE_UI || type == TYPE_JAVA); -#else - DCHECK_EQ(type, TYPE_UI); -#endif -} - -// static -MessageLoopCurrentForUI MessageLoopForUI::current() { - return MessageLoopCurrentForUI::Get(); -} - -// static -bool MessageLoopForUI::IsCurrent() { - return MessageLoopCurrentForUI::IsSet(); -} - -#if defined(OS_IOS) -void MessageLoopForUI::Attach() { - static_cast(pump_.get())->Attach(this); -} -#endif // defined(OS_IOS) - -#if defined(OS_ANDROID) -void MessageLoopForUI::Abort() { - static_cast(pump_.get())->Abort(); -} - -bool MessageLoopForUI::IsAborted() { - return static_cast(pump_.get())->IsAborted(); -} - -void MessageLoopForUI::QuitWhenIdle(base::OnceClosure callback) { - static_cast(pump_.get()) - ->QuitWhenIdle(std::move(callback)); -} -#endif // defined(OS_ANDROID) - -#if defined(OS_WIN) -void MessageLoopForUI::EnableWmQuit() { - static_cast(pump_.get())->EnableWmQuit(); -} -#endif // defined(OS_WIN) - -#endif // !defined(OS_NACL) - -//------------------------------------------------------------------------------ -// MessageLoopForIO - -// static -MessageLoopCurrentForIO MessageLoopForIO::current() { - return MessageLoopCurrentForIO::Get(); -} - -// static -bool MessageLoopForIO::IsCurrent() { - return MessageLoopCurrentForIO::IsSet(); -} - -} // namespace base diff --git a/message_loop/message_loop.h b/message_loop/message_loop.h deleted file mode 100644 index f34416015..000000000 --- a/message_loop/message_loop.h +++ /dev/null @@ -1,423 +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_MESSAGE_LOOP_MESSAGE_LOOP_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_H_ - -#include -#include -#include - -#include "base/base_export.h" -#include "base/callback_forward.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/memory/scoped_refptr.h" -#include "base/message_loop/incoming_task_queue.h" -#include "base/message_loop/message_loop_current.h" -#include "base/message_loop/message_loop_task_runner.h" -#include "base/message_loop/message_pump.h" -#include "base/message_loop/timer_slack.h" -#include "base/observer_list.h" -#include "base/pending_task.h" -#include "base/run_loop.h" -#include "base/synchronization/lock.h" -#include "base/threading/sequence_local_storage_map.h" -#include "base/threading/thread_checker.h" -#include "base/time/time.h" -#include "build/build_config.h" - -namespace base { - -class ThreadTaskRunnerHandle; - -// A MessageLoop is used to process events for a particular thread. There is -// at most one MessageLoop instance per thread. -// -// Events include at a minimum Task instances submitted to the MessageLoop's -// TaskRunner. Depending on the type of message pump used by the MessageLoop -// other events such as UI messages may be processed. On Windows APC calls (as -// time permits) and signals sent to a registered set of HANDLEs may also be -// processed. -// -// The MessageLoop's API should only be used directly by its owner (and users -// which the owner opts to share a MessageLoop* with). Other ways to access -// subsets of the MessageLoop API: -// - base::RunLoop : Drive the MessageLoop from the thread it's bound to. -// - base::Thread/SequencedTaskRunnerHandle : Post back to the MessageLoop -// from a task running on it. -// - SequenceLocalStorageSlot : Bind external state to this MessageLoop. -// - base::MessageLoopCurrent : Access statically exposed APIs of this -// MessageLoop. -// - Embedders may provide their own static accessors to post tasks on -// specific loops (e.g. content::BrowserThreads). -// -// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called -// on the thread where the MessageLoop's Run method executes. -// -// NOTE: MessageLoop has task reentrancy protection. This means that if a -// task is being processed, a second task cannot start until the first task is -// finished. Reentrancy can happen when processing a task, and an inner -// message pump is created. That inner pump then processes native messages -// which could implicitly start an inner task. Inner message pumps are created -// with dialogs (DialogBox), common dialogs (GetOpenFileName), OLE functions -// (DoDragDrop), printer functions (StartDoc) and *many* others. -// -// Sample workaround when inner task processing is needed: -// HRESULT hr; -// { -// MessageLoopCurrent::ScopedNestableTaskAllower allow; -// hr = DoDragDrop(...); // Implicitly runs a modal message loop. -// } -// // Process |hr| (the result returned by DoDragDrop()). -// -// Please be SURE your task is reentrant (nestable) and all global variables -// are stable and accessible before calling SetNestableTasksAllowed(true). -// -// TODO(gab): MessageLoop doesn't need to be a MessageLoopCurrent once callers -// that store MessageLoop::current() in a MessageLoop* variable have been -// updated to use a MessageLoopCurrent variable. -class BASE_EXPORT MessageLoop : public MessagePump::Delegate, - public RunLoop::Delegate, - public MessageLoopCurrent { - public: - // TODO(gab): Migrate usage of this class to MessageLoopCurrent and remove - // this forwarded declaration. - using DestructionObserver = MessageLoopCurrent::DestructionObserver; - - // A MessageLoop has a particular type, which indicates the set of - // asynchronous events it may process in addition to tasks and timers. - // - // TYPE_DEFAULT - // This type of ML only supports tasks and timers. - // - // TYPE_UI - // This type of ML also supports native UI events (e.g., Windows messages). - // See also MessageLoopForUI. - // - // TYPE_IO - // This type of ML also supports asynchronous IO. See also - // MessageLoopForIO. - // - // TYPE_JAVA - // This type of ML is backed by a Java message handler which is responsible - // for running the tasks added to the ML. This is only for use on Android. - // TYPE_JAVA behaves in essence like TYPE_UI, except during construction - // where it does not use the main thread specific pump factory. - // - // TYPE_CUSTOM - // MessagePump was supplied to constructor. - // - enum Type { - TYPE_DEFAULT, - TYPE_UI, - TYPE_CUSTOM, - TYPE_IO, -#if defined(OS_ANDROID) - TYPE_JAVA, -#endif // defined(OS_ANDROID) - }; - - // Normally, it is not necessary to instantiate a MessageLoop. Instead, it - // is typical to make use of the current thread's MessageLoop instance. - explicit MessageLoop(Type type = TYPE_DEFAULT); - // Creates a TYPE_CUSTOM MessageLoop with the supplied MessagePump, which must - // be non-NULL. - explicit MessageLoop(std::unique_ptr pump); - - ~MessageLoop() override; - - // TODO(gab): Mass migrate callers to MessageLoopCurrent::Get(). - static MessageLoopCurrent current(); - - using MessagePumpFactory = std::unique_ptr(); - // Uses the given base::MessagePumpForUIFactory to override the default - // MessagePump implementation for 'TYPE_UI'. Returns true if the factory - // was successfully registered. - static bool InitMessagePumpForUIFactory(MessagePumpFactory* factory); - - // Creates the default MessagePump based on |type|. Caller owns return - // value. - static std::unique_ptr CreateMessagePumpForType(Type type); - - // 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; - - // Returns the type passed to the constructor. - Type type() const { return type_; } - - // Returns the name of the thread this message loop is bound to. This function - // is only valid when this message loop is running, BindToCurrentThread has - // already been called and has an "happens-before" relationship with this call - // (this relationship is obtained implicitly by the MessageLoop's task posting - // system unless calling this very early). - std::string GetThreadName() const; - - // Gets the TaskRunner associated with this message loop. - const scoped_refptr& task_runner() const { - return task_runner_; - } - - // Sets a new TaskRunner for this message loop. The message loop must already - // have been bound to a thread prior to this call, and the task runner must - // belong to that thread. Note that changing the task runner will also affect - // the ThreadTaskRunnerHandle for the target thread. Must be called on the - // thread to which the message loop is bound. - void SetTaskRunner(scoped_refptr task_runner); - - // Clears task_runner() and the ThreadTaskRunnerHandle for the target thread. - // Must be called on the thread to which the message loop is bound. - void ClearTaskRunnerForTesting(); - - // TODO(https://crbug.com/825327): Remove users of TaskObservers through - // MessageLoop::current() and migrate the type back here. - using TaskObserver = MessageLoopCurrent::TaskObserver; - - // These functions can only be called on the same thread that |this| is - // running on. - void AddTaskObserver(TaskObserver* task_observer); - void RemoveTaskObserver(TaskObserver* task_observer); - - // Returns true if the message loop is idle (ignoring delayed tasks). This is - // the same condition which triggers DoWork() to return false: i.e. - // out of tasks which can be processed at the current run-level -- there might - // be deferred non-nestable tasks remaining if currently in a nested run - // level. - bool IsIdleForTesting(); - - // Runs the specified PendingTask. - void RunTask(PendingTask* pending_task); - - //---------------------------------------------------------------------------- - protected: - std::unique_ptr pump_; - - using MessagePumpFactoryCallback = - OnceCallback()>; - - // Common protected constructor. Other constructors delegate the - // initialization to this constructor. - // A subclass can invoke this constructor to create a message_loop of a - // specific type with a custom loop. The implementation does not call - // BindToCurrentThread. If this constructor is invoked directly by a subclass, - // then the subclass must subsequently bind the message loop. - MessageLoop(Type type, MessagePumpFactoryCallback pump_factory); - - // Configure various members and bind this message loop to the current thread. - void BindToCurrentThread(); - - private: - friend class internal::IncomingTaskQueue; - friend class MessageLoopCurrent; - friend class MessageLoopCurrentForIO; - friend class MessageLoopCurrentForUI; - friend class ScheduleWorkTest; - friend class Thread; - FRIEND_TEST_ALL_PREFIXES(MessageLoopTest, DeleteUnboundLoop); - - class Controller; - - // Creates a MessageLoop without binding to a thread. - // If |type| is TYPE_CUSTOM non-null |pump_factory| must be also given - // to create a message pump for this message loop. Otherwise a default - // message pump for the |type| is created. - // - // It is valid to call this to create a new message loop on one thread, - // and then pass it to the thread where the message loop actually runs. - // The message loop's BindToCurrentThread() method must be called on the - // thread the message loop runs on, before calling Run(). - // Before BindToCurrentThread() is called, only Post*Task() functions can - // be called on the message loop. - static std::unique_ptr CreateUnbound( - Type type, - MessagePumpFactoryCallback pump_factory); - - // Sets the ThreadTaskRunnerHandle for the current thread to point to the - // task runner for this message loop. - void SetThreadTaskRunnerHandle(); - - // RunLoop::Delegate: - void Run(bool application_tasks_allowed) override; - void Quit() override; - void EnsureWorkScheduled() override; - - // Called to process any delayed non-nestable tasks. - bool ProcessNextDelayedNonNestableTask(); - - // Calls RunTask or queues the pending_task on the deferred task list if it - // cannot be run right now. Returns true if the task was run. - bool DeferOrRunPendingTask(PendingTask pending_task); - - // Delete tasks that haven't run yet without running them. Used in the - // destructor to make sure all the task's destructors get called. - void DeletePendingTasks(); - - // Wakes up the message pump. Can be called on any thread. The caller is - // responsible for synchronizing ScheduleWork() calls. - void ScheduleWork(); - - // MessagePump::Delegate methods: - bool DoWork() override; - bool DoDelayedWork(TimeTicks* next_delayed_work_time) override; - bool DoIdleWork() override; - - const Type type_; - -#if defined(OS_WIN) - // Tracks if this MessageLoop has requested system-wide high resolution timers - // for its sleep period. Used to downvote that request when the sleep is over. - bool in_high_res_mode_ = false; -#endif - - // A recent snapshot of Time::Now(), used to check delayed_work_queue_. - TimeTicks recent_time_; - - // Non-null when the last thing this MessageLoop did is become idle with - // pending delayed tasks. Used to report metrics on the following wake up. - struct ScheduledWakeup { - // The scheduled time of the next delayed task when this loop became idle. - TimeTicks next_run_time; - // The delta until |next_run_time| when this loop became idle. - TimeDelta intended_sleep; - } scheduled_wakeup_; - - ObserverList destruction_observers_; - - // A boolean which prevents unintentional reentrant task execution (e.g. from - // induced nested message loops). As such, nested message loops will only - // process system messages (not application tasks) by default. A nested loop - // layer must have been explicitly granted permission to be able to execute - // application tasks. This is granted either by - // RunLoop::Type::kNestableTasksAllowed when the loop is driven by the - // application or by a ScopedNestableTaskAllower preceding a system call that - // is known to generate a system-driven nested loop. - bool task_execution_allowed_ = true; - - // pump_factory_.Run() is called to create a message pump for this loop - // if type_ is TYPE_CUSTOM and pump_ is null. - MessagePumpFactoryCallback pump_factory_; - - ObserverList task_observers_; - - // Pointer to this MessageLoop's Controller, valid until the reference to - // |incoming_task_queue_| is dropped below. - Controller* const message_loop_controller_; - scoped_refptr incoming_task_queue_; - - // A task runner which we haven't bound to a thread yet. - scoped_refptr unbound_task_runner_; - - // The task runner associated with this message loop. - scoped_refptr task_runner_; - std::unique_ptr thread_task_runner_handle_; - - // Id of the thread this message loop is bound to. Initialized once when the - // MessageLoop is bound to its thread and constant forever after. - PlatformThreadId thread_id_ = kInvalidThreadId; - - // Holds data stored through the SequenceLocalStorageSlot API. - internal::SequenceLocalStorageMap sequence_local_storage_map_; - - // Enables the SequenceLocalStorageSlot API within its scope. - // Instantiated in BindToCurrentThread(). - std::unique_ptr - scoped_set_sequence_local_storage_map_for_current_thread_; - - // Verifies that calls are made on the thread on which BindToCurrentThread() - // was invoked. - THREAD_CHECKER(bound_thread_checker_); - - DISALLOW_COPY_AND_ASSIGN(MessageLoop); -}; - -#if !defined(OS_NACL) - -//----------------------------------------------------------------------------- -// MessageLoopForUI extends MessageLoop with methods that are particular to a -// MessageLoop instantiated with TYPE_UI. -// -// By instantiating a MessageLoopForUI on the current thread, the owner enables -// native UI message pumping. -// -// MessageLoopCurrentForUI is exposed statically on its thread via -// MessageLoopCurrentForUI::Get() to provide additional functionality. -// -class BASE_EXPORT MessageLoopForUI : public MessageLoop { - public: - explicit MessageLoopForUI(Type type = TYPE_UI); - - // TODO(gab): Mass migrate callers to MessageLoopCurrentForUI::Get()/IsSet(). - static MessageLoopCurrentForUI current(); - static bool IsCurrent(); - -#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 - // PostTask() to work. - void Attach(); -#endif - -#if defined(OS_ANDROID) - // On Android there are cases where we want to abort immediately without - // calling Quit(), in these cases we call Abort(). - void Abort(); - - // True if this message pump has been aborted. - bool IsAborted(); - - // Since Run() is never called on Android, and the message loop is run by the - // java Looper, quitting the RunLoop won't join the thread, so we need a - // callback to run when the RunLoop goes idle to let the Java thread know when - // it can safely quit. - void QuitWhenIdle(base::OnceClosure callback); -#endif - -#if defined(OS_WIN) - // See method of the same name in the Windows MessagePumpForUI implementation. - void EnableWmQuit(); -#endif -}; - -// Do not add any member variables to MessageLoopForUI! This is important b/c -// MessageLoopForUI is often allocated via MessageLoop(TYPE_UI). Any extra -// data that you need should be stored on the MessageLoop's pump_ instance. -static_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. -// -// By instantiating a MessageLoopForIO on the current thread, the owner enables -// native async IO message pumping. -// -// MessageLoopCurrentForIO is exposed statically on its thread via -// MessageLoopCurrentForIO::Get() to provide additional functionality. -// -class BASE_EXPORT MessageLoopForIO : public MessageLoop { - public: - MessageLoopForIO() : MessageLoop(TYPE_IO) {} - - // TODO(gab): Mass migrate callers to MessageLoopCurrentForIO::Get()/IsSet(). - static MessageLoopCurrentForIO current(); - static bool IsCurrent(); -}; - -// Do not add any member variables to MessageLoopForIO! This is important b/c -// MessageLoopForIO is often allocated via MessageLoop(TYPE_IO). Any extra -// data that you need should be stored on the MessageLoop's pump_ instance. -static_assert(sizeof(MessageLoop) == sizeof(MessageLoopForIO), - "MessageLoopForIO should not have extra member variables"); - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_H_ diff --git a/message_loop/message_loop_current.cc b/message_loop/message_loop_current.cc deleted file mode 100644 index 4959b70e0..000000000 --- a/message_loop/message_loop_current.cc +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/message_loop/message_loop_current.h" - -#include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_pump_for_io.h" -#include "base/message_loop/message_pump_for_ui.h" -#include "base/no_destructor.h" -#include "base/threading/thread_local.h" - -namespace base { - -namespace { - -base::ThreadLocalPointer* GetTLSMessageLoop() { - static NoDestructor> lazy_tls_ptr; - return lazy_tls_ptr.get(); -} - -} // namespace - -//------------------------------------------------------------------------------ -// MessageLoopCurrent - -// static -MessageLoopCurrent MessageLoopCurrent::Get() { - return MessageLoopCurrent(GetTLSMessageLoop()->Get()); -} - -// static -bool MessageLoopCurrent::IsSet() { - return !!GetTLSMessageLoop()->Get(); -} - -void MessageLoopCurrent::AddDestructionObserver( - DestructionObserver* destruction_observer) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - current_->destruction_observers_.AddObserver(destruction_observer); -} - -void MessageLoopCurrent::RemoveDestructionObserver( - DestructionObserver* destruction_observer) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - current_->destruction_observers_.RemoveObserver(destruction_observer); -} - -const scoped_refptr& MessageLoopCurrent::task_runner() - const { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - return current_->task_runner(); -} - -void MessageLoopCurrent::SetTaskRunner( - scoped_refptr task_runner) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - current_->SetTaskRunner(std::move(task_runner)); -} - -bool MessageLoopCurrent::IsIdleForTesting() { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - return current_->IsIdleForTesting(); -} - -void MessageLoopCurrent::AddTaskObserver(TaskObserver* task_observer) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - current_->AddTaskObserver(task_observer); -} - -void MessageLoopCurrent::RemoveTaskObserver(TaskObserver* task_observer) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - current_->RemoveTaskObserver(task_observer); -} - -void MessageLoopCurrent::SetNestableTasksAllowed(bool allowed) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - if (allowed) { - // Kick the native pump just in case we enter a OS-driven nested message - // loop that does not go through RunLoop::Run(). - current_->pump_->ScheduleWork(); - } - current_->task_execution_allowed_ = allowed; -} - -bool MessageLoopCurrent::NestableTasksAllowed() const { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - return current_->task_execution_allowed_; -} - -MessageLoopCurrent::ScopedNestableTaskAllower::ScopedNestableTaskAllower() - : loop_(GetTLSMessageLoop()->Get()), - old_state_(loop_->NestableTasksAllowed()) { - loop_->SetNestableTasksAllowed(true); -} - -MessageLoopCurrent::ScopedNestableTaskAllower::~ScopedNestableTaskAllower() { - loop_->SetNestableTasksAllowed(old_state_); -} - -// static -void MessageLoopCurrent::BindToCurrentThreadInternal(MessageLoop* current) { - DCHECK(!GetTLSMessageLoop()->Get()) - << "Can't register a second MessageLoop on the same thread."; - GetTLSMessageLoop()->Set(current); -} - -// static -void MessageLoopCurrent::UnbindFromCurrentThreadInternal(MessageLoop* current) { - DCHECK_EQ(current, GetTLSMessageLoop()->Get()); - GetTLSMessageLoop()->Set(nullptr); -} - -bool MessageLoopCurrent::IsBoundToCurrentThreadInternal( - MessageLoop* message_loop) { - return GetTLSMessageLoop()->Get() == message_loop; -} - -#if !defined(OS_NACL) - -//------------------------------------------------------------------------------ -// MessageLoopCurrentForUI - -// static -MessageLoopCurrentForUI MessageLoopCurrentForUI::Get() { - MessageLoop* loop = GetTLSMessageLoop()->Get(); - DCHECK(loop); -#if defined(OS_ANDROID) - DCHECK(loop->IsType(MessageLoop::TYPE_UI) || - loop->IsType(MessageLoop::TYPE_JAVA)); -#else // defined(OS_ANDROID) - DCHECK(loop->IsType(MessageLoop::TYPE_UI)); -#endif // defined(OS_ANDROID) - auto* loop_for_ui = static_cast(loop); - return MessageLoopCurrentForUI( - loop_for_ui, static_cast(loop_for_ui->pump_.get())); -} - -// static -bool MessageLoopCurrentForUI::IsSet() { - MessageLoop* loop = GetTLSMessageLoop()->Get(); - return loop && -#if defined(OS_ANDROID) - (loop->IsType(MessageLoop::TYPE_UI) || - loop->IsType(MessageLoop::TYPE_JAVA)); -#else // defined(OS_ANDROID) - loop->IsType(MessageLoop::TYPE_UI); -#endif // defined(OS_ANDROID) -} - -#if defined(USE_OZONE) && !defined(OS_FUCHSIA) && !defined(OS_WIN) -bool MessageLoopCurrentForUI::WatchFileDescriptor( - int fd, - bool persistent, - MessagePumpForUI::Mode mode, - MessagePumpForUI::FdWatchController* controller, - MessagePumpForUI::FdWatcher* delegate) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - return pump_->WatchFileDescriptor(fd, persistent, mode, controller, delegate); -} -#endif - -#if defined(OS_IOS) -void MessageLoopCurrentForUI::Attach() { - static_cast(current_)->Attach(); -} -#endif // defined(OS_IOS) - -#if defined(OS_ANDROID) -void MessageLoopCurrentForUI::Abort() { - static_cast(current_)->Abort(); -} -#endif // defined(OS_ANDROID) - -#endif // !defined(OS_NACL) - -//------------------------------------------------------------------------------ -// MessageLoopCurrentForIO - -// static -MessageLoopCurrentForIO MessageLoopCurrentForIO::Get() { - MessageLoop* loop = GetTLSMessageLoop()->Get(); - DCHECK(loop); - DCHECK_EQ(MessageLoop::TYPE_IO, loop->type()); - auto* loop_for_io = static_cast(loop); - return MessageLoopCurrentForIO( - loop_for_io, static_cast(loop_for_io->pump_.get())); -} - -// static -bool MessageLoopCurrentForIO::IsSet() { - MessageLoop* loop = GetTLSMessageLoop()->Get(); - return loop && loop->IsType(MessageLoop::TYPE_IO); -} - -#if !defined(OS_NACL_SFI) - -#if defined(OS_WIN) -HRESULT MessageLoopCurrentForIO::RegisterIOHandler( - HANDLE file, - MessagePumpForIO::IOHandler* handler) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - return pump_->RegisterIOHandler(file, handler); -} - -bool MessageLoopCurrentForIO::RegisterJobObject( - HANDLE job, - MessagePumpForIO::IOHandler* handler) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - return pump_->RegisterJobObject(job, handler); -} - -bool MessageLoopCurrentForIO::WaitForIOCompletion( - DWORD timeout, - MessagePumpForIO::IOHandler* filter) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - return pump_->WaitForIOCompletion(timeout, filter); -} -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -bool MessageLoopCurrentForIO::WatchFileDescriptor( - int fd, - bool persistent, - MessagePumpForIO::Mode mode, - MessagePumpForIO::FdWatchController* controller, - MessagePumpForIO::FdWatcher* delegate) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - return pump_->WatchFileDescriptor(fd, persistent, mode, controller, delegate); -} -#endif // defined(OS_WIN) - -#endif // !defined(OS_NACL_SFI) - -#if defined(OS_FUCHSIA) -// Additional watch API for native platform resources. -bool MessageLoopCurrentForIO::WatchZxHandle( - zx_handle_t handle, - bool persistent, - zx_signals_t signals, - MessagePumpForIO::ZxHandleWatchController* controller, - MessagePumpForIO::ZxHandleWatcher* delegate) { - DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); - return pump_->WatchZxHandle(handle, persistent, signals, controller, - delegate); -} -#endif - -} // namespace base diff --git a/message_loop/message_loop_current.h b/message_loop/message_loop_current.h deleted file mode 100644 index 61d1607e3..000000000 --- a/message_loop/message_loop_current.h +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_CURRENT_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_CURRENT_H_ - -#include "base/base_export.h" -#include "base/logging.h" -#include "base/memory/scoped_refptr.h" -#include "base/message_loop/message_pump_for_io.h" -#include "base/message_loop/message_pump_for_ui.h" -#include "base/pending_task.h" -#include "base/single_thread_task_runner.h" -#include "build/build_config.h" - -namespace base { - -class MessageLoop; - -// MessageLoopCurrent is a proxy to the public interface of the MessageLoop -// bound to the thread it's obtained on. -// -// MessageLoopCurrent(ForUI|ForIO) is available statically through -// MessageLoopCurrent(ForUI|ForIO)::Get() on threads that have a matching -// MessageLoop instance. APIs intended for all consumers on the thread should be -// on MessageLoopCurrent(ForUI|ForIO), while APIs intended for the owner of the -// instance should be on MessageLoop(ForUI|ForIO). -// -// Why: Historically MessageLoop::current() gave access to the full MessageLoop -// API, preventing both addition of powerful owner-only APIs as well as making -// it harder to remove callers of deprecated APIs (that need to stick around for -// a few owner-only use cases and re-accrue callers after cleanup per remaining -// publicly available). -// -// As such, many methods below are flagged as deprecated and should be removed -// (or moved back to MessageLoop) once all static callers have been migrated. -class BASE_EXPORT MessageLoopCurrent { - public: - // MessageLoopCurrent is effectively just a disguised pointer and is fine to - // copy around. - MessageLoopCurrent(const MessageLoopCurrent& other) = default; - MessageLoopCurrent& operator=(const MessageLoopCurrent& other) = default; - - // Returns a proxy object to interact with the MessageLoop running the - // current thread. It must only be used on the thread it was obtained. - static MessageLoopCurrent Get(); - - // Returns true if the current thread is running a MessageLoop. Prefer this to - // verifying the boolean value of Get() (so that Get() can ultimately DCHECK - // it's only invoked when IsSet()). - static bool IsSet(); - - // Allow MessageLoopCurrent to be used like a pointer to support the many - // callsites that used MessageLoop::current() that way when it was a - // MessageLoop*. - MessageLoopCurrent* operator->() { return this; } - explicit operator bool() const { return !!current_; } - - // TODO(gab): Migrate the types of variables that store MessageLoop::current() - // and remove this implicit cast back to MessageLoop*. - operator MessageLoop*() const { return current_; } - - // 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 - // do final cleanup that depends on the MessageLoop. - // - // NOTE: Any tasks posted to the MessageLoop during this notification will - // not be run. Instead, they will be deleted. - // - // Deprecation note: Prefer SequenceLocalStorageSlot> to - // DestructionObserver to bind an object's lifetime to the current - // thread/sequence. - class BASE_EXPORT DestructionObserver { - public: - virtual void WillDestroyCurrentMessageLoop() = 0; - - protected: - virtual ~DestructionObserver() = default; - }; - - // Add a DestructionObserver, which will start receiving notifications - // immediately. - void AddDestructionObserver(DestructionObserver* destruction_observer); - - // Remove a DestructionObserver. It is safe to call this method while a - // DestructionObserver is receiving a notification callback. - void RemoveDestructionObserver(DestructionObserver* destruction_observer); - - // Forwards to MessageLoop::task_runner(). - // DEPRECATED(https://crbug.com/616447): Use ThreadTaskRunnerHandle::Get() - // instead of MessageLoopCurrent::Get()->task_runner(). - const scoped_refptr& task_runner() const; - - // Forwards to MessageLoop::SetTaskRunner(). - // DEPRECATED(https://crbug.com/825327): only owners of the MessageLoop - // instance should replace its TaskRunner. - void SetTaskRunner(scoped_refptr task_runner); - - // A TaskObserver is an object that receives task notifications from the - // MessageLoop. - // - // NOTE: A TaskObserver implementation should be extremely fast! - class BASE_EXPORT TaskObserver { - public: - // This method is called before processing a task. - virtual void WillProcessTask(const PendingTask& pending_task) = 0; - - // This method is called after processing a task. - virtual void DidProcessTask(const PendingTask& pending_task) = 0; - - protected: - virtual ~TaskObserver() = default; - }; - - // Forwards to MessageLoop::(Add|Remove)TaskObserver. - // DEPRECATED(https://crbug.com/825327): only owners of the MessageLoop - // instance should add task observers on it. - void AddTaskObserver(TaskObserver* task_observer); - void RemoveTaskObserver(TaskObserver* task_observer); - - // Enables or disables the recursive task processing. This happens in the case - // of recursive message loops. Some unwanted message loops may occur when - // using common controls or printer functions. By default, recursive task - // processing is disabled. - // - // Please use |ScopedNestableTaskAllower| instead of calling these methods - // directly. In general, nestable message loops are to be avoided. They are - // dangerous and difficult to get right, so please use with extreme caution. - // - // The specific case where tasks get queued is: - // - The thread is running a message loop. - // - It receives a task #1 and executes it. - // - The task #1 implicitly starts a message loop, like a MessageBox in the - // unit test. This can also be StartDoc or GetSaveFileName. - // - The thread receives a task #2 before or while in this second message - // loop. - // - With NestableTasksAllowed set to true, the task #2 will run right away. - // Otherwise, it will get executed right after task #1 completes at "thread - // message loop level". - // - // DEPRECATED(https://crbug.com/750779): Use RunLoop::Type on the relevant - // RunLoop instead of these methods. - // TODO(gab): Migrate usage and delete these methods. - void SetNestableTasksAllowed(bool allowed); - bool NestableTasksAllowed() const; - - // Enables nestable tasks on the current MessageLoop while in scope. - // DEPRECATED(https://crbug.com/750779): This should not be used when the - // nested loop is driven by RunLoop (use RunLoop::Type::kNestableTasksAllowed - // instead). It can however still be useful in a few scenarios where re- - // entrancy is caused by a native message loop. - // TODO(gab): Remove usage of this class alongside RunLoop and rename it to - // ScopedApplicationTasksAllowedInNativeNestedLoop(?) for remaining use cases. - class BASE_EXPORT ScopedNestableTaskAllower { - public: - ScopedNestableTaskAllower(); - ~ScopedNestableTaskAllower(); - - private: - MessageLoop* const loop_; - const bool old_state_; - }; - - // Returns true if the message loop is idle (ignoring delayed tasks). This is - // the same condition which triggers DoWork() to return false: i.e. - // out of tasks which can be processed at the current run-level -- there might - // be deferred non-nestable tasks remaining if currently in a nested run - // level. - bool IsIdleForTesting(); - - // Binds |current| to the current thread. It will from then on be the - // MessageLoop driven by MessageLoopCurrent on this thread. This is only meant - // to be invoked by the MessageLoop itself. - static void BindToCurrentThreadInternal(MessageLoop* current); - - // Unbinds |current| from the current thread. Must be invoked on the same - // thread that invoked |BindToCurrentThreadInternal(current)|. This is only - // meant to be invoked by the MessageLoop itself. - static void UnbindFromCurrentThreadInternal(MessageLoop* current); - - // Returns true if |message_loop| is bound to MessageLoopCurrent on the - // current thread. This is only meant to be invoked by the MessageLoop itself. - static bool IsBoundToCurrentThreadInternal(MessageLoop* message_loop); - - protected: - explicit MessageLoopCurrent(MessageLoop* current) : current_(current) {} - - MessageLoop* const current_; -}; - -#if !defined(OS_NACL) - -// ForUI extension of MessageLoopCurrent. -class BASE_EXPORT MessageLoopCurrentForUI : public MessageLoopCurrent { - public: - // Returns an interface for the MessageLoopForUI of the current thread. - // Asserts that IsSet(). - static MessageLoopCurrentForUI Get(); - - // Returns true if the current thread is running a MessageLoopForUI. - static bool IsSet(); - - MessageLoopCurrentForUI* operator->() { return this; } - -#if defined(USE_OZONE) && !defined(OS_FUCHSIA) && !defined(OS_WIN) - // Please see MessagePumpLibevent for definition. - static_assert(std::is_same::value, - "MessageLoopCurrentForUI::WatchFileDescriptor is not supported " - "when MessagePumpForUI is not a MessagePumpLibevent."); - bool WatchFileDescriptor(int fd, - bool persistent, - MessagePumpForUI::Mode mode, - MessagePumpForUI::FdWatchController* controller, - MessagePumpForUI::FdWatcher* delegate); -#endif - -#if defined(OS_IOS) - // Forwards to MessageLoopForUI::Attach(). - // TODO(https://crbug.com/825327): Plumb the actual MessageLoopForUI* to - // callers and remove ability to access this method from - // MessageLoopCurrentForUI. - void Attach(); -#endif - -#if defined(OS_ANDROID) - // Forwards to MessageLoopForUI::Abort(). - // TODO(https://crbug.com/825327): Plumb the actual MessageLoopForUI* to - // callers and remove ability to access this method from - // MessageLoopCurrentForUI. - void Abort(); -#endif - - private: - MessageLoopCurrentForUI(MessageLoop* current, MessagePumpForUI* pump) - : MessageLoopCurrent(current), pump_(pump) { - DCHECK(pump_); - } - - MessagePumpForUI* const pump_; -}; - -#endif // !defined(OS_NACL) - -// ForIO extension of MessageLoopCurrent. -class BASE_EXPORT MessageLoopCurrentForIO : public MessageLoopCurrent { - public: - // Returns an interface for the MessageLoopForIO of the current thread. - // Asserts that IsSet(). - static MessageLoopCurrentForIO Get(); - - // Returns true if the current thread is running a MessageLoopForIO. - static bool IsSet(); - - MessageLoopCurrentForIO* operator->() { return this; } - -#if !defined(OS_NACL_SFI) - -#if defined(OS_WIN) - // Please see MessagePumpWin for definitions of these methods. - HRESULT RegisterIOHandler(HANDLE file, MessagePumpForIO::IOHandler* handler); - bool RegisterJobObject(HANDLE job, MessagePumpForIO::IOHandler* handler); - bool WaitForIOCompletion(DWORD timeout, MessagePumpForIO::IOHandler* filter); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // Please see WatchableIOMessagePumpPosix for definition. - // Prefer base::FileDescriptorWatcher for non-critical IO. - bool WatchFileDescriptor(int fd, - bool persistent, - MessagePumpForIO::Mode mode, - MessagePumpForIO::FdWatchController* controller, - MessagePumpForIO::FdWatcher* delegate); -#endif // defined(OS_WIN) - -#if defined(OS_FUCHSIA) - // Additional watch API for native platform resources. - bool WatchZxHandle(zx_handle_t handle, - bool persistent, - zx_signals_t signals, - MessagePumpForIO::ZxHandleWatchController* controller, - MessagePumpForIO::ZxHandleWatcher* delegate); -#endif // defined(OS_FUCHSIA) - -#endif // !defined(OS_NACL_SFI) - - private: - MessageLoopCurrentForIO(MessageLoop* current, MessagePumpForIO* pump) - : MessageLoopCurrent(current), pump_(pump) { - DCHECK(pump_); - } - - MessagePumpForIO* const pump_; -}; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_CURRENT_H_ diff --git a/message_loop/message_loop_io_posix_unittest.cc b/message_loop/message_loop_io_posix_unittest.cc deleted file mode 100644 index 4dd5f2802..000000000 --- a/message_loop/message_loop_io_posix_unittest.cc +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/message_loop/message_loop.h" - -#include "base/bind.h" -#include "base/compiler_specific.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop_current.h" -#include "base/message_loop/message_pump_for_io.h" -#include "base/posix/eintr_wrapper.h" -#include "base/run_loop.h" -#include "base/test/gtest_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -#if !defined(OS_NACL) - -namespace { - -class MessageLoopForIoPosixTest : public testing::Test { - public: - MessageLoopForIoPosixTest() = default; - - // testing::Test interface. - void SetUp() override { - // Create a file descriptor. Doesn't need to be readable or writable, - // as we don't need to actually get any notifications. - // pipe() is just the easiest way to do it. - int pipefds[2]; - int err = pipe(pipefds); - ASSERT_EQ(0, err); - read_fd_ = ScopedFD(pipefds[0]); - write_fd_ = ScopedFD(pipefds[1]); - } - - void TriggerReadEvent() { - // Write from the other end of the pipe to trigger the event. - char c = '\0'; - EXPECT_EQ(1, HANDLE_EINTR(write(write_fd_.get(), &c, 1))); - } - - protected: - ScopedFD read_fd_; - ScopedFD write_fd_; - - DISALLOW_COPY_AND_ASSIGN(MessageLoopForIoPosixTest); -}; - -class TestHandler : public MessagePumpForIO::FdWatcher { - public: - void OnFileCanReadWithoutBlocking(int fd) override { - watcher_to_delete_ = nullptr; - is_readable_ = true; - RunLoop::QuitCurrentWhenIdleDeprecated(); - } - void OnFileCanWriteWithoutBlocking(int fd) override { - watcher_to_delete_ = nullptr; - is_writable_ = true; - RunLoop::QuitCurrentWhenIdleDeprecated(); - } - - bool is_readable_ = false; - bool is_writable_ = false; - - // If set then the contained watcher will be deleted on notification. - std::unique_ptr watcher_to_delete_; -}; - -// Watcher that calls specified closures when read/write events occur. Verifies -// that each non-null closure passed to this class is called once and only once. -// Also resets the read event by reading from the FD. -class CallClosureHandler : public MessagePumpForIO::FdWatcher { - public: - CallClosureHandler(OnceClosure read_closure, OnceClosure write_closure) - : read_closure_(std::move(read_closure)), - write_closure_(std::move(write_closure)) {} - - ~CallClosureHandler() override { - EXPECT_TRUE(read_closure_.is_null()); - EXPECT_TRUE(write_closure_.is_null()); - } - - void SetReadClosure(OnceClosure read_closure) { - EXPECT_TRUE(read_closure_.is_null()); - read_closure_ = std::move(read_closure); - } - - void SetWriteClosure(OnceClosure write_closure) { - EXPECT_TRUE(write_closure_.is_null()); - write_closure_ = std::move(write_closure); - } - - // base:MessagePumpFuchsia::Watcher interface. - void OnFileCanReadWithoutBlocking(int fd) override { - // Empty the pipe buffer to reset the event. Otherwise libevent - // implementation of MessageLoop may call the event handler again even if - // |read_closure_| below quits the RunLoop. - char c; - int result = HANDLE_EINTR(read(fd, &c, 1)); - if (result == -1) { - PLOG(ERROR) << "read"; - FAIL(); - } - EXPECT_EQ(result, 1); - - ASSERT_FALSE(read_closure_.is_null()); - std::move(read_closure_).Run(); - } - - void OnFileCanWriteWithoutBlocking(int fd) override { - ASSERT_FALSE(write_closure_.is_null()); - std::move(write_closure_).Run(); - } - - private: - OnceClosure read_closure_; - OnceClosure write_closure_; -}; - -TEST_F(MessageLoopForIoPosixTest, FileDescriptorWatcherOutlivesMessageLoop) { - // Simulate a MessageLoop that dies before an FileDescriptorWatcher. - // This could happen when people use the Singleton pattern or atexit. - - // Arrange for watcher to live longer than message loop. - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - TestHandler handler; - { - MessageLoopForIO message_loop; - - MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - write_fd_.get(), true, MessagePumpForIO::WATCH_WRITE, &watcher, - &handler); - // Don't run the message loop, just destroy it. - } - - ASSERT_FALSE(handler.is_readable_); - ASSERT_FALSE(handler.is_writable_); -} - -TEST_F(MessageLoopForIoPosixTest, FileDescriptorWatcherDoubleStop) { - // Verify that it's ok to call StopWatchingFileDescriptor(). - - // Arrange for message loop to live longer than watcher. - MessageLoopForIO message_loop; - { - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - - TestHandler handler; - MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - write_fd_.get(), true, MessagePumpForIO::WATCH_WRITE, &watcher, - &handler); - ASSERT_TRUE(watcher.StopWatchingFileDescriptor()); - ASSERT_TRUE(watcher.StopWatchingFileDescriptor()); - } -} - -TEST_F(MessageLoopForIoPosixTest, FileDescriptorWatcherDeleteInCallback) { - // Verify that it is OK to delete the FileDescriptorWatcher from within a - // callback. - MessageLoopForIO message_loop; - - TestHandler handler; - handler.watcher_to_delete_ = - std::make_unique(FROM_HERE); - - MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - write_fd_.get(), true, MessagePumpForIO::WATCH_WRITE, - handler.watcher_to_delete_.get(), &handler); - RunLoop().Run(); -} - -// Verify that basic readable notification works. -TEST_F(MessageLoopForIoPosixTest, WatchReadable) { - MessageLoopForIO message_loop; - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - TestHandler handler; - - // Watch the pipe for readability. - ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ, - &watcher, &handler)); - - // The pipe should not be readable when first created. - RunLoop().RunUntilIdle(); - ASSERT_FALSE(handler.is_readable_); - ASSERT_FALSE(handler.is_writable_); - - TriggerReadEvent(); - - // We don't want to assume that the read fd becomes readable the - // instant a bytes is written, so Run until quit by an event. - RunLoop().Run(); - - ASSERT_TRUE(handler.is_readable_); - ASSERT_FALSE(handler.is_writable_); -} - -// Verify that watching a file descriptor for writability succeeds. -TEST_F(MessageLoopForIoPosixTest, WatchWritable) { - MessageLoopForIO message_loop; - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - TestHandler handler; - - // Watch the pipe for writability. - ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - write_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_WRITE, - &watcher, &handler)); - - // We should not receive a writable notification until we process events. - ASSERT_FALSE(handler.is_readable_); - ASSERT_FALSE(handler.is_writable_); - - // The pipe should be writable immediately, but wait for the quit closure - // anyway, to be sure. - RunLoop().Run(); - - ASSERT_FALSE(handler.is_readable_); - ASSERT_TRUE(handler.is_writable_); -} - -// Verify that RunUntilIdle() receives IO notifications. -TEST_F(MessageLoopForIoPosixTest, RunUntilIdle) { - MessageLoopForIO message_loop; - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - TestHandler handler; - - // Watch the pipe for readability. - ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ, - &watcher, &handler)); - - // The pipe should not be readable when first created. - RunLoop().RunUntilIdle(); - ASSERT_FALSE(handler.is_readable_); - - TriggerReadEvent(); - - while (!handler.is_readable_) - RunLoop().RunUntilIdle(); -} - -void StopWatching(MessagePumpForIO::FdWatchController* controller, - RunLoop* run_loop) { - controller->StopWatchingFileDescriptor(); - run_loop->Quit(); -} - -// Verify that StopWatchingFileDescriptor() works from an event handler. -TEST_F(MessageLoopForIoPosixTest, StopFromHandler) { - MessageLoopForIO message_loop; - RunLoop run_loop; - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - CallClosureHandler handler(BindOnce(&StopWatching, &watcher, &run_loop), - OnceClosure()); - - // Create persistent watcher. - ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - read_fd_.get(), /*persistent=*/true, MessagePumpForIO::WATCH_READ, - &watcher, &handler)); - - TriggerReadEvent(); - run_loop.Run(); - - // Trigger the event again. The event handler should not be called again. - TriggerReadEvent(); - RunLoop().RunUntilIdle(); -} - -// Verify that non-persistent watcher is called only once. -TEST_F(MessageLoopForIoPosixTest, NonPersistentWatcher) { - MessageLoopForIO message_loop; - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - - RunLoop run_loop; - CallClosureHandler handler(run_loop.QuitClosure(), OnceClosure()); - - // Create a non-persistent watcher. - ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ, - &watcher, &handler)); - - TriggerReadEvent(); - run_loop.Run(); - - // Trigger the event again. handler should not be called again. - TriggerReadEvent(); - RunLoop().RunUntilIdle(); -} - -// Verify that persistent watcher is called every time the event is triggered. -TEST_F(MessageLoopForIoPosixTest, PersistentWatcher) { - MessageLoopForIO message_loop; - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - - RunLoop run_loop1; - CallClosureHandler handler(run_loop1.QuitClosure(), OnceClosure()); - - // Create persistent watcher. - ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - read_fd_.get(), /*persistent=*/true, MessagePumpForIO::WATCH_READ, - &watcher, &handler)); - - TriggerReadEvent(); - run_loop1.Run(); - - RunLoop run_loop2; - handler.SetReadClosure(run_loop2.QuitClosure()); - - // Trigger the event again. handler should be called now, which will quit - // run_loop2. - TriggerReadEvent(); - run_loop2.Run(); -} - -void StopWatchingAndWatchAgain(MessagePumpForIO::FdWatchController* controller, - int fd, - MessagePumpForIO::FdWatcher* new_handler, - RunLoop* run_loop) { - controller->StopWatchingFileDescriptor(); - - ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - fd, /*persistent=*/true, MessagePumpForIO::WATCH_READ, controller, - new_handler)); - - run_loop->Quit(); -} - -// Verify that a watcher can be stopped and reused from an event handler. -TEST_F(MessageLoopForIoPosixTest, StopAndRestartFromHandler) { - MessageLoopForIO message_loop; - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - - RunLoop run_loop1; - RunLoop run_loop2; - CallClosureHandler handler2(run_loop2.QuitClosure(), OnceClosure()); - CallClosureHandler handler1(BindOnce(&StopWatchingAndWatchAgain, &watcher, - read_fd_.get(), &handler2, &run_loop1), - OnceClosure()); - - // Create persistent watcher. - ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - read_fd_.get(), /*persistent=*/true, MessagePumpForIO::WATCH_READ, - &watcher, &handler1)); - - TriggerReadEvent(); - run_loop1.Run(); - - // Trigger the event again. handler2 should be called now, which will quit - // run_loop2 - TriggerReadEvent(); - run_loop2.Run(); -} - -// Verify that the pump properly handles a delayed task after an IO event. -TEST_F(MessageLoopForIoPosixTest, IoEventThenTimer) { - MessageLoopForIO message_loop; - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - - RunLoop timer_run_loop; - message_loop.task_runner()->PostDelayedTask( - FROM_HERE, timer_run_loop.QuitClosure(), - base::TimeDelta::FromMilliseconds(10)); - - RunLoop watcher_run_loop; - CallClosureHandler handler(watcher_run_loop.QuitClosure(), OnceClosure()); - - // Create a non-persistent watcher. - ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ, - &watcher, &handler)); - - TriggerReadEvent(); - - // Normally the IO event will be received before the delayed task is - // executed, so this run loop will first handle the IO event and then quit on - // the timer. - timer_run_loop.Run(); - - // Run watcher_run_loop in case the IO event wasn't received before the - // delayed task. - watcher_run_loop.Run(); -} - -// Verify that the pipe can handle an IO event after a delayed task. -TEST_F(MessageLoopForIoPosixTest, TimerThenIoEvent) { - MessageLoopForIO message_loop; - MessagePumpForIO::FdWatchController watcher(FROM_HERE); - - // Trigger read event from a delayed task. - message_loop.task_runner()->PostDelayedTask( - FROM_HERE, - BindOnce(&MessageLoopForIoPosixTest::TriggerReadEvent, Unretained(this)), - TimeDelta::FromMilliseconds(1)); - - RunLoop run_loop; - CallClosureHandler handler(run_loop.QuitClosure(), OnceClosure()); - - // Create a non-persistent watcher. - ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor( - read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ, - &watcher, &handler)); - - run_loop.Run(); -} - -} // namespace - -#endif // !defined(OS_NACL) - -} // namespace base diff --git a/message_loop/message_loop_perftest.cc b/message_loop/message_loop_perftest.cc deleted file mode 100644 index 867e8fe85..000000000 --- a/message_loop/message_loop_perftest.cc +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2017 The Chromium 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 -#include - -#include "base/atomicops.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/atomic_flag.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/platform_thread.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_test.h" - -namespace base { - -namespace { - -// A thread that waits for the caller to signal an event before proceeding to -// call Action::Run(). -class PostingThread { - public: - class Action { - public: - virtual ~Action() = default; - - // Called after the thread is started and |start_event_| is signalled. - virtual void Run() = 0; - - protected: - Action() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(Action); - }; - - // Creates a PostingThread where the thread waits on |start_event| before - // calling action->Run(). If a thread is returned, the thread is guaranteed to - // be allocated and running and the caller must call Join() before destroying - // the PostingThread. - static std::unique_ptr Create(WaitableEvent* start_event, - std::unique_ptr action) { - auto posting_thread = - WrapUnique(new PostingThread(start_event, std::move(action))); - - if (!posting_thread->Start()) - return nullptr; - - return posting_thread; - } - - ~PostingThread() { DCHECK_EQ(!thread_handle_.is_null(), join_called_); } - - void Join() { - PlatformThread::Join(thread_handle_); - join_called_ = true; - } - - private: - class Delegate final : public PlatformThread::Delegate { - public: - Delegate(PostingThread* outer, std::unique_ptr action) - : outer_(outer), action_(std::move(action)) { - DCHECK(outer_); - DCHECK(action_); - } - - ~Delegate() override = default; - - private: - void ThreadMain() override { - outer_->thread_started_.Signal(); - outer_->start_event_->Wait(); - action_->Run(); - } - - PostingThread* const outer_; - const std::unique_ptr action_; - - DISALLOW_COPY_AND_ASSIGN(Delegate); - }; - - PostingThread(WaitableEvent* start_event, std::unique_ptr delegate) - : start_event_(start_event), - thread_started_(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED), - delegate_(this, std::move(delegate)) { - DCHECK(start_event_); - } - - bool Start() { - bool thread_created = - PlatformThread::Create(0, &delegate_, &thread_handle_); - if (thread_created) - thread_started_.Wait(); - - return thread_created; - } - - bool join_called_ = false; - WaitableEvent* const start_event_; - WaitableEvent thread_started_; - Delegate delegate_; - - PlatformThreadHandle thread_handle_; - - DISALLOW_COPY_AND_ASSIGN(PostingThread); -}; - -class MessageLoopPerfTest : public ::testing::TestWithParam { - public: - MessageLoopPerfTest() - : message_loop_task_runner_(SequencedTaskRunnerHandle::Get()), - run_posting_threads_(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED) {} - - static std::string ParamInfoToString( - ::testing::TestParamInfo param_info) { - return PostingThreadCountToString(param_info.param); - } - - static std::string PostingThreadCountToString(int posting_threads) { - // Special case 1 thread for thread vs threads. - if (posting_threads == 1) - return "1_Posting_Thread"; - - return StringPrintf("%d_Posting_Threads", posting_threads); - } - - protected: - class ContinuouslyPostTasks final : public PostingThread::Action { - public: - ContinuouslyPostTasks(MessageLoopPerfTest* outer) : outer_(outer) { - DCHECK(outer_); - } - ~ContinuouslyPostTasks() override = default; - - private: - void Run() override { - RepeatingClosure task_to_run = - BindRepeating([](size_t* num_tasks_run) { ++*num_tasks_run; }, - &outer_->num_tasks_run_); - while (!outer_->stop_posting_threads_.IsSet()) { - outer_->message_loop_task_runner_->PostTask(FROM_HERE, task_to_run); - subtle::NoBarrier_AtomicIncrement(&outer_->num_tasks_posted_, 1); - } - } - - MessageLoopPerfTest* const outer_; - - DISALLOW_COPY_AND_ASSIGN(ContinuouslyPostTasks); - }; - - void SetUp() override { - // This check is here because we can't ASSERT_TRUE in the constructor. - ASSERT_TRUE(message_loop_task_runner_); - } - - // Runs ActionType::Run() on |num_posting_threads| and requests test - // termination around |duration|. - template - void RunTest(const int num_posting_threads, TimeDelta duration) { - std::vector> threads; - for (int i = 0; i < num_posting_threads; ++i) { - threads.emplace_back(PostingThread::Create( - &run_posting_threads_, std::make_unique(this))); - // Don't assert here to simplify the code that requires a Join() call for - // every created PostingThread. - EXPECT_TRUE(threads[i]); - } - - RunLoop run_loop; - message_loop_task_runner_->PostDelayedTask( - FROM_HERE, - BindOnce( - [](RunLoop* run_loop, AtomicFlag* stop_posting_threads) { - stop_posting_threads->Set(); - run_loop->Quit(); - }, - &run_loop, &stop_posting_threads_), - duration); - - TimeTicks post_task_start = TimeTicks::Now(); - run_posting_threads_.Signal(); - - TimeTicks run_loop_start = TimeTicks::Now(); - run_loop.Run(); - tasks_run_duration_ = TimeTicks::Now() - run_loop_start; - - for (auto& thread : threads) - thread->Join(); - - tasks_posted_duration_ = TimeTicks::Now() - post_task_start; - } - - size_t num_tasks_posted() const { - return subtle::NoBarrier_Load(&num_tasks_posted_); - } - - TimeDelta tasks_posted_duration() const { return tasks_posted_duration_; } - - size_t num_tasks_run() const { return num_tasks_run_; } - - TimeDelta tasks_run_duration() const { return tasks_run_duration_; } - - private: - MessageLoop message_loop_; - - // Accessed on multiple threads, thread-safe or constant: - const scoped_refptr message_loop_task_runner_; - WaitableEvent run_posting_threads_; - AtomicFlag stop_posting_threads_; - subtle::AtomicWord num_tasks_posted_ = 0; - - // Accessed only on the test case thread: - TimeDelta tasks_posted_duration_; - TimeDelta tasks_run_duration_; - size_t num_tasks_run_ = 0; - - DISALLOW_COPY_AND_ASSIGN(MessageLoopPerfTest); -}; - -} // namespace - -TEST_P(MessageLoopPerfTest, PostTaskRate) { - // Measures the average rate of posting tasks from different threads and the - // average rate that the message loop is running those tasks. - RunTest(GetParam(), TimeDelta::FromSeconds(3)); - perf_test::PrintResult("task_posting", "", - PostingThreadCountToString(GetParam()), - tasks_posted_duration().InMicroseconds() / - static_cast(num_tasks_posted()), - "us/task", true); - perf_test::PrintResult("task_running", "", - PostingThreadCountToString(GetParam()), - tasks_run_duration().InMicroseconds() / - static_cast(num_tasks_run()), - "us/task", true); -} - -INSTANTIATE_TEST_CASE_P(, - MessageLoopPerfTest, - ::testing::Values(1, 5, 10), - MessageLoopPerfTest::ParamInfoToString); -} // namespace base diff --git a/message_loop/message_loop_task_runner.cc b/message_loop/message_loop_task_runner.cc deleted file mode 100644 index f251e3b8b..000000000 --- a/message_loop/message_loop_task_runner.cc +++ /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. - -#include "base/message_loop/message_loop_task_runner.h" - -#include - -#include "base/location.h" -#include "base/logging.h" -#include "base/message_loop/incoming_task_queue.h" - -namespace base { -namespace internal { - -MessageLoopTaskRunner::MessageLoopTaskRunner( - scoped_refptr incoming_queue) - : incoming_queue_(incoming_queue), valid_thread_id_(kInvalidThreadId) { -} - -void MessageLoopTaskRunner::BindToCurrentThread() { - AutoLock lock(valid_thread_id_lock_); - DCHECK_EQ(kInvalidThreadId, valid_thread_id_); - valid_thread_id_ = PlatformThread::CurrentId(); -} - -bool MessageLoopTaskRunner::PostDelayedTask(const Location& from_here, - OnceClosure task, - base::TimeDelta delay) { - DCHECK(!task.is_null()) << from_here.ToString(); - return incoming_queue_->AddToIncomingQueue(from_here, std::move(task), delay, - Nestable::kNestable); -} - -bool MessageLoopTaskRunner::PostNonNestableDelayedTask( - const Location& from_here, - OnceClosure task, - base::TimeDelta delay) { - DCHECK(!task.is_null()) << from_here.ToString(); - return incoming_queue_->AddToIncomingQueue(from_here, std::move(task), delay, - Nestable::kNonNestable); -} - -bool MessageLoopTaskRunner::RunsTasksInCurrentSequence() const { - AutoLock lock(valid_thread_id_lock_); - return valid_thread_id_ == PlatformThread::CurrentId(); -} - -MessageLoopTaskRunner::~MessageLoopTaskRunner() = default; - -} // namespace internal - -} // namespace base diff --git a/message_loop/message_loop_task_runner.h b/message_loop/message_loop_task_runner.h deleted file mode 100644 index c7d48c28c..000000000 --- a/message_loop/message_loop_task_runner.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_TASK_RUNNER_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_TASK_RUNNER_H_ - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/pending_task.h" -#include "base/single_thread_task_runner.h" -#include "base/synchronization/lock.h" -#include "base/threading/platform_thread.h" - -namespace base { -namespace internal { - -class IncomingTaskQueue; - -// A stock implementation of SingleThreadTaskRunner that is created and managed -// by a MessageLoop. For now a MessageLoopTaskRunner can only be created as -// part of a MessageLoop. -class BASE_EXPORT MessageLoopTaskRunner : public SingleThreadTaskRunner { - public: - explicit MessageLoopTaskRunner( - scoped_refptr incoming_queue); - - // Initialize this message loop task runner on the current thread. - void BindToCurrentThread(); - - // SingleThreadTaskRunner implementation - bool PostDelayedTask(const Location& from_here, - OnceClosure task, - TimeDelta delay) override; - bool PostNonNestableDelayedTask(const Location& from_here, - OnceClosure task, - TimeDelta delay) override; - bool RunsTasksInCurrentSequence() const override; - - private: - friend class RefCountedThreadSafe; - ~MessageLoopTaskRunner() override; - - // The incoming queue receiving all posted tasks. - scoped_refptr incoming_queue_; - - // ID of the thread |this| was created on. Could be accessed on multiple - // threads, protected by |valid_thread_id_lock_|. - PlatformThreadId valid_thread_id_; - mutable Lock valid_thread_id_lock_; - - DISALLOW_COPY_AND_ASSIGN(MessageLoopTaskRunner); -}; - -} // namespace internal -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_TASK_RUNNER_H_ diff --git a/message_loop/message_loop_task_runner_perftest.cc b/message_loop/message_loop_task_runner_perftest.cc deleted file mode 100644 index 3ab9ba2dc..000000000 --- a/message_loop/message_loop_task_runner_perftest.cc +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/message_loop/message_loop_task_runner.h" - -#include -#include - -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/debug/task_annotator.h" -#include "base/macros.h" -#include "base/memory/scoped_refptr.h" -#include "base/message_loop/incoming_task_queue.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_task_runner.h" -#include "base/message_loop/message_pump.h" -#include "base/run_loop.h" -#include "base/strings/stringprintf.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/perf/perf_test.h" - -namespace base { - -namespace { - -// Tests below will post tasks in a loop until |kPostTaskPerfTestDuration| has -// elapsed. -constexpr TimeDelta kPostTaskPerfTestDuration = - base::TimeDelta::FromSeconds(30); - -} // namespace - -class FakeObserver : public internal::IncomingTaskQueue::Observer { - public: - // IncomingTaskQueue::Observer - void WillQueueTask(PendingTask* task) override {} - void DidQueueTask(bool was_empty) override {} - - virtual void RunTask(PendingTask* task) { std::move(task->task).Run(); } -}; - -// Exercises MessageLoopTaskRunner's multi-threaded queue in isolation. -class BasicPostTaskPerfTest : public testing::Test { - public: - void Run(int batch_size, - int tasks_per_reload, - std::unique_ptr task_source_observer) { - base::TimeTicks start = base::TimeTicks::Now(); - base::TimeTicks now; - FakeObserver* task_source_observer_raw = task_source_observer.get(); - scoped_refptr queue( - base::MakeRefCounted( - std::move(task_source_observer))); - scoped_refptr task_runner( - base::MakeRefCounted(queue)); - uint32_t num_posted = 0; - do { - for (int i = 0; i < batch_size; ++i) { - for (int j = 0; j < tasks_per_reload; ++j) { - task_runner->PostTask(FROM_HERE, DoNothing()); - num_posted++; - } - TaskQueue loop_local_queue; - queue->ReloadWorkQueue(&loop_local_queue); - while (!loop_local_queue.empty()) { - PendingTask t = std::move(loop_local_queue.front()); - loop_local_queue.pop(); - task_source_observer_raw->RunTask(&t); - } - } - - now = base::TimeTicks::Now(); - } while (now - start < kPostTaskPerfTestDuration); - std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload); - perf_test::PrintResult( - "task", "", trace, - (now - start).InMicroseconds() / static_cast(num_posted), - "us/task", true); - } -}; - -TEST_F(BasicPostTaskPerfTest, OneTaskPerReload) { - Run(10000, 1, std::make_unique()); -} - -TEST_F(BasicPostTaskPerfTest, TenTasksPerReload) { - Run(10000, 10, std::make_unique()); -} - -TEST_F(BasicPostTaskPerfTest, OneHundredTasksPerReload) { - Run(1000, 100, std::make_unique()); -} - -class StubMessagePump : public MessagePump { - public: - StubMessagePump() = default; - ~StubMessagePump() override = default; - - // MessagePump: - void Run(Delegate* delegate) override {} - void Quit() override {} - void ScheduleWork() override {} - void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override {} -}; - -// Simulates the overhead of hooking TaskAnnotator and ScheduleWork() to the -// post task machinery. -class FakeObserverSimulatingOverhead : public FakeObserver { - public: - FakeObserverSimulatingOverhead() = default; - - // FakeObserver: - void WillQueueTask(PendingTask* task) final { - task_annotator_.WillQueueTask("MessageLoop::PostTask", task); - } - - void DidQueueTask(bool was_empty) final { - AutoLock scoped_lock(message_loop_lock_); - pump_->ScheduleWork(); - } - - void RunTask(PendingTask* task) final { - task_annotator_.RunTask("MessageLoop::PostTask", task); - } - - private: - // Simulates overhead from ScheduleWork() and TaskAnnotator calls involved in - // a real PostTask (stores the StubMessagePump in a pointer to force a virtual - // dispatch for ScheduleWork() and be closer to reality). - Lock message_loop_lock_; - std::unique_ptr pump_{std::make_unique()}; - debug::TaskAnnotator task_annotator_; - - DISALLOW_COPY_AND_ASSIGN(FakeObserverSimulatingOverhead); -}; - -TEST_F(BasicPostTaskPerfTest, OneTaskPerReloadWithOverhead) { - Run(10000, 1, std::make_unique()); -} - -TEST_F(BasicPostTaskPerfTest, TenTasksPerReloadWithOverhead) { - Run(10000, 10, std::make_unique()); -} - -TEST_F(BasicPostTaskPerfTest, OneHundredTasksPerReloadWithOverhead) { - Run(1000, 100, std::make_unique()); -} - -// Exercises the full MessageLoop/RunLoop machinery. -class IntegratedPostTaskPerfTest : public testing::Test { - public: - void Run(int batch_size, int tasks_per_reload) { - base::TimeTicks start = base::TimeTicks::Now(); - base::TimeTicks now; - MessageLoop loop; - uint32_t num_posted = 0; - do { - for (int i = 0; i < batch_size; ++i) { - for (int j = 0; j < tasks_per_reload; ++j) { - loop->task_runner()->PostTask(FROM_HERE, DoNothing()); - num_posted++; - } - RunLoop().RunUntilIdle(); - } - - now = base::TimeTicks::Now(); - } while (now - start < kPostTaskPerfTestDuration); - std::string trace = StringPrintf("%d_tasks_per_reload", tasks_per_reload); - perf_test::PrintResult( - "task", "", trace, - (now - start).InMicroseconds() / static_cast(num_posted), - "us/task", true); - } -}; - -TEST_F(IntegratedPostTaskPerfTest, OneTaskPerReload) { - Run(10000, 1); -} - -TEST_F(IntegratedPostTaskPerfTest, TenTasksPerReload) { - Run(10000, 10); -} - -TEST_F(IntegratedPostTaskPerfTest, OneHundredTasksPerReload) { - Run(1000, 100); -} - -} // namespace base diff --git a/message_loop/message_loop_task_runner_unittest.cc b/message_loop/message_loop_task_runner_unittest.cc deleted file mode 100644 index c7e9aa059..000000000 --- a/message_loop/message_loop_task_runner_unittest.cc +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/message_loop/message_loop_task_runner.h" - -#include - -#include "base/atomic_sequence_num.h" -#include "base/bind.h" -#include "base/debug/leak_annotations.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_task_runner.h" -#include "base/run_loop.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -namespace base { - -class MessageLoopTaskRunnerTest : public testing::Test { - public: - MessageLoopTaskRunnerTest() - : current_loop_(new MessageLoop()), - task_thread_("task_thread"), - thread_sync_(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED) {} - - void DeleteCurrentMessageLoop() { current_loop_.reset(); } - - protected: - void SetUp() override { - // Use SetUp() instead of the constructor to avoid posting a task to a - // partially constructed object. - task_thread_.Start(); - - // Allow us to pause the |task_thread_|'s MessageLoop. - task_thread_.task_runner()->PostTask( - FROM_HERE, BindOnce(&MessageLoopTaskRunnerTest::BlockTaskThreadHelper, - Unretained(this))); - } - - void TearDown() override { - // Make sure the |task_thread_| is not blocked, and stop the thread - // fully before destruction because its tasks may still depend on the - // |thread_sync_| event. - thread_sync_.Signal(); - task_thread_.Stop(); - DeleteCurrentMessageLoop(); - } - - // Make LoopRecorder threadsafe so that there is defined behavior even if a - // threading mistake sneaks into the PostTaskAndReplyRelay implementation. - class LoopRecorder : public RefCountedThreadSafe { - public: - LoopRecorder(MessageLoop** run_on, - MessageLoop** deleted_on, - int* destruct_order) - : run_on_(run_on), - deleted_on_(deleted_on), - destruct_order_(destruct_order) {} - - void RecordRun() { *run_on_ = MessageLoop::current(); } - - private: - friend class RefCountedThreadSafe; - ~LoopRecorder() { - *deleted_on_ = MessageLoop::current(); - *destruct_order_ = g_order.GetNext(); - } - - MessageLoop** run_on_; - MessageLoop** deleted_on_; - int* destruct_order_; - }; - - static void RecordLoop(scoped_refptr recorder) { - recorder->RecordRun(); - } - - static void RecordLoopAndQuit(scoped_refptr recorder) { - recorder->RecordRun(); - RunLoop::QuitCurrentWhenIdleDeprecated(); - } - - void UnblockTaskThread() { thread_sync_.Signal(); } - - void BlockTaskThreadHelper() { thread_sync_.Wait(); } - - static AtomicSequenceNumber g_order; - - std::unique_ptr current_loop_; - Thread task_thread_; - - private: - base::WaitableEvent thread_sync_; -}; - -AtomicSequenceNumber MessageLoopTaskRunnerTest::g_order; - -TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_Basic) { - MessageLoop* task_run_on = nullptr; - MessageLoop* task_deleted_on = nullptr; - int task_delete_order = -1; - MessageLoop* reply_run_on = nullptr; - MessageLoop* reply_deleted_on = nullptr; - int reply_delete_order = -1; - - scoped_refptr task_recorder = - new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order); - scoped_refptr reply_recorder = - new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order); - - ASSERT_TRUE(task_thread_.task_runner()->PostTaskAndReply( - FROM_HERE, BindOnce(&RecordLoop, task_recorder), - BindOnce(&RecordLoopAndQuit, reply_recorder))); - - // Die if base::Bind doesn't retain a reference to the recorders. - task_recorder = nullptr; - reply_recorder = nullptr; - ASSERT_FALSE(task_deleted_on); - ASSERT_FALSE(reply_deleted_on); - - UnblockTaskThread(); - RunLoop().Run(); - - EXPECT_EQ(task_thread_.message_loop(), task_run_on); - EXPECT_EQ(task_thread_.message_loop(), task_deleted_on); - EXPECT_EQ(current_loop_.get(), reply_run_on); - EXPECT_EQ(current_loop_.get(), reply_deleted_on); - EXPECT_LT(task_delete_order, reply_delete_order); -} - -TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReplyOnDeletedThreadDoesNotLeak) { - MessageLoop* task_run_on = nullptr; - MessageLoop* task_deleted_on = nullptr; - int task_delete_order = -1; - MessageLoop* reply_run_on = nullptr; - MessageLoop* reply_deleted_on = nullptr; - int reply_delete_order = -1; - - scoped_refptr task_recorder = - new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order); - scoped_refptr reply_recorder = - new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order); - - // Grab a task runner to a dead MessageLoop. - scoped_refptr task_runner = - task_thread_.task_runner(); - UnblockTaskThread(); - task_thread_.Stop(); - - ASSERT_FALSE(task_runner->PostTaskAndReply( - FROM_HERE, BindOnce(&RecordLoop, task_recorder), - BindOnce(&RecordLoopAndQuit, reply_recorder))); - - // The relay should have properly deleted its resources leaving us as the only - // reference. - EXPECT_EQ(task_delete_order, reply_delete_order); - ASSERT_TRUE(task_recorder->HasOneRef()); - ASSERT_TRUE(reply_recorder->HasOneRef()); - - // Nothing should have run though. - EXPECT_FALSE(task_run_on); - EXPECT_FALSE(reply_run_on); -} - -TEST_F(MessageLoopTaskRunnerTest, PostTaskAndReply_SameLoop) { - MessageLoop* task_run_on = nullptr; - MessageLoop* task_deleted_on = nullptr; - int task_delete_order = -1; - MessageLoop* reply_run_on = nullptr; - MessageLoop* reply_deleted_on = nullptr; - int reply_delete_order = -1; - - scoped_refptr task_recorder = - new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order); - scoped_refptr reply_recorder = - new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order); - - // Enqueue the relay. - ASSERT_TRUE(current_loop_->task_runner()->PostTaskAndReply( - FROM_HERE, BindOnce(&RecordLoop, task_recorder), - BindOnce(&RecordLoopAndQuit, reply_recorder))); - - // Die if base::Bind doesn't retain a reference to the recorders. - task_recorder = nullptr; - reply_recorder = nullptr; - ASSERT_FALSE(task_deleted_on); - ASSERT_FALSE(reply_deleted_on); - - RunLoop().Run(); - - EXPECT_EQ(current_loop_.get(), task_run_on); - EXPECT_EQ(current_loop_.get(), task_deleted_on); - EXPECT_EQ(current_loop_.get(), reply_run_on); - EXPECT_EQ(current_loop_.get(), reply_deleted_on); - EXPECT_LT(task_delete_order, reply_delete_order); -} - -TEST_F(MessageLoopTaskRunnerTest, - PostTaskAndReply_DeadReplyTaskRunnerBehavior) { - // Annotate the scope as having memory leaks to suppress heapchecker reports. - ANNOTATE_SCOPED_MEMORY_LEAK; - MessageLoop* task_run_on = nullptr; - MessageLoop* task_deleted_on = nullptr; - int task_delete_order = -1; - MessageLoop* reply_run_on = nullptr; - MessageLoop* reply_deleted_on = nullptr; - int reply_delete_order = -1; - - scoped_refptr task_recorder = - new LoopRecorder(&task_run_on, &task_deleted_on, &task_delete_order); - scoped_refptr reply_recorder = - new LoopRecorder(&reply_run_on, &reply_deleted_on, &reply_delete_order); - - // Enqueue the relay. - task_thread_.task_runner()->PostTaskAndReply( - FROM_HERE, BindOnce(&RecordLoop, task_recorder), - BindOnce(&RecordLoopAndQuit, reply_recorder)); - - // Die if base::Bind doesn't retain a reference to the recorders. - task_recorder = nullptr; - reply_recorder = nullptr; - ASSERT_FALSE(task_deleted_on); - ASSERT_FALSE(reply_deleted_on); - - UnblockTaskThread(); - - // Mercilessly whack the current loop before |reply| gets to run. - current_loop_.reset(); - - // This should ensure the relay has been run. We need to record the - // MessageLoop pointer before stopping the thread because Thread::Stop() will - // NULL out its own pointer. - MessageLoop* task_loop = task_thread_.message_loop(); - task_thread_.Stop(); - - // Even if the reply task runner is already gone, the original task should - // already be deleted. However, the reply which hasn't executed yet should - // leak to avoid thread-safety issues. - EXPECT_EQ(task_loop, task_run_on); - EXPECT_EQ(task_loop, task_deleted_on); - EXPECT_FALSE(reply_run_on); - ASSERT_FALSE(reply_deleted_on); - - // The PostTaskAndReplyRelay is leaked here. Even if we had a reference to - // it, we cannot just delete it because PostTaskAndReplyRelay's destructor - // checks that MessageLoop::current() is the the same as when the - // PostTaskAndReplyRelay object was constructed. However, this loop must have - // already been deleted in order to perform this test. See - // http://crbug.com/86301. -} - -class MessageLoopTaskRunnerThreadingTest : public testing::Test { - public: - void Release() const { - AssertOnIOThread(); - Quit(); - } - - void Quit() const { - loop_.task_runner()->PostTask( - FROM_HERE, RunLoop::QuitCurrentWhenIdleClosureDeprecated()); - } - - void AssertOnIOThread() const { - ASSERT_TRUE(io_thread_->task_runner()->BelongsToCurrentThread()); - ASSERT_EQ(io_thread_->task_runner(), ThreadTaskRunnerHandle::Get()); - } - - void AssertOnFileThread() const { - ASSERT_TRUE(file_thread_->task_runner()->BelongsToCurrentThread()); - ASSERT_EQ(file_thread_->task_runner(), ThreadTaskRunnerHandle::Get()); - } - - protected: - void SetUp() override { - io_thread_.reset(new Thread("MessageLoopTaskRunnerThreadingTest_IO")); - file_thread_.reset(new Thread("MessageLoopTaskRunnerThreadingTest_File")); - io_thread_->Start(); - file_thread_->Start(); - } - - void TearDown() override { - io_thread_->Stop(); - file_thread_->Stop(); - } - - static void BasicFunction(MessageLoopTaskRunnerThreadingTest* test) { - test->AssertOnFileThread(); - test->Quit(); - } - - static void AssertNotRun() { FAIL() << "Callback Should not get executed."; } - - class DeletedOnFile { - public: - explicit DeletedOnFile(MessageLoopTaskRunnerThreadingTest* test) - : test_(test) {} - - ~DeletedOnFile() { - test_->AssertOnFileThread(); - test_->Quit(); - } - - private: - MessageLoopTaskRunnerThreadingTest* test_; - }; - - std::unique_ptr io_thread_; - std::unique_ptr file_thread_; - - private: - mutable MessageLoop loop_; -}; - -TEST_F(MessageLoopTaskRunnerThreadingTest, Release) { - EXPECT_TRUE(io_thread_->task_runner()->ReleaseSoon(FROM_HERE, this)); - RunLoop().Run(); -} - -TEST_F(MessageLoopTaskRunnerThreadingTest, Delete) { - DeletedOnFile* deleted_on_file = new DeletedOnFile(this); - EXPECT_TRUE( - file_thread_->task_runner()->DeleteSoon(FROM_HERE, deleted_on_file)); - RunLoop().Run(); -} - -TEST_F(MessageLoopTaskRunnerThreadingTest, PostTask) { - EXPECT_TRUE(file_thread_->task_runner()->PostTask( - FROM_HERE, BindOnce(&MessageLoopTaskRunnerThreadingTest::BasicFunction, - Unretained(this)))); - RunLoop().Run(); -} - -TEST_F(MessageLoopTaskRunnerThreadingTest, PostTaskAfterThreadExits) { - std::unique_ptr test_thread( - new Thread("MessageLoopTaskRunnerThreadingTest_Dummy")); - test_thread->Start(); - scoped_refptr task_runner = - test_thread->task_runner(); - test_thread->Stop(); - - bool ret = task_runner->PostTask( - FROM_HERE, BindOnce(&MessageLoopTaskRunnerThreadingTest::AssertNotRun)); - EXPECT_FALSE(ret); -} - -TEST_F(MessageLoopTaskRunnerThreadingTest, PostTaskAfterThreadIsDeleted) { - scoped_refptr task_runner; - { - std::unique_ptr test_thread( - new Thread("MessageLoopTaskRunnerThreadingTest_Dummy")); - test_thread->Start(); - task_runner = test_thread->task_runner(); - } - bool ret = task_runner->PostTask( - FROM_HERE, BindOnce(&MessageLoopTaskRunnerThreadingTest::AssertNotRun)); - EXPECT_FALSE(ret); -} - -} // namespace base diff --git a/message_loop/message_loop_unittest.cc b/message_loop/message_loop_unittest.cc deleted file mode 100644 index db4b9bf45..000000000 --- a/message_loop/message_loop_unittest.cc +++ /dev/null @@ -1,2293 +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 -#include - -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_current.h" -#include "base/message_loop/message_pump_for_io.h" -#include "base/pending_task.h" -#include "base/posix/eintr_wrapper.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/synchronization/atomic_flag.h" -#include "base/synchronization/waitable_event.h" -#include "base/task_scheduler/task_scheduler.h" -#include "base/test/bind_test_util.h" -#include "base/test/gtest_util.h" -#include "base/test/test_simple_task_runner.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" -#include "base/threading/sequence_local_storage_slot.h" -#include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -#if defined(OS_ANDROID) -#include "base/android/java_handler_thread.h" -#include "base/android/jni_android.h" -#include "base/test/android/java_handler_thread_helpers.h" -#endif - -#if defined(OS_WIN) -#include "base/message_loop/message_pump_win.h" -#include "base/process/memory.h" -#include "base/strings/string16.h" -#include "base/win/current_module.h" -#include "base/win/scoped_handle.h" -#endif - -namespace base { - -// TODO(darin): Platform-specific MessageLoop tests should be grouped together -// to avoid chopping this file up with so many #ifdefs. - -namespace { - -class Foo : public RefCounted { - public: - 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_; } - - private: - friend class RefCounted; - - ~Foo() = default; - - int test_count_; - std::string result_; - - DISALLOW_COPY_AND_ASSIGN(Foo); -}; - -// This function runs slowly to simulate a large amount of work being done. -static void SlowFunc(TimeDelta pause, int* quit_counter) { - PlatformThread::Sleep(pause); - if (--(*quit_counter) == 0) - RunLoop::QuitCurrentWhenIdleDeprecated(); -} - -// This function records the time when Run was called in a Time object, which is -// useful for building a variety of MessageLoop tests. -static void RecordRunTimeFunc(TimeTicks* run_time, int* quit_counter) { - *run_time = TimeTicks::Now(); - - // Cause our Run function to take some time to execute. As a result we can - // count on subsequent RecordRunTimeFunc()s running at a future time, - // without worry about the resolution of our system clock being an issue. - SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter); -} - -enum TaskType { - MESSAGEBOX, - ENDDIALOG, - RECURSIVE, - TIMEDMESSAGELOOP, - QUITMESSAGELOOP, - ORDERED, - PUMPS, - SLEEP, - RUNS, -}; - -// Saves the order in which the tasks executed. -struct TaskItem { - TaskItem(TaskType t, int c, bool s) - : type(t), - cookie(c), - start(s) { - } - - TaskType type; - int cookie; - bool start; - - bool operator == (const TaskItem& other) const { - return type == other.type && cookie == other.cookie && start == other.start; - } -}; - -std::ostream& operator <<(std::ostream& os, TaskType type) { - switch (type) { - case MESSAGEBOX: os << "MESSAGEBOX"; break; - case ENDDIALOG: os << "ENDDIALOG"; break; - case RECURSIVE: os << "RECURSIVE"; break; - case TIMEDMESSAGELOOP: os << "TIMEDMESSAGELOOP"; break; - case QUITMESSAGELOOP: os << "QUITMESSAGELOOP"; break; - case ORDERED: os << "ORDERED"; break; - case PUMPS: os << "PUMPS"; break; - case SLEEP: os << "SLEEP"; break; - default: - NOTREACHED(); - os << "Unknown TaskType"; - break; - } - return os; -} - -std::ostream& operator <<(std::ostream& os, const TaskItem& item) { - if (item.start) - return os << item.type << " " << item.cookie << " starts"; - else - return os << item.type << " " << item.cookie << " ends"; -} - -class TaskList { - public: - void RecordStart(TaskType type, int cookie) { - TaskItem item(type, cookie, true); - DVLOG(1) << item; - task_list_.push_back(item); - } - - void RecordEnd(TaskType type, int cookie) { - TaskItem item(type, cookie, false); - DVLOG(1) << item; - task_list_.push_back(item); - } - - size_t Size() { - return task_list_.size(); - } - - TaskItem Get(int n) { - return task_list_[n]; - } - - private: - std::vector task_list_; -}; - -class DummyTaskObserver : public MessageLoop::TaskObserver { - public: - explicit DummyTaskObserver(int num_tasks) - : num_tasks_started_(0), num_tasks_processed_(0), num_tasks_(num_tasks) {} - - DummyTaskObserver(int num_tasks, int num_tasks_started) - : num_tasks_started_(num_tasks_started), - num_tasks_processed_(0), - num_tasks_(num_tasks) {} - - ~DummyTaskObserver() override = default; - - void WillProcessTask(const PendingTask& pending_task) override { - num_tasks_started_++; - EXPECT_LE(num_tasks_started_, num_tasks_); - EXPECT_EQ(num_tasks_started_, num_tasks_processed_ + 1); - } - - void DidProcessTask(const PendingTask& pending_task) override { - num_tasks_processed_++; - EXPECT_LE(num_tasks_started_, num_tasks_); - EXPECT_EQ(num_tasks_started_, num_tasks_processed_); - } - - int num_tasks_started() const { return num_tasks_started_; } - int num_tasks_processed() const { return num_tasks_processed_; } - - private: - int num_tasks_started_; - int num_tasks_processed_; - const int num_tasks_; - - DISALLOW_COPY_AND_ASSIGN(DummyTaskObserver); -}; - -void RecursiveFunc(TaskList* order, int cookie, int depth, - bool is_reentrant) { - order->RecordStart(RECURSIVE, cookie); - if (depth > 0) { - if (is_reentrant) - MessageLoopCurrent::Get()->SetNestableTasksAllowed(true); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - BindOnce(&RecursiveFunc, order, cookie, depth - 1, is_reentrant)); - } - order->RecordEnd(RECURSIVE, cookie); -} - -void QuitFunc(TaskList* order, int cookie) { - order->RecordStart(QUITMESSAGELOOP, cookie); - RunLoop::QuitCurrentWhenIdleDeprecated(); - order->RecordEnd(QUITMESSAGELOOP, cookie); -} - -void PostNTasks(int posts_remaining) { - if (posts_remaining > 1) { - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&PostNTasks, posts_remaining - 1)); - } -} - -enum class TaskSchedulerAvailability { - NO_TASK_SCHEDULER, - WITH_TASK_SCHEDULER, -}; - -std::string TaskSchedulerAvailabilityToString( - TaskSchedulerAvailability availability) { - switch (availability) { - case TaskSchedulerAvailability::NO_TASK_SCHEDULER: - return "NoTaskScheduler"; - case TaskSchedulerAvailability::WITH_TASK_SCHEDULER: - return "WithTaskScheduler"; - } - NOTREACHED(); - return "Unknown"; -} - -class MessageLoopTest - : public ::testing::TestWithParam { - public: - MessageLoopTest() = default; - ~MessageLoopTest() override = default; - - void SetUp() override { - if (GetParam() == TaskSchedulerAvailability::WITH_TASK_SCHEDULER) - TaskScheduler::CreateAndStartWithDefaultParams("MessageLoopTest"); - } - - void TearDown() override { - if (GetParam() == TaskSchedulerAvailability::WITH_TASK_SCHEDULER) { - // Failure to call FlushForTesting() could result in task leaks as tasks - // are skipped on shutdown. - base::TaskScheduler::GetInstance()->FlushForTesting(); - base::TaskScheduler::GetInstance()->Shutdown(); - base::TaskScheduler::GetInstance()->JoinForTesting(); - base::TaskScheduler::SetInstance(nullptr); - } - } - - static std::string ParamInfoToString( - ::testing::TestParamInfo param_info) { - return TaskSchedulerAvailabilityToString(param_info.param); - } - - private: - DISALLOW_COPY_AND_ASSIGN(MessageLoopTest); -}; - -#if defined(OS_ANDROID) -void DoNotRun() { - ASSERT_TRUE(false); -} - -void RunTest_AbortDontRunMoreTasks(bool delayed, bool init_java_first) { - WaitableEvent test_done_event(WaitableEvent::ResetPolicy::MANUAL, - WaitableEvent::InitialState::NOT_SIGNALED); - std::unique_ptr java_thread; - if (init_java_first) { - java_thread = android::JavaHandlerThreadHelpers::CreateJavaFirst(); - } else { - java_thread = std::make_unique( - "JavaHandlerThreadForTesting from AbortDontRunMoreTasks"); - } - java_thread->Start(); - java_thread->ListenForUncaughtExceptionsForTesting(); - - auto target = - BindOnce(&android::JavaHandlerThreadHelpers::ThrowExceptionAndAbort, - &test_done_event); - if (delayed) { - java_thread->message_loop()->task_runner()->PostDelayedTask( - FROM_HERE, std::move(target), TimeDelta::FromMilliseconds(10)); - } else { - java_thread->message_loop()->task_runner()->PostTask(FROM_HERE, - std::move(target)); - java_thread->message_loop()->task_runner()->PostTask(FROM_HERE, - BindOnce(&DoNotRun)); - } - test_done_event.Wait(); - java_thread->Stop(); - android::ScopedJavaLocalRef exception = - java_thread->GetUncaughtExceptionIfAny(); - ASSERT_TRUE( - android::JavaHandlerThreadHelpers::IsExceptionTestException(exception)); -} - -TEST_P(MessageLoopTest, JavaExceptionAbort) { - constexpr bool delayed = false; - constexpr bool init_java_first = false; - RunTest_AbortDontRunMoreTasks(delayed, init_java_first); -} -TEST_P(MessageLoopTest, DelayedJavaExceptionAbort) { - constexpr bool delayed = true; - constexpr bool init_java_first = false; - RunTest_AbortDontRunMoreTasks(delayed, init_java_first); -} -TEST_P(MessageLoopTest, JavaExceptionAbortInitJavaFirst) { - constexpr bool delayed = false; - constexpr bool init_java_first = true; - RunTest_AbortDontRunMoreTasks(delayed, init_java_first); -} - -TEST_P(MessageLoopTest, RunTasksWhileShuttingDownJavaThread) { - const int kNumPosts = 6; - DummyTaskObserver observer(kNumPosts, 1); - - auto java_thread = std::make_unique("test"); - java_thread->Start(); - - java_thread->message_loop()->task_runner()->PostTask( - FROM_HERE, - BindOnce( - [](android::JavaHandlerThread* java_thread, - DummyTaskObserver* observer, int num_posts) { - java_thread->message_loop()->AddTaskObserver(observer); - ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, BindOnce([]() { ADD_FAILURE(); }), - TimeDelta::FromDays(1)); - java_thread->StopMessageLoopForTesting(); - PostNTasks(num_posts); - }, - Unretained(java_thread.get()), Unretained(&observer), kNumPosts)); - - java_thread->JoinForTesting(); - java_thread.reset(); - - EXPECT_EQ(kNumPosts, observer.num_tasks_started()); - EXPECT_EQ(kNumPosts, observer.num_tasks_processed()); -} -#endif // defined(OS_ANDROID) - -#if defined(OS_WIN) - -void SubPumpFunc() { - MessageLoopCurrent::Get()->SetNestableTasksAllowed(true); - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - RunLoop::QuitCurrentWhenIdleDeprecated(); -} - -void RunTest_PostDelayedTask_SharedTimer_SubPump() { - MessageLoop message_loop(MessageLoop::TYPE_UI); - - // Test that the interval of the timer, used to run the next delayed task, is - // set to a value corresponding to when the next delayed task should run. - - // By setting num_tasks to 1, we ensure that the first task to run causes the - // run loop to exit. - int num_tasks = 1; - TimeTicks run_time; - - message_loop.task_runner()->PostTask(FROM_HERE, BindOnce(&SubPumpFunc)); - - // This very delayed task should never run. - message_loop.task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks), - TimeDelta::FromSeconds(1000)); - - // This slightly delayed task should run from within SubPumpFunc. - message_loop.task_runner()->PostDelayedTask(FROM_HERE, - BindOnce(&PostQuitMessage, 0), - TimeDelta::FromMilliseconds(10)); - - Time start_time = Time::Now(); - - RunLoop().Run(); - EXPECT_EQ(1, num_tasks); - - // Ensure that we ran in far less time than the slower timer. - TimeDelta total_time = Time::Now() - start_time; - EXPECT_GT(5000, total_time.InMilliseconds()); - - // In case both timers somehow run at nearly the same time, sleep a little - // and then run all pending to force them both to have run. This is just - // encouraging flakiness if there is any. - PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); - RunLoop().RunUntilIdle(); - - EXPECT_TRUE(run_time.is_null()); -} - -const wchar_t kMessageBoxTitle[] = L"MessageLoop Unit Test"; - -// MessageLoop implicitly start a "modal message loop". Modal dialog boxes, -// common controls (like OpenFile) and StartDoc printing function can cause -// implicit message loops. -void MessageBoxFunc(TaskList* order, int cookie, bool is_reentrant) { - order->RecordStart(MESSAGEBOX, cookie); - if (is_reentrant) - MessageLoopCurrent::Get()->SetNestableTasksAllowed(true); - MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK); - order->RecordEnd(MESSAGEBOX, cookie); -} - -// Will end the MessageBox. -void EndDialogFunc(TaskList* order, int cookie) { - order->RecordStart(ENDDIALOG, cookie); - HWND window = GetActiveWindow(); - if (window != NULL) { - EXPECT_NE(EndDialog(window, IDCONTINUE), 0); - // Cheap way to signal that the window wasn't found if RunEnd() isn't - // called. - order->RecordEnd(ENDDIALOG, cookie); - } -} - -void RecursiveFuncWin(scoped_refptr task_runner, - HANDLE event, - bool expect_window, - TaskList* order, - bool is_reentrant) { - task_runner->PostTask(FROM_HERE, - BindOnce(&RecursiveFunc, order, 1, 2, is_reentrant)); - task_runner->PostTask(FROM_HERE, - BindOnce(&MessageBoxFunc, order, 2, is_reentrant)); - task_runner->PostTask(FROM_HERE, - BindOnce(&RecursiveFunc, order, 3, 2, is_reentrant)); - // The trick here is that for recursive task processing, this task will be - // ran _inside_ the MessageBox message loop, dismissing the MessageBox - // without a chance. - // For non-recursive task processing, this will be executed _after_ the - // MessageBox will have been dismissed by the code below, where - // expect_window_ is true. - task_runner->PostTask(FROM_HERE, BindOnce(&EndDialogFunc, order, 4)); - task_runner->PostTask(FROM_HERE, BindOnce(&QuitFunc, order, 5)); - - // Enforce that every tasks are sent before starting to run the main thread - // message loop. - ASSERT_TRUE(SetEvent(event)); - - // Poll for the MessageBox. Don't do this at home! At the speed we do it, - // you will never realize one MessageBox was shown. - for (; expect_window;) { - HWND window = FindWindow(L"#32770", kMessageBoxTitle); - if (window) { - // Dismiss it. - for (;;) { - HWND button = FindWindowEx(window, NULL, L"Button", NULL); - if (button != NULL) { - EXPECT_EQ(0, SendMessage(button, WM_LBUTTONDOWN, 0, 0)); - EXPECT_EQ(0, SendMessage(button, WM_LBUTTONUP, 0, 0)); - break; - } - } - break; - } - } -} - -// TODO(darin): These tests need to be ported since they test critical -// message loop functionality. - -// A side effect of this test is the generation a beep. Sorry. -void RunTest_RecursiveDenial2(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - Thread worker("RecursiveDenial2_worker"); - Thread::Options options; - options.message_loop_type = message_loop_type; - ASSERT_EQ(true, worker.StartWithOptions(options)); - TaskList order; - win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); - worker.task_runner()->PostTask( - FROM_HERE, BindOnce(&RecursiveFuncWin, ThreadTaskRunnerHandle::Get(), - event.Get(), true, &order, false)); - // Let the other thread execute. - WaitForSingleObject(event.Get(), INFINITE); - RunLoop().Run(); - - ASSERT_EQ(17u, order.Size()); - EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true)); - EXPECT_EQ(order.Get(3), TaskItem(MESSAGEBOX, 2, false)); - EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 3, false)); - // When EndDialogFunc is processed, the window is already dismissed, hence no - // "end" entry. - EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, true)); - EXPECT_EQ(order.Get(7), TaskItem(QUITMESSAGELOOP, 5, true)); - EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, false)); - EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, false)); - EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, false)); -} - -// A side effect of this test is the generation a beep. Sorry. This test also -// needs to process windows messages on the current thread. -void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type) { - MessageLoop loop(message_loop_type); - - Thread worker("RecursiveSupport2_worker"); - Thread::Options options; - options.message_loop_type = message_loop_type; - ASSERT_EQ(true, worker.StartWithOptions(options)); - TaskList order; - win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); - worker.task_runner()->PostTask( - FROM_HERE, BindOnce(&RecursiveFuncWin, ThreadTaskRunnerHandle::Get(), - event.Get(), false, &order, true)); - // Let the other thread execute. - WaitForSingleObject(event.Get(), INFINITE); - RunLoop().Run(); - - ASSERT_EQ(18u, order.Size()); - EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(2), TaskItem(MESSAGEBOX, 2, true)); - // Note that this executes in the MessageBox modal loop. - EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 3, false)); - EXPECT_EQ(order.Get(5), TaskItem(ENDDIALOG, 4, true)); - EXPECT_EQ(order.Get(6), TaskItem(ENDDIALOG, 4, false)); - EXPECT_EQ(order.Get(7), TaskItem(MESSAGEBOX, 2, false)); - /* The order can subtly change here. The reason is that when RecursiveFunc(1) - is called in the main thread, if it is faster than getting to the - PostTask(FROM_HERE, BindOnce(&QuitFunc) execution, the order of task - execution can change. We don't care anyway that the order isn't correct. - EXPECT_EQ(order.Get(8), TaskItem(QUITMESSAGELOOP, 5, true)); - EXPECT_EQ(order.Get(9), TaskItem(QUITMESSAGELOOP, 5, false)); - EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false)); - */ - EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 3, false)); - EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(16), TaskItem(RECURSIVE, 3, true)); - EXPECT_EQ(order.Get(17), TaskItem(RECURSIVE, 3, false)); -} - -#endif // defined(OS_WIN) - -void PostNTasksThenQuit(int posts_remaining) { - if (posts_remaining > 1) { - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&PostNTasksThenQuit, posts_remaining - 1)); - } else { - RunLoop::QuitCurrentWhenIdleDeprecated(); - } -} - -#if defined(OS_WIN) - -class TestIOHandler : public MessagePumpForIO::IOHandler { - public: - TestIOHandler(const wchar_t* name, HANDLE signal, bool wait); - - void OnIOCompleted(MessagePumpForIO::IOContext* context, - DWORD bytes_transfered, - DWORD error) override; - - void Init(); - void WaitForIO(); - OVERLAPPED* context() { return &context_.overlapped; } - DWORD size() { return sizeof(buffer_); } - - private: - char buffer_[48]; - MessagePumpForIO::IOContext context_; - HANDLE signal_; - win::ScopedHandle file_; - bool wait_; -}; - -TestIOHandler::TestIOHandler(const wchar_t* name, HANDLE signal, bool wait) - : signal_(signal), wait_(wait) { - memset(buffer_, 0, sizeof(buffer_)); - - file_.Set(CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL)); - EXPECT_TRUE(file_.IsValid()); -} - -void TestIOHandler::Init() { - MessageLoopCurrentForIO::Get()->RegisterIOHandler(file_.Get(), this); - - DWORD read; - EXPECT_FALSE(ReadFile(file_.Get(), buffer_, size(), &read, context())); - EXPECT_EQ(static_cast(ERROR_IO_PENDING), GetLastError()); - if (wait_) - WaitForIO(); -} - -void TestIOHandler::OnIOCompleted(MessagePumpForIO::IOContext* context, - DWORD bytes_transfered, - DWORD error) { - ASSERT_TRUE(context == &context_); - ASSERT_TRUE(SetEvent(signal_)); -} - -void TestIOHandler::WaitForIO() { - EXPECT_TRUE(MessageLoopCurrentForIO::Get()->WaitForIOCompletion(300, this)); - EXPECT_TRUE(MessageLoopCurrentForIO::Get()->WaitForIOCompletion(400, this)); -} - -void RunTest_IOHandler() { - win::ScopedHandle callback_called(CreateEvent(NULL, TRUE, FALSE, NULL)); - ASSERT_TRUE(callback_called.IsValid()); - - const wchar_t* kPipeName = L"\\\\.\\pipe\\iohandler_pipe"; - win::ScopedHandle server( - CreateNamedPipe(kPipeName, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL)); - ASSERT_TRUE(server.IsValid()); - - Thread thread("IOHandler test"); - Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - ASSERT_TRUE(thread.StartWithOptions(options)); - - TestIOHandler handler(kPipeName, callback_called.Get(), false); - thread.task_runner()->PostTask( - FROM_HERE, BindOnce(&TestIOHandler::Init, Unretained(&handler))); - // Make sure the thread runs and sleeps for lack of work. - PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); - - const char buffer[] = "Hello there!"; - DWORD written; - EXPECT_TRUE(WriteFile(server.Get(), buffer, sizeof(buffer), &written, NULL)); - - DWORD result = WaitForSingleObject(callback_called.Get(), 1000); - EXPECT_EQ(WAIT_OBJECT_0, result); - - thread.Stop(); -} - -void RunTest_WaitForIO() { - win::ScopedHandle callback1_called( - CreateEvent(NULL, TRUE, FALSE, NULL)); - win::ScopedHandle callback2_called( - CreateEvent(NULL, TRUE, FALSE, NULL)); - ASSERT_TRUE(callback1_called.IsValid()); - ASSERT_TRUE(callback2_called.IsValid()); - - const wchar_t* kPipeName1 = L"\\\\.\\pipe\\iohandler_pipe1"; - const wchar_t* kPipeName2 = L"\\\\.\\pipe\\iohandler_pipe2"; - win::ScopedHandle server1( - CreateNamedPipe(kPipeName1, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL)); - win::ScopedHandle server2( - CreateNamedPipe(kPipeName2, PIPE_ACCESS_OUTBOUND, 0, 1, 0, 0, 0, NULL)); - ASSERT_TRUE(server1.IsValid()); - ASSERT_TRUE(server2.IsValid()); - - Thread thread("IOHandler test"); - Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - ASSERT_TRUE(thread.StartWithOptions(options)); - - TestIOHandler handler1(kPipeName1, callback1_called.Get(), false); - TestIOHandler handler2(kPipeName2, callback2_called.Get(), true); - thread.task_runner()->PostTask( - FROM_HERE, BindOnce(&TestIOHandler::Init, Unretained(&handler1))); - // TODO(ajwong): Do we really need such long Sleeps in this function? - // Make sure the thread runs and sleeps for lack of work. - TimeDelta delay = TimeDelta::FromMilliseconds(100); - PlatformThread::Sleep(delay); - thread.task_runner()->PostTask( - FROM_HERE, BindOnce(&TestIOHandler::Init, Unretained(&handler2))); - PlatformThread::Sleep(delay); - - // At this time handler1 is waiting to be called, and the thread is waiting - // on the Init method of handler2, filtering only handler2 callbacks. - - const char buffer[] = "Hello there!"; - DWORD written; - EXPECT_TRUE(WriteFile(server1.Get(), buffer, sizeof(buffer), &written, NULL)); - PlatformThread::Sleep(2 * delay); - EXPECT_EQ(static_cast(WAIT_TIMEOUT), - WaitForSingleObject(callback1_called.Get(), 0)) - << "handler1 has not been called"; - - EXPECT_TRUE(WriteFile(server2.Get(), buffer, sizeof(buffer), &written, NULL)); - - HANDLE objects[2] = { callback1_called.Get(), callback2_called.Get() }; - DWORD result = WaitForMultipleObjects(2, objects, TRUE, 1000); - EXPECT_EQ(WAIT_OBJECT_0, result); - - thread.Stop(); -} - -#endif // defined(OS_WIN) - -} // namespace - -//----------------------------------------------------------------------------- -// Each test is run against each type of MessageLoop. That way we are sure -// that message loops work properly in all configurations. Of course, in some -// cases, a unit test may only be for a particular type of loop. - -namespace { - -struct MessageLoopTypedTestParams { - MessageLoopTypedTestParams( - MessageLoop::Type type_in, - TaskSchedulerAvailability task_scheduler_availability_in) { - type = type_in; - task_scheduler_availability = task_scheduler_availability_in; - } - - MessageLoop::Type type; - TaskSchedulerAvailability task_scheduler_availability; -}; - -class MessageLoopTypedTest - : public ::testing::TestWithParam { - public: - MessageLoopTypedTest() = default; - ~MessageLoopTypedTest() = default; - - void SetUp() override { - if (GetTaskSchedulerAvailability() == - TaskSchedulerAvailability::WITH_TASK_SCHEDULER) { - TaskScheduler::CreateAndStartWithDefaultParams("MessageLoopTypedTest"); - } - } - - void TearDown() override { - if (GetTaskSchedulerAvailability() == - TaskSchedulerAvailability::WITH_TASK_SCHEDULER) { - // Failure to call FlushForTesting() could result in task leaks as tasks - // are skipped on shutdown. - base::TaskScheduler::GetInstance()->FlushForTesting(); - base::TaskScheduler::GetInstance()->Shutdown(); - base::TaskScheduler::GetInstance()->JoinForTesting(); - base::TaskScheduler::SetInstance(nullptr); - } - } - - static std::string ParamInfoToString( - ::testing::TestParamInfo param_info) { - return MessageLoopTypeToString(param_info.param.type) + "_" + - TaskSchedulerAvailabilityToString( - param_info.param.task_scheduler_availability); - } - - protected: - MessageLoop::Type GetMessageLoopType() { return GetParam().type; } - - private: - static std::string MessageLoopTypeToString(MessageLoop::Type type) { - switch (type) { - case MessageLoop::TYPE_DEFAULT: - return "Default"; - case MessageLoop::TYPE_IO: - return "IO"; - case MessageLoop::TYPE_UI: - return "UI"; - case MessageLoop::TYPE_CUSTOM: -#if defined(OS_ANDROID) - case MessageLoop::TYPE_JAVA: -#endif // defined(OS_ANDROID) - break; - } - NOTREACHED(); - return "NotSupported"; - } - - TaskSchedulerAvailability GetTaskSchedulerAvailability() { - return GetParam().task_scheduler_availability; - } - - DISALLOW_COPY_AND_ASSIGN(MessageLoopTypedTest); -}; - -} // namespace - -TEST_P(MessageLoopTypedTest, PostTask) { - MessageLoop loop(GetMessageLoopType()); - // Add tests to message loop - scoped_refptr foo(new Foo()); - std::string a("a"), b("b"), c("c"), d("d"); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&Foo::Test0, foo)); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&Foo::Test1Ptr, foo, &b)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&Foo::Test1Int, foo, 100)); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&Foo::Test2Ptr, foo, &a, &c)); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&Foo::Test2Mixed, foo, a, &d)); - // After all tests, post a message that will shut down the message loop - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&RunLoop::QuitCurrentWhenIdleDeprecated)); - - // Now kick things off - RunLoop().Run(); - - EXPECT_EQ(foo->test_count(), 105); - EXPECT_EQ(foo->result(), "abacad"); -} - -TEST_P(MessageLoopTypedTest, PostDelayedTask_Basic) { - MessageLoop loop(GetMessageLoopType()); - - // Test that PostDelayedTask results in a delayed task. - - const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); - - int num_tasks = 1; - TimeTicks run_time; - - TimeTicks time_before_run = TimeTicks::Now(); - loop.task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks), kDelay); - RunLoop().Run(); - TimeTicks time_after_run = TimeTicks::Now(); - - EXPECT_EQ(0, num_tasks); - EXPECT_LT(kDelay, time_after_run - time_before_run); -} - -TEST_P(MessageLoopTypedTest, PostDelayedTask_InDelayOrder) { - MessageLoop loop(GetMessageLoopType()); - - // Test that two tasks with different delays run in the right order. - int num_tasks = 2; - TimeTicks run_time1, run_time2; - - loop.task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks), - TimeDelta::FromMilliseconds(200)); - // If we get a large pause in execution (due to a context switch) here, this - // test could fail. - loop.task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), - TimeDelta::FromMilliseconds(10)); - - RunLoop().Run(); - EXPECT_EQ(0, num_tasks); - - EXPECT_TRUE(run_time2 < run_time1); -} - -TEST_P(MessageLoopTypedTest, PostDelayedTask_InPostOrder) { - MessageLoop loop(GetMessageLoopType()); - - // Test that two tasks with the same delay run in the order in which they - // were posted. - // - // NOTE: This is actually an approximate test since the API only takes a - // "delay" parameter, so we are not exactly simulating two tasks that get - // posted at the exact same time. It would be nice if the API allowed us to - // specify the desired run time. - - const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); - - int num_tasks = 2; - TimeTicks run_time1, run_time2; - - loop.task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay); - loop.task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay); - - RunLoop().Run(); - EXPECT_EQ(0, num_tasks); - - EXPECT_TRUE(run_time1 < run_time2); -} - -TEST_P(MessageLoopTypedTest, PostDelayedTask_InPostOrder_2) { - MessageLoop loop(GetMessageLoopType()); - - // Test that a delayed task still runs after a normal tasks even if the - // normal tasks take a long time to run. - - const TimeDelta kPause = TimeDelta::FromMilliseconds(50); - - int num_tasks = 2; - TimeTicks run_time; - - loop.task_runner()->PostTask(FROM_HERE, - BindOnce(&SlowFunc, kPause, &num_tasks)); - loop.task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time, &num_tasks), - TimeDelta::FromMilliseconds(10)); - - TimeTicks time_before_run = TimeTicks::Now(); - RunLoop().Run(); - TimeTicks time_after_run = TimeTicks::Now(); - - EXPECT_EQ(0, num_tasks); - - EXPECT_LT(kPause, time_after_run - time_before_run); -} - -TEST_P(MessageLoopTypedTest, PostDelayedTask_InPostOrder_3) { - MessageLoop loop(GetMessageLoopType()); - - // Test that a delayed task still runs after a pile of normal tasks. The key - // difference between this test and the previous one is that here we return - // the MessageLoop a lot so we give the MessageLoop plenty of opportunities - // to maybe run the delayed task. It should know not to do so until the - // delayed task's delay has passed. - - int num_tasks = 11; - TimeTicks run_time1, run_time2; - - // Clutter the ML with tasks. - for (int i = 1; i < num_tasks; ++i) - loop.task_runner()->PostTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks)); - - loop.task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), - TimeDelta::FromMilliseconds(1)); - - RunLoop().Run(); - EXPECT_EQ(0, num_tasks); - - EXPECT_TRUE(run_time2 > run_time1); -} - -TEST_P(MessageLoopTypedTest, PostDelayedTask_SharedTimer) { - MessageLoop loop(GetMessageLoopType()); - - // Test that the interval of the timer, used to run the next delayed task, is - // set to a value corresponding to when the next delayed task should run. - - // By setting num_tasks to 1, we ensure that the first task to run causes the - // run loop to exit. - int num_tasks = 1; - TimeTicks run_time1, run_time2; - - loop.task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time1, &num_tasks), - TimeDelta::FromSeconds(1000)); - loop.task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&RecordRunTimeFunc, &run_time2, &num_tasks), - TimeDelta::FromMilliseconds(10)); - - TimeTicks start_time = TimeTicks::Now(); - - RunLoop().Run(); - EXPECT_EQ(0, num_tasks); - - // Ensure that we ran in far less time than the slower timer. - TimeDelta total_time = TimeTicks::Now() - start_time; - EXPECT_GT(5000, total_time.InMilliseconds()); - - // In case both timers somehow run at nearly the same time, sleep a little - // and then run all pending to force them both to have run. This is just - // encouraging flakiness if there is any. - PlatformThread::Sleep(TimeDelta::FromMilliseconds(100)); - RunLoop().RunUntilIdle(); - - EXPECT_TRUE(run_time1.is_null()); - EXPECT_FALSE(run_time2.is_null()); -} - -namespace { - -// This is used to inject a test point for recording the destructor calls for -// Closure objects send to MessageLoop::PostTask(). It is awkward usage since we -// are trying to hook the actual destruction, which is not a common operation. -class RecordDeletionProbe : public RefCounted { - public: - RecordDeletionProbe(RecordDeletionProbe* post_on_delete, bool* was_deleted) - : post_on_delete_(post_on_delete), was_deleted_(was_deleted) {} - void Run() {} - - private: - friend class RefCounted; - - ~RecordDeletionProbe() { - *was_deleted_ = true; - if (post_on_delete_.get()) - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&RecordDeletionProbe::Run, post_on_delete_)); - } - - scoped_refptr post_on_delete_; - bool* was_deleted_; -}; - -} // namespace - -/* TODO(darin): MessageLoop does not support deleting all tasks in the */ -/* destructor. */ -/* Fails, http://crbug.com/50272. */ -TEST_P(MessageLoopTypedTest, DISABLED_EnsureDeletion) { - bool a_was_deleted = false; - bool b_was_deleted = false; - { - MessageLoop loop(GetMessageLoopType()); - loop.task_runner()->PostTask( - FROM_HERE, BindOnce(&RecordDeletionProbe::Run, - new RecordDeletionProbe(nullptr, &a_was_deleted))); - // TODO(ajwong): Do we really need 1000ms here? - loop.task_runner()->PostDelayedTask( - FROM_HERE, - BindOnce(&RecordDeletionProbe::Run, - new RecordDeletionProbe(nullptr, &b_was_deleted)), - TimeDelta::FromMilliseconds(1000)); - } - EXPECT_TRUE(a_was_deleted); - EXPECT_TRUE(b_was_deleted); -} - -/* TODO(darin): MessageLoop does not support deleting all tasks in the */ -/* destructor. */ -/* Fails, http://crbug.com/50272. */ -TEST_P(MessageLoopTypedTest, DISABLED_EnsureDeletion_Chain) { - bool a_was_deleted = false; - bool b_was_deleted = false; - bool c_was_deleted = false; - { - MessageLoop loop(GetMessageLoopType()); - // The scoped_refptr for each of the below is held either by the chained - // RecordDeletionProbe, or the bound RecordDeletionProbe::Run() callback. - RecordDeletionProbe* a = new RecordDeletionProbe(nullptr, &a_was_deleted); - RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted); - RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted); - loop.task_runner()->PostTask(FROM_HERE, - BindOnce(&RecordDeletionProbe::Run, c)); - } - EXPECT_TRUE(a_was_deleted); - EXPECT_TRUE(b_was_deleted); - EXPECT_TRUE(c_was_deleted); -} - -namespace { - -void NestingFunc(int* depth) { - if (*depth > 0) { - *depth -= 1; - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&NestingFunc, depth)); - - MessageLoopCurrent::Get()->SetNestableTasksAllowed(true); - RunLoop().Run(); - } - base::RunLoop::QuitCurrentWhenIdleDeprecated(); -} - -} // namespace - -TEST_P(MessageLoopTypedTest, Nesting) { - MessageLoop loop(GetMessageLoopType()); - - int depth = 50; - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&NestingFunc, &depth)); - RunLoop().Run(); - EXPECT_EQ(depth, 0); -} - -TEST_P(MessageLoopTypedTest, RecursiveDenial1) { - MessageLoop loop(GetMessageLoopType()); - - EXPECT_TRUE(MessageLoopCurrent::Get()->NestableTasksAllowed()); - TaskList order; - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2, false)); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2, false)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&QuitFunc, &order, 3)); - - RunLoop().Run(); - - // FIFO order. - ASSERT_EQ(14U, order.Size()); - EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true)); - EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false)); - EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false)); -} - -namespace { - -void RecursiveSlowFunc(TaskList* order, - int cookie, - int depth, - bool is_reentrant) { - RecursiveFunc(order, cookie, depth, is_reentrant); - PlatformThread::Sleep(TimeDelta::FromMilliseconds(10)); -} - -void OrderedFunc(TaskList* order, int cookie) { - order->RecordStart(ORDERED, cookie); - order->RecordEnd(ORDERED, cookie); -} - -} // namespace - -TEST_P(MessageLoopTypedTest, RecursiveDenial3) { - MessageLoop loop(GetMessageLoopType()); - - EXPECT_TRUE(MessageLoopCurrent::Get()->NestableTasksAllowed()); - TaskList order; - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&RecursiveSlowFunc, &order, 1, 2, false)); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&RecursiveSlowFunc, &order, 2, 2, false)); - ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, BindOnce(&OrderedFunc, &order, 3), - TimeDelta::FromMilliseconds(5)); - ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, BindOnce(&QuitFunc, &order, 4), - TimeDelta::FromMilliseconds(5)); - - RunLoop().Run(); - - // FIFO order. - ASSERT_EQ(16U, order.Size()); - EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 3, true)); - EXPECT_EQ(order.Get(7), TaskItem(ORDERED, 3, false)); - EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 4, true)); - EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 4, false)); - EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 2, false)); -} - -TEST_P(MessageLoopTypedTest, RecursiveSupport1) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2, true)); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2, true)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&QuitFunc, &order, 3)); - - RunLoop().Run(); - - // FIFO order. - ASSERT_EQ(14U, order.Size()); - EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true)); - EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false)); - EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false)); - EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true)); - EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false)); - EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true)); - EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false)); -} - -// Tests that non nestable tasks run in FIFO if there are no nested loops. -TEST_P(MessageLoopTypedTest, NonNestableWithNoNesting) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - ThreadTaskRunnerHandle::Get()->PostNonNestableTask( - FROM_HERE, BindOnce(&OrderedFunc, &order, 1)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&QuitFunc, &order, 3)); - RunLoop().Run(); - - // FIFO order. - ASSERT_EQ(6U, order.Size()); - EXPECT_EQ(order.Get(0), TaskItem(ORDERED, 1, true)); - EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 1, false)); - EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(3), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true)); - EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false)); -} - -namespace { - -void FuncThatPumps(TaskList* order, int cookie) { - order->RecordStart(PUMPS, cookie); - RunLoop(RunLoop::Type::kNestableTasksAllowed).RunUntilIdle(); - order->RecordEnd(PUMPS, cookie); -} - -void SleepFunc(TaskList* order, int cookie, TimeDelta delay) { - order->RecordStart(SLEEP, cookie); - PlatformThread::Sleep(delay); - order->RecordEnd(SLEEP, cookie); -} - -} // namespace - -// Tests that non nestable tasks don't run when there's code in the call stack. -TEST_P(MessageLoopTypedTest, NonNestableDelayedInNestedLoop) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&FuncThatPumps, &order, 1)); - ThreadTaskRunnerHandle::Get()->PostNonNestableTask( - FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 3)); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - BindOnce(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 5)); - ThreadTaskRunnerHandle::Get()->PostNonNestableTask( - FROM_HERE, BindOnce(&QuitFunc, &order, 6)); - - RunLoop().Run(); - - // FIFO order. - ASSERT_EQ(12U, order.Size()); - EXPECT_EQ(order.Get(0), TaskItem(PUMPS, 1, true)); - EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 3, true)); - EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 3, false)); - EXPECT_EQ(order.Get(3), TaskItem(SLEEP, 4, true)); - EXPECT_EQ(order.Get(4), TaskItem(SLEEP, 4, false)); - EXPECT_EQ(order.Get(5), TaskItem(ORDERED, 5, true)); - EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 5, false)); - EXPECT_EQ(order.Get(7), TaskItem(PUMPS, 1, false)); - EXPECT_EQ(order.Get(8), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(9), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 6, true)); - EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 6, false)); -} - -namespace { - -void FuncThatRuns(TaskList* order, int cookie, RunLoop* run_loop) { - order->RecordStart(RUNS, cookie); - { - MessageLoopCurrent::ScopedNestableTaskAllower allow; - run_loop->Run(); - } - order->RecordEnd(RUNS, cookie); -} - -void FuncThatQuitsNow() { - base::RunLoop::QuitCurrentDeprecated(); -} - -} // namespace - -// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. -TEST_P(MessageLoopTypedTest, QuitNow) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - RunLoop run_loop; - - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&run_loop))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&FuncThatQuitsNow)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 3)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&FuncThatQuitsNow)); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&OrderedFunc, &order, 4)); // never runs - - RunLoop().Run(); - - ASSERT_EQ(6U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false)); - EXPECT_EQ(static_cast(task_index), order.Size()); -} - -// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. -TEST_P(MessageLoopTypedTest, RunLoopQuitTop) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - RunLoop outer_run_loop; - RunLoop nested_run_loop; - - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - outer_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_run_loop.QuitClosure()); - - outer_run_loop.Run(); - - ASSERT_EQ(4U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(static_cast(task_index), order.Size()); -} - -// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. -TEST_P(MessageLoopTypedTest, RunLoopQuitNested) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - RunLoop outer_run_loop; - RunLoop nested_run_loop; - - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - outer_run_loop.QuitClosure()); - - outer_run_loop.Run(); - - ASSERT_EQ(4U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(static_cast(task_index), order.Size()); -} - -// Quits current loop and immediately runs a nested loop. -void QuitAndRunNestedLoop(TaskList* order, - int cookie, - RunLoop* outer_run_loop, - RunLoop* nested_run_loop) { - order->RecordStart(RUNS, cookie); - outer_run_loop->Quit(); - nested_run_loop->Run(); - order->RecordEnd(RUNS, cookie); -} - -// Test that we can run nested loop after quitting the current one. -TEST_P(MessageLoopTypedTest, RunLoopNestedAfterQuit) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - RunLoop outer_run_loop; - RunLoop nested_run_loop; - - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&QuitAndRunNestedLoop, &order, 1, &outer_run_loop, - &nested_run_loop)); - - outer_run_loop.Run(); - - ASSERT_EQ(2U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(static_cast(task_index), order.Size()); -} - -// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. -TEST_P(MessageLoopTypedTest, RunLoopQuitBogus) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - RunLoop outer_run_loop; - RunLoop nested_run_loop; - RunLoop bogus_run_loop; - - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - bogus_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - outer_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_run_loop.QuitClosure()); - - outer_run_loop.Run(); - - ASSERT_EQ(4U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(static_cast(task_index), order.Size()); -} - -// Tests RunLoopQuit only quits the corresponding MessageLoop::Run. -TEST_P(MessageLoopTypedTest, RunLoopQuitDeep) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - RunLoop outer_run_loop; - RunLoop nested_loop1; - RunLoop nested_loop2; - RunLoop nested_loop3; - RunLoop nested_loop4; - - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_loop1))); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&FuncThatRuns, &order, 2, Unretained(&nested_loop2))); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&FuncThatRuns, &order, 3, Unretained(&nested_loop3))); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&FuncThatRuns, &order, 4, Unretained(&nested_loop4))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 5)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - outer_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 6)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_loop1.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 7)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_loop2.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 8)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_loop3.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 9)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_loop4.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 10)); - - outer_run_loop.Run(); - - ASSERT_EQ(18U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(static_cast(task_index), order.Size()); -} - -// Tests RunLoopQuit works before RunWithID. -TEST_P(MessageLoopTypedTest, RunLoopQuitOrderBefore) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - RunLoop run_loop; - - run_loop.Quit(); - - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&OrderedFunc, &order, 1)); // never runs - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&FuncThatQuitsNow)); // never runs - - run_loop.Run(); - - ASSERT_EQ(0U, order.Size()); -} - -// Tests RunLoopQuit works during RunWithID. -TEST_P(MessageLoopTypedTest, RunLoopQuitOrderDuring) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - RunLoop run_loop; - - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 1)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); // never runs - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&FuncThatQuitsNow)); // never runs - - run_loop.Run(); - - ASSERT_EQ(2U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, false)); - EXPECT_EQ(static_cast(task_index), order.Size()); -} - -// Tests RunLoopQuit works after RunWithID. -TEST_P(MessageLoopTypedTest, RunLoopQuitOrderAfter) { - MessageLoop loop(GetMessageLoopType()); - - TaskList order; - - RunLoop run_loop; - - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&run_loop))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&FuncThatQuitsNow)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 3)); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, run_loop.QuitClosure()); // has no affect - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 4)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&FuncThatQuitsNow)); - - RunLoop outer_run_loop; - outer_run_loop.Run(); - - ASSERT_EQ(8U, order.Size()); - int task_index = 0; - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, true)); - EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, false)); - EXPECT_EQ(static_cast(task_index), order.Size()); -} - -// There was a bug in the MessagePumpGLib where posting tasks recursively -// caused the message loop to hang, due to the buffer of the internal pipe -// becoming full. Test all MessageLoop types to ensure this issue does not -// exist in other MessagePumps. -// -// On Linux, the pipe buffer size is 64KiB by default. The bug caused one -// byte accumulated in the pipe per two posts, so we should repeat 128K -// times to reproduce the bug. -#if defined(OS_FUCHSIA) -// TODO(crbug.com/810077): This is flaky on Fuchsia. -#define MAYBE_RecursivePosts DISABLED_RecursivePosts -#else -#define MAYBE_RecursivePosts RecursivePosts -#endif -TEST_P(MessageLoopTypedTest, MAYBE_RecursivePosts) { - const int kNumTimes = 1 << 17; - MessageLoop loop(GetMessageLoopType()); - loop.task_runner()->PostTask(FROM_HERE, - BindOnce(&PostNTasksThenQuit, kNumTimes)); - RunLoop().Run(); -} - -TEST_P(MessageLoopTypedTest, NestableTasksAllowedAtTopLevel) { - MessageLoop loop(GetMessageLoopType()); - EXPECT_TRUE(MessageLoopCurrent::Get()->NestableTasksAllowed()); -} - -// Nestable tasks shouldn't be allowed to run reentrantly by default (regression -// test for https://crbug.com/754112). -TEST_P(MessageLoopTypedTest, NestableTasksDisallowedByDefault) { - MessageLoop loop(GetMessageLoopType()); - RunLoop run_loop; - loop.task_runner()->PostTask( - FROM_HERE, - BindOnce( - [](RunLoop* run_loop) { - EXPECT_FALSE(MessageLoopCurrent::Get()->NestableTasksAllowed()); - run_loop->Quit(); - }, - Unretained(&run_loop))); - run_loop.Run(); -} - -TEST_P(MessageLoopTypedTest, NestableTasksProcessedWhenRunLoopAllows) { - MessageLoop loop(GetMessageLoopType()); - RunLoop run_loop; - loop.task_runner()->PostTask( - FROM_HERE, - BindOnce( - [](RunLoop* run_loop) { - // This test would hang if this RunLoop wasn't of type - // kNestableTasksAllowed (i.e. this is testing that this is - // processed and doesn't hang). - RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - BindOnce( - [](RunLoop* nested_run_loop) { - // Each additional layer of application task nesting - // requires its own allowance. The kNestableTasksAllowed - // RunLoop allowed this task to be processed but further - // nestable tasks are by default disallowed from this - // layer. - EXPECT_FALSE( - MessageLoopCurrent::Get()->NestableTasksAllowed()); - nested_run_loop->Quit(); - }, - Unretained(&nested_run_loop))); - nested_run_loop.Run(); - - run_loop->Quit(); - }, - Unretained(&run_loop))); - run_loop.Run(); -} - -TEST_P(MessageLoopTypedTest, NestableTasksAllowedExplicitlyInScope) { - MessageLoop loop(GetMessageLoopType()); - RunLoop run_loop; - loop.task_runner()->PostTask( - FROM_HERE, - BindOnce( - [](RunLoop* run_loop) { - { - MessageLoopCurrent::ScopedNestableTaskAllower - allow_nestable_tasks; - EXPECT_TRUE(MessageLoopCurrent::Get()->NestableTasksAllowed()); - } - EXPECT_FALSE(MessageLoopCurrent::Get()->NestableTasksAllowed()); - run_loop->Quit(); - }, - Unretained(&run_loop))); - run_loop.Run(); -} - -TEST_P(MessageLoopTypedTest, NestableTasksAllowedManually) { - MessageLoop loop(GetMessageLoopType()); - RunLoop run_loop; - loop.task_runner()->PostTask( - FROM_HERE, - BindOnce( - [](RunLoop* run_loop) { - EXPECT_FALSE(MessageLoopCurrent::Get()->NestableTasksAllowed()); - MessageLoopCurrent::Get()->SetNestableTasksAllowed(true); - EXPECT_TRUE(MessageLoopCurrent::Get()->NestableTasksAllowed()); - MessageLoopCurrent::Get()->SetNestableTasksAllowed(false); - EXPECT_FALSE(MessageLoopCurrent::Get()->NestableTasksAllowed()); - run_loop->Quit(); - }, - Unretained(&run_loop))); - run_loop.Run(); -} - -INSTANTIATE_TEST_CASE_P( - , - MessageLoopTypedTest, - ::testing::Values(MessageLoopTypedTestParams( - MessageLoop::TYPE_DEFAULT, - TaskSchedulerAvailability::NO_TASK_SCHEDULER), - MessageLoopTypedTestParams( - MessageLoop::TYPE_IO, - TaskSchedulerAvailability::NO_TASK_SCHEDULER), - MessageLoopTypedTestParams( - MessageLoop::TYPE_UI, - TaskSchedulerAvailability::NO_TASK_SCHEDULER), - MessageLoopTypedTestParams( - MessageLoop::TYPE_DEFAULT, - TaskSchedulerAvailability::WITH_TASK_SCHEDULER), - MessageLoopTypedTestParams( - MessageLoop::TYPE_IO, - TaskSchedulerAvailability::WITH_TASK_SCHEDULER), - MessageLoopTypedTestParams( - MessageLoop::TYPE_UI, - TaskSchedulerAvailability::WITH_TASK_SCHEDULER)), - MessageLoopTypedTest::ParamInfoToString); - -#if defined(OS_WIN) -// Verifies that the MessageLoop ignores WM_QUIT, rather than quitting. -// Users of MessageLoop typically expect to control when their RunLoops stop -// Run()ning explicitly, via QuitClosure() etc (see https://crbug.com/720078) -TEST_P(MessageLoopTest, WmQuitIsIgnored) { - MessageLoop loop(MessageLoop::TYPE_UI); - - // Post a WM_QUIT message to the current thread. - ::PostQuitMessage(0); - - // Post a task to the current thread, with a small delay to make it less - // likely that we process the posted task before looking for WM_* messages. - bool task_was_run = false; - RunLoop run_loop; - loop.task_runner()->PostDelayedTask( - FROM_HERE, - BindOnce( - [](bool* flag, OnceClosure closure) { - *flag = true; - std::move(closure).Run(); - }, - &task_was_run, run_loop.QuitClosure()), - TestTimeouts::tiny_timeout()); - - // Run the loop, and ensure that the posted task is processed before we quit. - run_loop.Run(); - EXPECT_TRUE(task_was_run); -} - -TEST_P(MessageLoopTest, WmQuitIsNotIgnoredWithEnableWmQuit) { - MessageLoop loop(MessageLoop::TYPE_UI); - static_cast(&loop)->EnableWmQuit(); - - // Post a WM_QUIT message to the current thread. - ::PostQuitMessage(0); - - // Post a task to the current thread, with a small delay to make it less - // likely that we process the posted task before looking for WM_* messages. - RunLoop run_loop; - loop.task_runner()->PostDelayedTask(FROM_HERE, - BindOnce( - [](OnceClosure closure) { - ADD_FAILURE(); - std::move(closure).Run(); - }, - run_loop.QuitClosure()), - TestTimeouts::tiny_timeout()); - - // Run the loop. It should not result in ADD_FAILURE() getting called. - run_loop.Run(); -} - -TEST_P(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) { - RunTest_PostDelayedTask_SharedTimer_SubPump(); -} - -// This test occasionally hangs. See http://crbug.com/44567. -TEST_P(MessageLoopTest, DISABLED_RecursiveDenial2) { - RunTest_RecursiveDenial2(MessageLoop::TYPE_DEFAULT); - RunTest_RecursiveDenial2(MessageLoop::TYPE_UI); - RunTest_RecursiveDenial2(MessageLoop::TYPE_IO); -} - -TEST_P(MessageLoopTest, RecursiveSupport2) { - // This test requires a UI loop. - RunTest_RecursiveSupport2(MessageLoop::TYPE_UI); -} -#endif // defined(OS_WIN) - -TEST_P(MessageLoopTest, TaskObserver) { - const int kNumPosts = 6; - DummyTaskObserver observer(kNumPosts); - - MessageLoop loop; - loop.AddTaskObserver(&observer); - loop.task_runner()->PostTask(FROM_HERE, - BindOnce(&PostNTasksThenQuit, kNumPosts)); - RunLoop().Run(); - loop.RemoveTaskObserver(&observer); - - EXPECT_EQ(kNumPosts, observer.num_tasks_started()); - EXPECT_EQ(kNumPosts, observer.num_tasks_processed()); -} - -#if defined(OS_WIN) -TEST_P(MessageLoopTest, IOHandler) { - RunTest_IOHandler(); -} - -TEST_P(MessageLoopTest, WaitForIO) { - RunTest_WaitForIO(); -} - -TEST_P(MessageLoopTest, HighResolutionTimer) { - Thread verification_thread("verification thread"); - verification_thread.StartAndWaitForTesting(); - - MessageLoop message_loop; - Time::EnableHighResolutionTimer(true); - - constexpr TimeDelta kFastTimer = TimeDelta::FromMilliseconds(31); - constexpr TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100); - - // Since MessageLoop disables its vote to activate high-resolution timers the - // instant it's active, the high-resolution timer's activation can only be - // tested async. Try to make this as reliable as possible by verifying in the - // middle of the MessageLoop's sleep period (giving plenty of time before for - // it to establish the high-res timer and plenty of time after for it not to - // wakeup while verifying). - constexpr TimeDelta kVerificationDelay = kFastTimer / 2; - - { - // Post a fast task to enable the high resolution timers. - RunLoop run_loop; - message_loop.task_runner()->PostDelayedTask( - FROM_HERE, BindLambdaForTesting([&]() { - // The loop deactivates high resolution timers the instant it's - // active. - EXPECT_FALSE(Time::IsHighResolutionTimerInUse()); - run_loop.Quit(); - }), - kFastTimer); - - // Ensures the verification runs (that thread could theoretically never be - // scheduled -- skipping the test). - AtomicFlag verification_ran; - verification_thread.task_runner()->PostDelayedTask( - FROM_HERE, BindLambdaForTesting([&]() { - EXPECT_TRUE(Time::IsHighResolutionTimerInUse()); - verification_ran.Set(); - }), - kVerificationDelay); - - run_loop.Run(); - ASSERT_TRUE(verification_ran.IsSet()); - } - EXPECT_FALSE(Time::IsHighResolutionTimerInUse()); - { - // Check that a slow task does not trigger the high resolution logic. - RunLoop run_loop; - message_loop.task_runner()->PostDelayedTask( - FROM_HERE, BindLambdaForTesting([&]() { - // The loop deactivates high resolution timers the instant it's - // active. - EXPECT_FALSE(Time::IsHighResolutionTimerInUse()); - run_loop.Quit(); - }), - kSlowTimer); - - AtomicFlag verification_ran; - verification_thread.task_runner()->PostDelayedTask( - FROM_HERE, BindLambdaForTesting([&]() { - EXPECT_FALSE(Time::IsHighResolutionTimerInUse()); - verification_ran.Set(); - }), - kVerificationDelay); - - run_loop.Run(); - ASSERT_TRUE(verification_ran.IsSet()); - } - Time::EnableHighResolutionTimer(false); - Time::ResetHighResolutionTimerUsage(); -} - -#endif // defined(OS_WIN) - -namespace { -// Inject a test point for recording the destructor calls for Closure objects -// send to MessageLoop::PostTask(). It is awkward usage since we are trying to -// hook the actual destruction, which is not a common operation. -class DestructionObserverProbe : - public RefCounted { - public: - DestructionObserverProbe(bool* task_destroyed, - bool* destruction_observer_called) - : task_destroyed_(task_destroyed), - destruction_observer_called_(destruction_observer_called) { - } - virtual void Run() { - // This task should never run. - ADD_FAILURE(); - } - private: - friend class RefCounted; - - virtual ~DestructionObserverProbe() { - EXPECT_FALSE(*destruction_observer_called_); - *task_destroyed_ = true; - } - - bool* task_destroyed_; - bool* destruction_observer_called_; -}; - -class MLDestructionObserver : public MessageLoopCurrent::DestructionObserver { - public: - MLDestructionObserver(bool* task_destroyed, bool* destruction_observer_called) - : task_destroyed_(task_destroyed), - destruction_observer_called_(destruction_observer_called), - task_destroyed_before_message_loop_(false) { - } - void WillDestroyCurrentMessageLoop() override { - task_destroyed_before_message_loop_ = *task_destroyed_; - *destruction_observer_called_ = true; - } - bool task_destroyed_before_message_loop() const { - return task_destroyed_before_message_loop_; - } - private: - bool* task_destroyed_; - bool* destruction_observer_called_; - bool task_destroyed_before_message_loop_; -}; - -} // namespace - -TEST_P(MessageLoopTest, DestructionObserverTest) { - // Verify that the destruction observer gets called at the very end (after - // all the pending tasks have been destroyed). - MessageLoop* loop = new MessageLoop; - const TimeDelta kDelay = TimeDelta::FromMilliseconds(100); - - bool task_destroyed = false; - bool destruction_observer_called = false; - - MLDestructionObserver observer(&task_destroyed, &destruction_observer_called); - loop->AddDestructionObserver(&observer); - loop->task_runner()->PostDelayedTask( - FROM_HERE, - BindOnce(&DestructionObserverProbe::Run, - new DestructionObserverProbe(&task_destroyed, - &destruction_observer_called)), - kDelay); - delete loop; - EXPECT_TRUE(observer.task_destroyed_before_message_loop()); - // The task should have been destroyed when we deleted the loop. - EXPECT_TRUE(task_destroyed); - EXPECT_TRUE(destruction_observer_called); -} - - -// Verify that MessageLoop sets ThreadMainTaskRunner::current() and it -// posts tasks on that message loop. -TEST_P(MessageLoopTest, ThreadMainTaskRunner) { - MessageLoop loop; - - scoped_refptr foo(new Foo()); - std::string a("a"); - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a)); - - // Post quit task; - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&RunLoop::QuitCurrentWhenIdleDeprecated)); - - // Now kick things off - RunLoop().Run(); - - EXPECT_EQ(foo->test_count(), 1); - EXPECT_EQ(foo->result(), "a"); -} - -TEST_P(MessageLoopTest, IsType) { - MessageLoop loop(MessageLoop::TYPE_UI); - EXPECT_TRUE(loop.IsType(MessageLoop::TYPE_UI)); - EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_IO)); - EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_DEFAULT)); -} - -#if defined(OS_WIN) -void EmptyFunction() {} - -void PostMultipleTasks() { - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::BindOnce(&EmptyFunction)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::BindOnce(&EmptyFunction)); -} - -static const int kSignalMsg = WM_USER + 2; - -void PostWindowsMessage(HWND message_hwnd) { - PostMessage(message_hwnd, kSignalMsg, 0, 2); -} - -void EndTest(bool* did_run, HWND hwnd) { - *did_run = true; - PostMessage(hwnd, WM_CLOSE, 0, 0); -} - -int kMyMessageFilterCode = 0x5002; - -LRESULT CALLBACK TestWndProcThunk(HWND hwnd, UINT message, - WPARAM wparam, LPARAM lparam) { - if (message == WM_CLOSE) - EXPECT_TRUE(DestroyWindow(hwnd)); - if (message != kSignalMsg) - return DefWindowProc(hwnd, message, wparam, lparam); - - switch (lparam) { - case 1: - // First, we post a task that will post multiple no-op tasks to make sure - // that the pump's incoming task queue does not become empty during the - // test. - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::BindOnce(&PostMultipleTasks)); - // Next, we post a task that posts a windows message to trigger the second - // stage of the test. - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&PostWindowsMessage, hwnd)); - break; - case 2: - // Since we're about to enter a modal loop, tell the message loop that we - // intend to nest tasks. - MessageLoopCurrent::Get()->SetNestableTasksAllowed(true); - bool did_run = false; - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&EndTest, &did_run, hwnd)); - // Run a nested windows-style message loop and verify that our task runs. If - // it doesn't, then we'll loop here until the test times out. - MSG msg; - while (GetMessage(&msg, 0, 0, 0)) { - if (!CallMsgFilter(&msg, kMyMessageFilterCode)) - DispatchMessage(&msg); - // If this message is a WM_CLOSE, explicitly exit the modal loop. Posting - // a WM_QUIT should handle this, but unfortunately MessagePumpWin eats - // WM_QUIT messages even when running inside a modal loop. - if (msg.message == WM_CLOSE) - break; - } - EXPECT_TRUE(did_run); - RunLoop::QuitCurrentWhenIdleDeprecated(); - break; - } - return 0; -} - -TEST_P(MessageLoopTest, AlwaysHaveUserMessageWhenNesting) { - MessageLoop loop(MessageLoop::TYPE_UI); - HINSTANCE instance = CURRENT_MODULE(); - WNDCLASSEX wc = {0}; - wc.cbSize = sizeof(wc); - wc.lpfnWndProc = TestWndProcThunk; - wc.hInstance = instance; - wc.lpszClassName = L"MessageLoopTest_HWND"; - ATOM atom = RegisterClassEx(&wc); - ASSERT_TRUE(atom); - - HWND message_hwnd = CreateWindow(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0, - HWND_MESSAGE, 0, instance, 0); - ASSERT_TRUE(message_hwnd) << GetLastError(); - - ASSERT_TRUE(PostMessage(message_hwnd, kSignalMsg, 0, 1)); - - RunLoop().Run(); - - ASSERT_TRUE(UnregisterClass(MAKEINTATOM(atom), instance)); -} -#endif // defined(OS_WIN) - -TEST_P(MessageLoopTest, SetTaskRunner) { - MessageLoop loop; - scoped_refptr new_runner(new TestSimpleTaskRunner()); - - loop.SetTaskRunner(new_runner); - EXPECT_EQ(new_runner, loop.task_runner()); - EXPECT_EQ(new_runner, ThreadTaskRunnerHandle::Get()); -} - -TEST_P(MessageLoopTest, OriginalRunnerWorks) { - MessageLoop loop; - scoped_refptr new_runner(new TestSimpleTaskRunner()); - scoped_refptr original_runner(loop.task_runner()); - loop.SetTaskRunner(new_runner); - - scoped_refptr foo(new Foo()); - original_runner->PostTask(FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, "a")); - RunLoop().RunUntilIdle(); - EXPECT_EQ(1, foo->test_count()); -} - -TEST_P(MessageLoopTest, DeleteUnboundLoop) { - // It should be possible to delete an unbound message loop on a thread which - // already has another active loop. This happens when thread creation fails. - MessageLoop loop; - std::unique_ptr unbound_loop(MessageLoop::CreateUnbound( - MessageLoop::TYPE_DEFAULT, MessageLoop::MessagePumpFactoryCallback())); - unbound_loop.reset(); - EXPECT_EQ(&loop, MessageLoop::current()); - EXPECT_EQ(loop.task_runner(), ThreadTaskRunnerHandle::Get()); -} - -TEST_P(MessageLoopTest, ThreadName) { - { - std::string kThreadName("foo"); - MessageLoop loop; - PlatformThread::SetName(kThreadName); - EXPECT_EQ(kThreadName, loop.GetThreadName()); - } - - { - std::string kThreadName("bar"); - base::Thread thread(kThreadName); - ASSERT_TRUE(thread.StartAndWaitForTesting()); - EXPECT_EQ(kThreadName, thread.message_loop()->GetThreadName()); - } -} - -// Verify that tasks posted to and code running in the scope of the same -// MessageLoop access the same SequenceLocalStorage values. -TEST_P(MessageLoopTest, SequenceLocalStorageSetGet) { - MessageLoop loop; - - SequenceLocalStorageSlot slot; - - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - BindOnce(&SequenceLocalStorageSlot::Set, Unretained(&slot), 11)); - - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce( - [](SequenceLocalStorageSlot* slot) { - EXPECT_EQ(slot->Get(), 11); - }, - &slot)); - - RunLoop().RunUntilIdle(); - EXPECT_EQ(slot.Get(), 11); -} - -// Verify that tasks posted to and code running in different MessageLoops access -// different SequenceLocalStorage values. -TEST_P(MessageLoopTest, SequenceLocalStorageDifferentMessageLoops) { - SequenceLocalStorageSlot slot; - - { - MessageLoop loop; - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - BindOnce(&SequenceLocalStorageSlot::Set, Unretained(&slot), 11)); - - RunLoop().RunUntilIdle(); - EXPECT_EQ(slot.Get(), 11); - } - - MessageLoop loop; - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce( - [](SequenceLocalStorageSlot* slot) { - EXPECT_NE(slot->Get(), 11); - }, - &slot)); - - RunLoop().RunUntilIdle(); - EXPECT_NE(slot.Get(), 11); -} - -INSTANTIATE_TEST_CASE_P( - , - MessageLoopTest, - ::testing::Values(TaskSchedulerAvailability::NO_TASK_SCHEDULER, - TaskSchedulerAvailability::WITH_TASK_SCHEDULER), - MessageLoopTest::ParamInfoToString); - -namespace { - -class PostTaskOnDestroy { - public: - PostTaskOnDestroy(int times) : times_remaining_(times) {} - ~PostTaskOnDestroy() { PostTaskWithPostingDestructor(times_remaining_); } - - // Post a task that will repost itself on destruction |times| times. - static void PostTaskWithPostingDestructor(int times) { - if (times > 0) { - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce([](std::unique_ptr) {}, - std::make_unique(times - 1))); - } - } - - private: - const int times_remaining_; - - DISALLOW_COPY_AND_ASSIGN(PostTaskOnDestroy); -}; - -} // namespace - -// Test that MessageLoop destruction handles a task's destructor posting another -// task by: -// 1) Not getting stuck clearing its task queue. -// 2) DCHECKing when clearing pending tasks many times still doesn't yield an -// empty queue. -TEST(MessageLoopDestructionTest, ExpectDeathWithStubbornPostTaskOnDestroy) { - std::unique_ptr loop = std::make_unique(); - - EXPECT_DCHECK_DEATH({ - PostTaskOnDestroy::PostTaskWithPostingDestructor(1000); - loop.reset(); - }); -} - -TEST(MessageLoopDestructionTest, DestroysFineWithReasonablePostTaskOnDestroy) { - std::unique_ptr loop = std::make_unique(); - - PostTaskOnDestroy::PostTaskWithPostingDestructor(10); - loop.reset(); -} - -} // namespace base diff --git a/message_loop/message_pump.cc b/message_loop/message_pump.cc deleted file mode 100644 index 907617624..000000000 --- a/message_loop/message_pump.cc +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/message_loop/message_pump.h" - -namespace base { - -MessagePump::MessagePump() = default; - -MessagePump::~MessagePump() = default; - -void MessagePump::SetTimerSlack(TimerSlack) { -} - -} // namespace base diff --git a/message_loop/message_pump.h b/message_loop/message_pump.h deleted file mode 100644 index dec0c94f6..000000000 --- a/message_loop/message_pump.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_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_ - -#include "base/base_export.h" -#include "base/message_loop/timer_slack.h" -#include "base/sequence_checker.h" - -namespace base { - -class TimeTicks; - -class BASE_EXPORT MessagePump { - public: - // Please see the comments above the Run method for an illustration of how - // these delegate methods are used. - class BASE_EXPORT Delegate { - public: - virtual ~Delegate() = default; - - // Called from within Run in response to ScheduleWork or when the message - // pump would otherwise call DoDelayedWork. Returns true to indicate that - // work was done. DoDelayedWork will still be called if DoWork returns - // true, but DoIdleWork will not. - virtual bool DoWork() = 0; - - // Called from within Run in response to ScheduleDelayedWork or when the - // message pump would otherwise sleep waiting for more work. Returns true - // to indicate that delayed work was done. DoIdleWork will not be called - // if DoDelayedWork returns true. Upon return |next_delayed_work_time| - // indicates the time when DoDelayedWork should be called again. If - // |next_delayed_work_time| is null (per Time::is_null), then the queue of - // future delayed work (timer events) is currently empty, and no additional - // calls to this function need to be scheduled. - virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) = 0; - - // Called from within Run just before the message pump goes to sleep. - // Returns true to indicate that idle work was done. Returning false means - // the pump will now wait. - virtual bool DoIdleWork() = 0; - }; - - MessagePump(); - virtual ~MessagePump(); - - // The Run method is called to enter the message pump's run loop. - // - // Within the method, the message pump is responsible for processing native - // messages as well as for giving cycles to the delegate periodically. The - // message pump should take care to mix delegate callbacks with native - // message processing so neither type of event starves the other of cycles. - // - // The anatomy of a typical run loop: - // - // for (;;) { - // bool did_work = DoInternalWork(); - // if (should_quit_) - // break; - // - // did_work |= delegate_->DoWork(); - // if (should_quit_) - // break; - // - // TimeTicks next_time; - // did_work |= delegate_->DoDelayedWork(&next_time); - // if (should_quit_) - // break; - // - // if (did_work) - // continue; - // - // did_work = delegate_->DoIdleWork(); - // if (should_quit_) - // break; - // - // if (did_work) - // continue; - // - // WaitForWork(); - // } - // - // Here, DoInternalWork is some private method of the message pump that is - // responsible for dispatching the next UI message or notifying the next IO - // completion (for example). WaitForWork is a private method that simply - // blocks until there is more work of any type to do. - // - // Notice that the run loop cycles between calling DoInternalWork, DoWork, - // and DoDelayedWork methods. This helps ensure that none of these work - // queues starve the others. This is important for message pumps that are - // used to drive animations, for example. - // - // Notice also that after each callout to foreign code, the run loop checks - // to see if it should quit. The Quit method is responsible for setting this - // flag. No further work is done once the quit flag is set. - // - // NOTE: Care must be taken to handle Run being called again from within any - // of the callouts to foreign code. Native message pumps may also need to - // deal with other native message pumps being run outside their control - // (e.g., the MessageBox API on Windows pumps UI messages!). To be specific, - // the callouts (DoWork and DoDelayedWork) MUST still be provided even in - // nested sub-loops that are "seemingly" outside the control of this message - // pump. DoWork in particular must never be starved for time slices unless - // it returns false (meaning it has run out of things to do). - // - virtual void Run(Delegate* delegate) = 0; - - // Quit immediately from the most recently entered run loop. This method may - // only be used on the thread that called Run. - virtual void Quit() = 0; - - // Schedule a DoWork callback to happen reasonably soon. Does nothing if a - // DoWork callback is already scheduled. This method may be called from any - // thread. Once this call is made, DoWork should not be "starved" at least - // until it returns a value of false. - virtual void ScheduleWork() = 0; - - // Schedule a DoDelayedWork callback to happen at the specified time, - // 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 - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_ diff --git a/message_loop/message_pump_android.cc b/message_loop/message_pump_android.cc deleted file mode 100644 index 3fd5567e6..000000000 --- a/message_loop/message_pump_android.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_android.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "base/android/jni_android.h" -#include "base/android/scoped_java_ref.h" -#include "base/callback_helpers.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/run_loop.h" - -// Android stripped sys/timerfd.h out of their platform headers, so we have to -// use syscall to make use of timerfd. Once the min API level is 20, we can -// directly use timerfd.h. -#ifndef __NR_timerfd_create -#error "Unable to find syscall for __NR_timerfd_create" -#endif - -#ifndef TFD_TIMER_ABSTIME -#define TFD_TIMER_ABSTIME (1 << 0) -#endif - -using base::android::JavaParamRef; -using base::android::ScopedJavaLocalRef; - -namespace base { - -namespace { - -// See sys/timerfd.h -int timerfd_create(int clockid, int flags) { - return syscall(__NR_timerfd_create, clockid, flags); -} - -// See sys/timerfd.h -int timerfd_settime(int ufc, - int flags, - const struct itimerspec* utmr, - struct itimerspec* otmr) { - return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr); -} - -int NonDelayedLooperCallback(int fd, int events, void* data) { - if (events & ALOOPER_EVENT_HANGUP) - return 0; - - DCHECK(events & ALOOPER_EVENT_INPUT); - MessagePumpForUI* pump = reinterpret_cast(data); - pump->OnNonDelayedLooperCallback(); - return 1; // continue listening for events -} - -int DelayedLooperCallback(int fd, int events, void* data) { - if (events & ALOOPER_EVENT_HANGUP) - return 0; - - DCHECK(events & ALOOPER_EVENT_INPUT); - MessagePumpForUI* pump = reinterpret_cast(data); - pump->OnDelayedLooperCallback(); - return 1; // continue listening for events -} - -} // namespace - -MessagePumpForUI::MessagePumpForUI() { - // The Android native ALooper uses epoll to poll our file descriptors and wake - // us up. We use a simple level-triggered eventfd to signal that non-delayed - // work is available, and a timerfd to signal when delayed work is ready to - // be run. - non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); - CHECK_NE(non_delayed_fd_, -1); - DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC); - - // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't - // include timerfd.h. See comments above on __NR_timerfd_create. It looks like - // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be - // fine. - delayed_fd_ = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC); - CHECK_NE(delayed_fd_, -1); - - looper_ = ALooper_prepare(0); - DCHECK(looper_); - // Add a reference to the looper so it isn't deleted on us. - ALooper_acquire(looper_); - ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT, - &NonDelayedLooperCallback, reinterpret_cast(this)); - ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT, - &DelayedLooperCallback, reinterpret_cast(this)); -} - -MessagePumpForUI::~MessagePumpForUI() { - DCHECK_EQ(ALooper_forThread(), looper_); - ALooper_removeFd(looper_, non_delayed_fd_); - ALooper_removeFd(looper_, delayed_fd_); - ALooper_release(looper_); - looper_ = nullptr; - - close(non_delayed_fd_); - close(delayed_fd_); -} - -void MessagePumpForUI::OnDelayedLooperCallback() { - if (ShouldQuit()) - return; - - // Clear the fd. - uint64_t value; - int ret = read(delayed_fd_, &value, sizeof(value)); - DCHECK_GE(ret, 0); - delayed_scheduled_time_ = base::TimeTicks(); - - base::TimeTicks next_delayed_work_time; - delegate_->DoDelayedWork(&next_delayed_work_time); - if (!next_delayed_work_time.is_null()) { - ScheduleDelayedWork(next_delayed_work_time); - } - if (ShouldQuit()) - return; - // We may be idle now, so pump the loop to find out. - ScheduleWork(); -} - -void MessagePumpForUI::OnNonDelayedLooperCallback() { - base::TimeTicks next_delayed_work_time; - bool did_any_work = false; - - // Runs all native tasks scheduled to run, scheduling delayed work if - // necessary. - while (true) { - bool did_work_this_loop = false; - if (ShouldQuit()) - return; - did_work_this_loop = delegate_->DoWork(); - if (ShouldQuit()) - return; - - did_work_this_loop |= delegate_->DoDelayedWork(&next_delayed_work_time); - - did_any_work |= did_work_this_loop; - - // If we didn't do any work, we're out of native tasks to run, and we should - // return control to the looper to run Java tasks. - if (!did_work_this_loop) - break; - } - // If we did any work, return control to the looper to run java tasks before - // we call DoIdleWork(). We haven't cleared the fd yet, so we'll get woken up - // again soon to check for idle-ness. - if (did_any_work) - return; - if (ShouldQuit()) - return; - - // Read the file descriptor, resetting its contents to 0 and reading back the - // stored value. - // See http://man7.org/linux/man-pages/man2/eventfd.2.html - uint64_t value = 0; - int ret = read(non_delayed_fd_, &value, sizeof(value)); - DCHECK_GE(ret, 0); - - // If we read a value > 1, it means we lost the race to clear the fd before a - // new task was posted. This is okay, we can just re-schedule work. - if (value > 1) { - ScheduleWork(); - } else { - // At this point, the java looper might not be idle - it's impossible to - // know pre-Android-M, so we may end up doing Idle work while java tasks are - // still queued up. Note that this won't cause us to fail to run java tasks - // using QuitWhenIdle, as the JavaHandlerThread will finish running all - // currently scheduled tasks before it quits. Also note that we can't just - // add an idle callback to the java looper, as that will fire even if native - // tasks are still queued up. - DoIdleWork(); - if (!next_delayed_work_time.is_null()) { - ScheduleDelayedWork(next_delayed_work_time); - } - } -} - -void MessagePumpForUI::DoIdleWork() { - if (delegate_->DoIdleWork()) { - // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump - // the loop here because we may in fact be idle after doing idle work - // without any new tasks being queued. - ScheduleWork(); - } -} - -void MessagePumpForUI::Run(Delegate* delegate) { - DCHECK(IsTestImplementation()); - // This function is only called in tests. We manually pump the native looper - // which won't run any java tasks. - quit_ = false; - - SetDelegate(delegate); - - // Pump the loop once in case we're starting off idle as ALooper_pollOnce will - // never return in that case. - ScheduleWork(); - while (true) { - // Waits for either the delayed, or non-delayed fds to be signalled, calling - // either OnDelayedLooperCallback, or OnNonDelayedLooperCallback, - // respectively. This uses Android's Looper implementation, which is based - // off of epoll. - ALooper_pollOnce(-1, nullptr, nullptr, nullptr); - if (quit_) - break; - } -} - -void MessagePumpForUI::Attach(Delegate* delegate) { - DCHECK(!quit_); - - // Since the Looper is controlled by the UI thread or JavaHandlerThread, we - // can't use Run() like we do on other platforms or we would prevent Java - // tasks from running. Instead we create and initialize a run loop here, then - // return control back to the Looper. - - SetDelegate(delegate); - run_loop_ = std::make_unique(); - // Since the RunLoop was just created above, BeforeRun should be guaranteed to - // return true (it only returns false if the RunLoop has been Quit already). - if (!run_loop_->BeforeRun()) - NOTREACHED(); -} - -void MessagePumpForUI::Quit() { - if (quit_) - return; - - quit_ = true; - - int64_t value; - // Clear any pending timer. - read(delayed_fd_, &value, sizeof(value)); - // Clear the eventfd. - read(non_delayed_fd_, &value, sizeof(value)); - - if (run_loop_) { - run_loop_->AfterRun(); - run_loop_ = nullptr; - } - if (on_quit_callback_) { - std::move(on_quit_callback_).Run(); - } -} - -void MessagePumpForUI::ScheduleWork() { - if (ShouldQuit()) - return; - - // Write (add) 1 to the eventfd. This tells the Looper to wake up and call our - // callback, allowing us to run tasks. This also allows us to detect, when we - // clear the fd, whether additional work was scheduled after we finished - // performing work, but before we cleared the fd, as we'll read back >=2 - // instead of 1 in that case. - // See the eventfd man pages - // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how - // the read and write APIs for this file descriptor work, specifically without - // EFD_SEMAPHORE. - uint64_t value = 1; - int ret = write(non_delayed_fd_, &value, sizeof(value)); - DCHECK_GE(ret, 0); -} - -void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { - if (ShouldQuit()) - return; - - if (!delayed_scheduled_time_.is_null() && - delayed_work_time >= delayed_scheduled_time_) { - return; - } - - DCHECK(!delayed_work_time.is_null()); - delayed_scheduled_time_ = delayed_work_time; - int64_t nanos = delayed_work_time.since_origin().InNanoseconds(); - struct itimerspec ts; - ts.it_interval.tv_sec = 0; // Don't repeat. - ts.it_interval.tv_nsec = 0; - ts.it_value.tv_sec = nanos / TimeTicks::kNanosecondsPerSecond; - ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond; - - int ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr); - DCHECK_GE(ret, 0); -} - -void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) { - DCHECK(!on_quit_callback_); - DCHECK(run_loop_); - on_quit_callback_ = std::move(callback); - run_loop_->QuitWhenIdle(); - // Pump the loop in case we're already idle. - ScheduleWork(); -} - -bool MessagePumpForUI::IsTestImplementation() const { - return false; -} - -} // namespace base diff --git a/message_loop/message_pump_android.h b/message_loop/message_pump_android.h deleted file mode 100644 index d7e0f50fd..000000000 --- a/message_loop/message_pump_android.h +++ /dev/null @@ -1,102 +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_ANDROID_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_ - -#include -#include - -#include "base/android/scoped_java_ref.h" -#include "base/base_export.h" -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/message_loop/message_pump.h" -#include "base/time/time.h" - -struct ALooper; - -namespace base { - -class RunLoop; - -// This class implements a MessagePump needed for TYPE_UI MessageLoops on -// OS_ANDROID platform. -class BASE_EXPORT MessagePumpForUI : public MessagePump { - public: - MessagePumpForUI(); - ~MessagePumpForUI() override; - - void Run(Delegate* delegate) override; - void Quit() override; - void ScheduleWork() override; - void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; - - // Attaches |delegate| to this native MessagePump. |delegate| will from then - // on be invoked by the native loop to process application tasks. - virtual void Attach(Delegate* delegate); - - // We call Abort when there is a pending JNI exception, meaning that the - // current thread will crash when we return to Java. - // We can't call any JNI-methods before returning to Java as we would then - // cause a native crash (instead of the original Java crash). - void Abort() { should_abort_ = true; } - bool IsAborted() { return should_abort_; } - bool ShouldQuit() const { return should_abort_ || quit_; } - - // Tells the RunLoop to quit when idle, calling the callback when it's safe - // for the Thread to stop. - void QuitWhenIdle(base::OnceClosure callback); - - // These functions are only public so that the looper callbacks can call them, - // and should not be called from outside this class. - void OnDelayedLooperCallback(); - void OnNonDelayedLooperCallback(); - - protected: - void SetDelegate(Delegate* delegate) { delegate_ = delegate; } - virtual bool IsTestImplementation() const; - - private: - void DoIdleWork(); - - // Unlike other platforms, we don't control the message loop as it's - // controlled by the Android Looper, so we can't run a RunLoop to keep the - // Thread this pump belongs to alive. However, threads are expected to have an - // active run loop, so we manage a RunLoop internally here, starting/stopping - // it as necessary. - std::unique_ptr run_loop_; - - // See Abort(). - bool should_abort_ = false; - - // Whether this message pump is quitting, or has quit. - bool quit_ = false; - - // The MessageLoop::Delegate for this pump. - Delegate* delegate_ = nullptr; - - // The time at which we are currently scheduled to wake up and perform a - // delayed task. - base::TimeTicks delayed_scheduled_time_; - - // If set, a callback to fire when the message pump is quit. - base::OnceClosure on_quit_callback_; - - // The file descriptor used to signal that non-delayed work is available. - int non_delayed_fd_; - - // The file descriptor used to signal that delayed work is available. - int delayed_fd_; - - // The Android Looper for this thread. - ALooper* looper_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); -}; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_ diff --git a/message_loop/message_pump_default.cc b/message_loop/message_pump_default.cc deleted file mode 100644 index 4104e7346..000000000 --- a/message_loop/message_pump_default.cc +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/message_loop/message_pump_default.h" - -#include "base/auto_reset.h" -#include "base/logging.h" -#include "base/threading/thread_restrictions.h" -#include "build/build_config.h" - -#if defined(OS_MACOSX) -#include - -#include "base/mac/mach_logging.h" -#include "base/mac/scoped_mach_port.h" -#include "base/mac/scoped_nsautorelease_pool.h" -#endif - -namespace base { - -MessagePumpDefault::MessagePumpDefault() - : keep_running_(true), - event_(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED) {} - -MessagePumpDefault::~MessagePumpDefault() = default; - -void MessagePumpDefault::Run(Delegate* delegate) { - AutoReset auto_reset_keep_running(&keep_running_, true); - - for (;;) { -#if defined(OS_MACOSX) - mac::ScopedNSAutoreleasePool autorelease_pool; -#endif - - bool did_work = delegate->DoWork(); - if (!keep_running_) - break; - - did_work |= delegate->DoDelayedWork(&delayed_work_time_); - if (!keep_running_) - break; - - if (did_work) - continue; - - did_work = delegate->DoIdleWork(); - if (!keep_running_) - break; - - if (did_work) - continue; - - ThreadRestrictions::ScopedAllowWait allow_wait; - if (delayed_work_time_.is_null()) { - event_.Wait(); - } else { - // No need to handle already expired |delayed_work_time_| in any special - // way. When |delayed_work_time_| is in the past TimeWaitUntil returns - // promptly and |delayed_work_time_| will re-initialized on a next - // DoDelayedWork call which has to be called in order to get here again. - event_.TimedWaitUntil(delayed_work_time_); - } - // Since event_ is auto-reset, we don't need to do anything special here - // other than service each delegate method. - } -} - -void MessagePumpDefault::Quit() { - keep_running_ = false; -} - -void MessagePumpDefault::ScheduleWork() { - // Since this can be called on any thread, we need to ensure that our Run - // loop wakes up. - event_.Signal(); -} - -void MessagePumpDefault::ScheduleDelayedWork( - const TimeTicks& delayed_work_time) { - // We know that we can't be blocked on Wait right now since this method can - // only be called on the same thread as Run, so we only need to update our - // record of how long to sleep when we do sleep. - delayed_work_time_ = delayed_work_time; -} - -#if defined(OS_MACOSX) -void MessagePumpDefault::SetTimerSlack(TimerSlack timer_slack) { - thread_latency_qos_policy_data_t policy{}; - policy.thread_latency_qos_tier = timer_slack == TIMER_SLACK_MAXIMUM - ? LATENCY_QOS_TIER_3 - : LATENCY_QOS_TIER_UNSPECIFIED; - mac::ScopedMachSendRight thread_port(mach_thread_self()); - kern_return_t kr = - thread_policy_set(thread_port.get(), THREAD_LATENCY_QOS_POLICY, - reinterpret_cast(&policy), - THREAD_LATENCY_QOS_POLICY_COUNT); - MACH_DVLOG_IF(1, kr != KERN_SUCCESS, kr) << "thread_policy_set"; -} -#endif - -} // namespace base diff --git a/message_loop/message_pump_default.h b/message_loop/message_pump_default.h deleted file mode 100644 index dd11adcb6..000000000 --- a/message_loop/message_pump_default.h +++ /dev/null @@ -1,46 +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_DEFAULT_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_DEFAULT_H_ - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/message_loop/message_pump.h" -#include "base/synchronization/waitable_event.h" -#include "base/time/time.h" -#include "build/build_config.h" - -namespace base { - -class BASE_EXPORT MessagePumpDefault : public MessagePump { - public: - MessagePumpDefault(); - ~MessagePumpDefault() override; - - // MessagePump methods: - void Run(Delegate* delegate) override; - void Quit() override; - void ScheduleWork() override; - void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; -#if defined(OS_MACOSX) - void SetTimerSlack(TimerSlack timer_slack) override; -#endif - - private: - // This flag is set to false when Run should return. - bool keep_running_; - - // Used to sleep until there is more work to do. - WaitableEvent event_; - - // The time at which we should call DoDelayedWork. - TimeTicks delayed_work_time_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpDefault); -}; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_DEFAULT_H_ diff --git a/message_loop/message_pump_for_io.h b/message_loop/message_pump_for_io.h deleted file mode 100644 index 6aac1e609..000000000 --- a/message_loop/message_pump_for_io.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 The Chromium 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_FOR_IO_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_FOR_IO_H_ - -// This header is a forwarding header to coalesce the various platform specific -// types representing MessagePumpForIO. - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/message_loop/message_pump_win.h" -#elif defined(OS_IOS) -#include "base/message_loop/message_pump_io_ios.h" -#elif defined(OS_NACL_SFI) -#include "base/message_loop/message_pump_default.h" -#elif defined(OS_FUCHSIA) -#include "base/message_loop/message_pump_fuchsia.h" -#elif defined(OS_POSIX) -#include "base/message_loop/message_pump_libevent.h" -#endif - -namespace base { - -#if defined(OS_WIN) -// Windows defines it as-is. -using MessagePumpForIO = MessagePumpForIO; -#elif defined(OS_IOS) -using MessagePumpForIO = MessagePumpIOSForIO; -#elif defined(OS_NACL_SFI) -using MessagePumpForIO = MessagePumpDefault; -#elif defined(OS_FUCHSIA) -using MessagePumpForIO = MessagePumpFuchsia; -#elif defined(OS_POSIX) -using MessagePumpForIO = MessagePumpLibevent; -#else -#error Platform does not define MessagePumpForIO -#endif - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_FOR_IO_H_ diff --git a/message_loop/message_pump_for_ui.h b/message_loop/message_pump_for_ui.h deleted file mode 100644 index 6ee02b094..000000000 --- a/message_loop/message_pump_for_ui.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2018 The Chromium 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_FOR_UI_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_FOR_UI_H_ - -// This header is a forwarding header to coalesce the various platform specific -// implementations of MessagePumpForUI. - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/message_loop/message_pump_win.h" -#elif defined(OS_ANDROID) -#include "base/message_loop/message_pump_android.h" -#elif defined(OS_MACOSX) -#include "base/message_loop/message_pump.h" -#elif defined(OS_NACL) || defined(OS_AIX) -// No MessagePumpForUI, see below. -#elif defined(USE_GLIB) -#include "base/message_loop/message_pump_glib.h" -#elif defined(OS_LINUX) || defined(OS_BSD) -#include "base/message_loop/message_pump_libevent.h" -#elif defined(OS_FUCHSIA) -#include "base/message_loop/message_pump_fuchsia.h" -#endif - -namespace base { - -#if defined(OS_WIN) -// Windows defines it as-is. -using MessagePumpForUI = MessagePumpForUI; -#elif defined(OS_ANDROID) -// Android defines it as-is. -using MessagePumpForUI = MessagePumpForUI; -#elif defined(OS_MACOSX) -// MessagePumpForUI isn't bound to a specific impl on Mac. While each impl can -// be represented by a plain MessagePump: MessagePumpMac::Create() must be used -// to instantiate the right impl. -using MessagePumpForUI = MessagePump; -#elif defined(OS_NACL) || defined(OS_AIX) -// Currently NaCl and AIX don't have a MessagePumpForUI. -// TODO(abarth): Figure out if we need this. -#elif defined(USE_GLIB) -using MessagePumpForUI = MessagePumpGlib; -#elif defined(OS_LINUX) || defined(OS_BSD) -using MessagePumpForUI = MessagePumpLibevent; -#elif defined(OS_FUCHSIA) -using MessagePumpForUI = MessagePumpFuchsia; -#else -#error Platform does not define MessagePumpForUI -#endif - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_FOR_UI_H_ diff --git a/message_loop/message_pump_fuchsia.cc b/message_loop/message_pump_fuchsia.cc deleted file mode 100644 index 91585fcd5..000000000 --- a/message_loop/message_pump_fuchsia.cc +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright 2017 The Chromium 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_fuchsia.h" - -#include -#include -#include -#include - -#include "base/auto_reset.h" -#include "base/fuchsia/fuchsia_logging.h" -#include "base/logging.h" - -namespace base { - -MessagePumpFuchsia::ZxHandleWatchController::ZxHandleWatchController( - const Location& from_here) - : async_wait_t({}), created_from_location_(from_here) {} - -MessagePumpFuchsia::ZxHandleWatchController::~ZxHandleWatchController() { - if (!StopWatchingZxHandle()) - NOTREACHED(); -} - -bool MessagePumpFuchsia::ZxHandleWatchController::WaitBegin() { - DCHECK(!handler); - async_wait_t::handler = &HandleSignal; - - zx_status_t status = async_begin_wait(&weak_pump_->async_dispatcher_, this); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "async_begin_wait() failed"; - async_wait_t::handler = nullptr; - return false; - } - - return true; -} - -bool MessagePumpFuchsia::ZxHandleWatchController::StopWatchingZxHandle() { - if (was_stopped_) { - DCHECK(!*was_stopped_); - *was_stopped_ = true; - - // |was_stopped_| points at a value stored on the stack, which will go out - // of scope. MessagePumpFuchsia::Run() will reset it only if the value is - // false. So we need to reset this pointer here as well, to make sure it's - // not used again. - was_stopped_ = nullptr; - } - - // If the pump is gone then there is nothing to cancel. - if (!weak_pump_) - return true; - - // |handler| is set when waiting for a signal. - if (!handler) - return true; - - async_wait_t::handler = nullptr; - - zx_status_t result = async_cancel_wait(&weak_pump_->async_dispatcher_, this); - ZX_DLOG_IF(ERROR, result != ZX_OK, result) << "async_cancel_wait failed"; - return result == ZX_OK; -} - -// static -void MessagePumpFuchsia::ZxHandleWatchController::HandleSignal( - async_t* async, - async_wait_t* wait, - zx_status_t status, - const zx_packet_signal_t* signal) { - if (status != ZX_OK) { - ZX_LOG(WARNING, status) << "async wait failed"; - return; - } - - ZxHandleWatchController* controller = - static_cast(wait); - DCHECK_EQ(controller->handler, &HandleSignal); - controller->handler = nullptr; - - // |signal| can include other spurious things, in particular, that an fd - // is writable, when we only asked to know when it was readable. In that - // case, we don't want to call both the CanWrite and CanRead callback, - // when the caller asked for only, for example, readable callbacks. So, - // mask with the events that we actually wanted to know about. - zx_signals_t signals = signal->trigger & signal->observed; - DCHECK_NE(0u, signals); - - // In the case of a persistent Watch, the Watch may be stopped and - // potentially deleted by the caller within the callback, in which case - // |controller| should not be accessed again, and we mustn't continue the - // watch. We check for this with a bool on the stack, which the Watch - // receives a pointer to. - bool was_stopped = false; - controller->was_stopped_ = &was_stopped; - - controller->watcher_->OnZxHandleSignalled(wait->object, signals); - - if (was_stopped) - return; - - controller->was_stopped_ = nullptr; - - if (controller->persistent_) - controller->WaitBegin(); -} - -void MessagePumpFuchsia::FdWatchController::OnZxHandleSignalled( - zx_handle_t handle, - zx_signals_t signals) { - uint32_t events; - __fdio_wait_end(io_, signals, &events); - - // Each |watcher_| callback we invoke may stop or delete |this|. The pump has - // set |was_stopped_| to point to a safe location on the calling stack, so we - // can use that to detect being stopped mid-callback and avoid doing further - // work that would touch |this|. - bool* was_stopped = was_stopped_; - if (events & FDIO_EVT_WRITABLE) - watcher_->OnFileCanWriteWithoutBlocking(fd_); - if (!*was_stopped && (events & FDIO_EVT_READABLE)) - watcher_->OnFileCanReadWithoutBlocking(fd_); - - // Don't add additional work here without checking |*was_stopped_| again. -} - -MessagePumpFuchsia::FdWatchController::FdWatchController( - const Location& from_here) - : FdWatchControllerInterface(from_here), - ZxHandleWatchController(from_here) {} - -MessagePumpFuchsia::FdWatchController::~FdWatchController() { - if (!StopWatchingFileDescriptor()) - NOTREACHED(); -} - -bool MessagePumpFuchsia::FdWatchController::WaitBegin() { - // Refresh the |handle_| and |desired_signals_| from the mxio for the fd. - // Some types of fdio map read/write events to different signals depending on - // their current state, so we must do this every time we begin to wait. - __fdio_wait_begin(io_, desired_events_, &object, &trigger); - if (async_wait_t::object == ZX_HANDLE_INVALID) { - DLOG(ERROR) << "fdio_wait_begin failed"; - return false; - } - - return MessagePumpFuchsia::ZxHandleWatchController::WaitBegin(); -} - -bool MessagePumpFuchsia::FdWatchController::StopWatchingFileDescriptor() { - bool success = StopWatchingZxHandle(); - if (io_) { - __fdio_release(io_); - io_ = nullptr; - } - return success; -} - -MessagePumpFuchsia::MessagePumpFuchsia() : weak_factory_(this) {} - -MessagePumpFuchsia::~MessagePumpFuchsia() = default; - -bool MessagePumpFuchsia::WatchFileDescriptor(int fd, - bool persistent, - int mode, - FdWatchController* controller, - FdWatcher* delegate) { - DCHECK_GE(fd, 0); - DCHECK(controller); - DCHECK(delegate); - - if (!controller->StopWatchingFileDescriptor()) - NOTREACHED(); - - controller->fd_ = fd; - controller->watcher_ = delegate; - - DCHECK(!controller->io_); - controller->io_ = __fdio_fd_to_io(fd); - if (!controller->io_) { - DLOG(ERROR) << "Failed to get IO for FD"; - return false; - } - - switch (mode) { - case WATCH_READ: - controller->desired_events_ = FDIO_EVT_READABLE; - break; - case WATCH_WRITE: - controller->desired_events_ = FDIO_EVT_WRITABLE; - break; - case WATCH_READ_WRITE: - controller->desired_events_ = FDIO_EVT_READABLE | FDIO_EVT_WRITABLE; - break; - default: - NOTREACHED() << "unexpected mode: " << mode; - return false; - } - - // Pass dummy |handle| and |signals| values to WatchZxHandle(). The real - // values will be populated by FdWatchController::WaitBegin(), before actually - // starting the wait operation. - return WatchZxHandle(ZX_HANDLE_INVALID, persistent, 1, controller, - controller); -} - -bool MessagePumpFuchsia::WatchZxHandle(zx_handle_t handle, - bool persistent, - zx_signals_t signals, - ZxHandleWatchController* controller, - ZxHandleWatcher* delegate) { - DCHECK_NE(0u, signals); - DCHECK(controller); - DCHECK(delegate); - DCHECK(handle == ZX_HANDLE_INVALID || - controller->async_wait_t::object == ZX_HANDLE_INVALID || - handle == controller->async_wait_t::object); - - if (!controller->StopWatchingZxHandle()) - NOTREACHED(); - - controller->async_wait_t::object = handle; - controller->persistent_ = persistent; - controller->async_wait_t::trigger = signals; - controller->watcher_ = delegate; - - controller->weak_pump_ = weak_factory_.GetWeakPtr(); - - return controller->WaitBegin(); -} - -bool MessagePumpFuchsia::HandleEvents(zx_time_t deadline) { - zx_status_t status = async_dispatcher_.DispatchOrWaitUntil(deadline); - switch (status) { - // Return true if some tasks or events were dispatched or if the dispatcher - // was stopped by ScheduleWork(). - case ZX_OK: - case ZX_ERR_CANCELED: - return true; - - case ZX_ERR_TIMED_OUT: - return false; - - default: - ZX_DLOG(DCHECK, status) << "unexpected wait status"; - return false; - } -} - -void MessagePumpFuchsia::Run(Delegate* delegate) { - AutoReset auto_reset_keep_running(&keep_running_, true); - - for (;;) { - bool did_work = delegate->DoWork(); - if (!keep_running_) - break; - - did_work |= delegate->DoDelayedWork(&delayed_work_time_); - if (!keep_running_) - break; - - did_work |= HandleEvents(/*deadline=*/0); - if (!keep_running_) - break; - - if (did_work) - continue; - - did_work = delegate->DoIdleWork(); - if (!keep_running_) - break; - - if (did_work) - continue; - - zx_time_t deadline = delayed_work_time_.is_null() - ? ZX_TIME_INFINITE - : delayed_work_time_.ToZxTime(); - HandleEvents(deadline); - } -} - -void MessagePumpFuchsia::Quit() { - keep_running_ = false; -} - -void MessagePumpFuchsia::ScheduleWork() { - // Stop AsyncDispatcher to let MessagePumpFuchsia::Run() handle message loop - // tasks. - async_dispatcher_.Stop(); -} - -void MessagePumpFuchsia::ScheduleDelayedWork( - const TimeTicks& delayed_work_time) { - // We know that we can't be blocked right now since this method can only be - // called on the same thread as Run, so we only need to update our record of - // how long to sleep when we do sleep. - delayed_work_time_ = delayed_work_time; -} - -} // namespace base diff --git a/message_loop/message_pump_fuchsia.h b/message_loop/message_pump_fuchsia.h deleted file mode 100644 index 514e23f79..000000000 --- a/message_loop/message_pump_fuchsia.h +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2017 The Chromium 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_FUCHSIA_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_FUCHSIA_H_ - -#include - -#include "base/base_export.h" -#include "base/fuchsia/async_dispatcher.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_pump.h" -#include "base/message_loop/watchable_io_message_pump_posix.h" - -typedef struct fdio fdio_t; - -namespace base { - -class BASE_EXPORT MessagePumpFuchsia : public MessagePump, - public WatchableIOMessagePumpPosix { - public: - // Implemented by callers to receive notifications of handle & fd events. - class ZxHandleWatcher { - public: - virtual void OnZxHandleSignalled(zx_handle_t handle, - zx_signals_t signals) = 0; - - protected: - virtual ~ZxHandleWatcher() {} - }; - - // Manages an active watch on an zx_handle_t. - class ZxHandleWatchController : public async_wait_t { - public: - explicit ZxHandleWatchController(const Location& from_here); - // Deleting the Controller implicitly calls StopWatchingZxHandle. - virtual ~ZxHandleWatchController(); - - // Stop watching the handle, always safe to call. No-op if there's nothing - // to do. - bool StopWatchingZxHandle(); - - const Location& created_from_location() { return created_from_location_; } - - protected: - friend class MessagePumpFuchsia; - - virtual bool WaitBegin(); - - static void HandleSignal(async_t* async, - async_wait_t* wait, - zx_status_t status, - const zx_packet_signal_t* signal); - - const Location created_from_location_; - - // This bool is used by the pump when invoking the ZxHandleWatcher callback, - // and by the FdHandleWatchController when invoking read & write callbacks, - // to cope with the possibility of the caller deleting the *Watcher within - // the callback. The pump sets |was_stopped_| to a location on the stack, - // and the Watcher writes to it, if set, when deleted, allowing the pump - // to check the value on the stack to short-cut any post-callback work. - bool* was_stopped_ = nullptr; - - // Set directly from the inputs to WatchFileDescriptor. - ZxHandleWatcher* watcher_ = nullptr; - - // Used to safely access resources owned by the associated message pump. - WeakPtr weak_pump_; - - // A watch may be marked as persistent, which means it remains active even - // after triggering. - bool persistent_ = false; - - DISALLOW_COPY_AND_ASSIGN(ZxHandleWatchController); - }; - - class FdWatchController : public FdWatchControllerInterface, - public ZxHandleWatchController, - public ZxHandleWatcher { - public: - explicit FdWatchController(const Location& from_here); - ~FdWatchController() override; - - // FdWatchControllerInterface: - bool StopWatchingFileDescriptor() override; - - private: - friend class MessagePumpFuchsia; - - // Determines the desires signals, and begins waiting on the handle. - bool WaitBegin() override; - - // ZxHandleWatcher interface. - void OnZxHandleSignalled(zx_handle_t handle, zx_signals_t signals) override; - - // Set directly from the inputs to WatchFileDescriptor. - FdWatcher* watcher_ = nullptr; - int fd_ = -1; - uint32_t desired_events_ = 0; - - // Set by WatchFileDescriptor to hold a reference to the descriptor's mxio. - fdio_t* io_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(FdWatchController); - }; - - enum Mode { - WATCH_READ = 1 << 0, - WATCH_WRITE = 1 << 1, - WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE - }; - - MessagePumpFuchsia(); - ~MessagePumpFuchsia() override; - - bool WatchZxHandle(zx_handle_t handle, - bool persistent, - zx_signals_t signals, - ZxHandleWatchController* controller, - ZxHandleWatcher* delegate); - bool WatchFileDescriptor(int fd, - bool persistent, - int mode, - FdWatchController* controller, - FdWatcher* delegate); - - // MessagePump implementation: - void Run(Delegate* delegate) override; - void Quit() override; - void ScheduleWork() override; - void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; - - private: - // Handles IO events by running |async_dispatcher_|. Returns true if any - // events were received or if ScheduleWork() was called. - bool HandleEvents(zx_time_t deadline); - - // This flag is set to false when Run should return. - bool keep_running_ = true; - - AsyncDispatcher async_dispatcher_; - - // The time at which we should call DoDelayedWork. - TimeTicks delayed_work_time_; - - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpFuchsia); -}; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_FUCHSIA_H_ diff --git a/message_loop/message_pump_glib.cc b/message_loop/message_pump_glib.cc deleted file mode 100644 index 2f1909b84..000000000 --- a/message_loop/message_pump_glib.cc +++ /dev/null @@ -1,359 +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_glib.h" - -#include -#include - -#include - -#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 { - -namespace { - -// Return a timeout suitable for the glib loop, -1 to block forever, -// 0 to return right away, or a timeout in milliseconds from now. -int GetTimeIntervalMilliseconds(const TimeTicks& from) { - if (from.is_null()) - return -1; - - // Be careful here. TimeDelta has a precision of microseconds, but we want a - // value in milliseconds. If there are 5.5ms left, should the delay be 5 or - // 6? It should be 6 to avoid executing delayed work too early. - int delay = static_cast( - ceil((from - TimeTicks::Now()).InMillisecondsF())); - - // If this value is negative, then we need to run delayed work soon. - return delay < 0 ? 0 : delay; -} - -// A brief refresher on GLib: -// GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize. -// On each iteration of the GLib pump, it calls each source's Prepare function. -// This function should return TRUE if it wants GLib to call its Dispatch, and -// FALSE otherwise. It can also set a timeout in this case for the next time -// Prepare should be called again (it may be called sooner). -// After the Prepare calls, GLib does a poll to check for events from the -// system. File descriptors can be attached to the sources. The poll may block -// if none of the Prepare calls returned TRUE. It will block indefinitely, or -// by the minimum time returned by a source in Prepare. -// After the poll, GLib calls Check for each source that returned FALSE -// from Prepare. The return value of Check has the same meaning as for Prepare, -// making Check a second chance to tell GLib we are ready for Dispatch. -// Finally, GLib calls Dispatch for each source that is ready. If Dispatch -// returns FALSE, GLib will destroy the source. Dispatch calls may be recursive -// (i.e., you can call Run from them), but Prepare and Check cannot. -// Finalize is called when the source is destroyed. -// NOTE: It is common for subsystems to want to process pending events while -// doing intensive work, for example the flash plugin. They usually use the -// following pattern (recommended by the GTK docs): -// while (gtk_events_pending()) { -// gtk_main_iteration(); -// } -// -// gtk_events_pending just calls g_main_context_pending, which does the -// following: -// - Call prepare on all the sources. -// - Do the poll with a timeout of 0 (not blocking). -// - Call check on all the sources. -// - *Does not* call dispatch on the sources. -// - Return true if any of prepare() or check() returned true. -// -// gtk_main_iteration just calls g_main_context_iteration, which does the whole -// thing, respecting the timeout for the poll (and block, although it is -// expected not to if gtk_events_pending returned true), and call dispatch. -// -// Thus it is important to only return true from prepare or check if we -// actually have events or work to do. We also need to make sure we keep -// internal state consistent so that if prepare/check return true when called -// from gtk_events_pending, they will still return true when called right -// after, from gtk_main_iteration. -// -// For the GLib pump we try to follow the Windows UI pump model: -// - Whenever we receive a wakeup event or the timer for delayed work expires, -// we run DoWork and/or DoDelayedWork. That part will also run in the other -// event pumps. -// - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main -// loop, around event handling. - -struct WorkSource : public GSource { - MessagePumpGlib* pump; -}; - -gboolean WorkSourcePrepare(GSource* source, - gint* timeout_ms) { - *timeout_ms = static_cast(source)->pump->HandlePrepare(); - // We always return FALSE, so that our timeout is honored. If we were - // to return TRUE, the timeout would be considered to be 0 and the poll - // would never block. Once the poll is finished, Check will be called. - return FALSE; -} - -gboolean WorkSourceCheck(GSource* source) { - // Only return TRUE if Dispatch should be called. - return static_cast(source)->pump->HandleCheck(); -} - -gboolean WorkSourceDispatch(GSource* source, - GSourceFunc unused_func, - gpointer unused_data) { - - static_cast(source)->pump->HandleDispatch(); - // Always return TRUE so our source stays registered. - return TRUE; -} - -// I wish these could be const, but g_source_new wants non-const. -GSourceFuncs WorkSourceFuncs = {WorkSourcePrepare, WorkSourceCheck, - WorkSourceDispatch, nullptr}; - -// 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::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; - - // Used to flag that the current Run() invocation should return ASAP. - bool should_quit; - - // Used to count how many Run() invocations are on the stack. - int run_depth; - - // This keeps the state of whether the pump got signaled that there was new - // work to be done. Since we eat the message on the wake up pipe as soon as - // we get it, we keep that state here to stay consistent. - bool has_work; -}; - -MessagePumpGlib::MessagePumpGlib() - : state_(nullptr), - context_(g_main_context_default()), - wakeup_gpollfd_(new GPollFD) { - // Create our wakeup pipe, which is used to flag when work was scheduled. - int fds[2]; - int ret = pipe(fds); - DCHECK_EQ(ret, 0); - (void)ret; // Prevent warning in release mode. - - wakeup_pipe_read_ = fds[0]; - wakeup_pipe_write_ = fds[1]; - wakeup_gpollfd_->fd = wakeup_pipe_read_; - wakeup_gpollfd_->events = G_IO_IN; - - work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); - static_cast(work_source_)->pump = this; - g_source_add_poll(work_source_, wakeup_gpollfd_.get()); - // Use a low priority so that we let other events in the queue go first. - g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE); - // This is needed to allow Run calls inside Dispatch. - g_source_set_can_recurse(work_source_, TRUE); - g_source_attach(work_source_, context_); -} - -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_); -} - -// 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 - // the pump block so that we can do some processing. - if (state_ && // state_ may be null during tests. - state_->has_work) - return 0; - - // We don't think we have work to do, but make sure not to block - // longer than the next time we need to run delayed work. - return GetTimeIntervalMilliseconds(delayed_work_time_); -} - -bool MessagePumpGlib::HandleCheck() { - if (!state_) // state_ may be null during tests. - return false; - - // We usually have a single message on the wakeup pipe, since we are only - // signaled when the queue went from empty to non-empty, but there can be - // two messages if a task posted a task, hence we read at most two bytes. - // The glib poll will tell us whether there was data, so this read - // shouldn't block. - if (wakeup_gpollfd_->revents & G_IO_IN) { - char msg[2]; - const int num_bytes = HANDLE_EINTR(read(wakeup_pipe_read_, msg, 2)); - if (num_bytes < 1) { - NOTREACHED() << "Error reading from the wakeup pipe."; - } - DCHECK((num_bytes == 1 && msg[0] == '!') || - (num_bytes == 2 && msg[0] == '!' && msg[1] == '!')); - // Since we ate the message, we need to record that we have more work, - // because HandleCheck() may be called without HandleDispatch being called - // afterwards. - state_->has_work = true; - } - - if (state_->has_work) - return true; - - if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) { - // The timer has expired. That condition will stay true until we process - // that delayed work, so we don't need to record this differently. - return true; - } - - return false; -} - -void MessagePumpGlib::HandleDispatch() { - state_->has_work = false; - if (state_->delegate->DoWork()) { - // NOTE: on Windows at this point we would call ScheduleWork (see - // MessagePumpGlib::HandleWorkMessage in message_pump_win.cc). But here, - // instead of posting a message on the wakeup pipe, we can avoid the - // syscalls and just signal that we have more work. - state_->has_work = true; - } - - if (state_->should_quit) - return; - - state_->delegate->DoDelayedWork(&delayed_work_time_); -} - -void MessagePumpGlib::Run(Delegate* delegate) { -#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() { - if (state_) { - state_->should_quit = true; - } else { - NOTREACHED() << "Quit called outside Run!"; - } -} - -void MessagePumpGlib::ScheduleWork() { - // This can be called on any thread, so we don't want to touch any state - // variables as we would then need locks all over. This ensures that if - // we are sleeping in a poll that we will wake up. - char msg = '!'; - if (HANDLE_EINTR(write(wakeup_pipe_write_, &msg, 1)) != 1) { - NOTREACHED() << "Could not write to the UI message loop wakeup pipe!"; - } -} - -void MessagePumpGlib::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { - // We need to wake up the loop in case the poll timeout needs to be - // adjusted. This will cause us to try to do work, but that's OK. - delayed_work_time_ = delayed_work_time; - ScheduleWork(); -} - -bool MessagePumpGlib::ShouldQuit() const { - CHECK(state_); - return state_->should_quit; -} - -} // namespace base diff --git a/message_loop/message_pump_glib.h b/message_loop/message_pump_glib.h deleted file mode 100644 index d79dba55a..000000000 --- a/message_loop/message_pump_glib.h +++ /dev/null @@ -1,80 +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_GLIB_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_GLIB_H_ - -#include - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/message_loop/message_pump.h" -#include "base/observer_list.h" -#include "base/time/time.h" - -typedef struct _GMainContext GMainContext; -typedef struct _GPollFD GPollFD; -typedef struct _GSource GSource; - -namespace base { - -// This class implements a base MessagePump needed for TYPE_UI MessageLoops on -// platforms using GLib. -class BASE_EXPORT MessagePumpGlib : public MessagePump { - public: - MessagePumpGlib(); - ~MessagePumpGlib() override; - - // 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 - // will be passed to the poll. HandleCheck is called after the poll - // has completed, and returns whether or not HandleDispatch should be called. - // HandleDispatch is called if HandleCheck returned true. - int HandlePrepare(); - bool HandleCheck(); - void HandleDispatch(); - - // Overridden from MessagePump: - void Run(Delegate* delegate) override; - void Quit() override; - void ScheduleWork() override; - void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; - - 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; - - RunState* state_; - - // This is a GLib structure that we can add event sources to. We use the - // default GLib context, which is the one to which all GTK events are - // dispatched. - GMainContext* context_; - - // This is the time when we need to do delayed work. - TimeTicks delayed_work_time_; - - // The work source. It is shared by all calls to Run and destroyed when - // the message pump is destroyed. - GSource* work_source_; - - // We use a wakeup pipe to make sure we'll get out of the glib polling phase - // when another thread has scheduled us to do some work. There is a glib - // mechanism g_main_context_wakeup, but this won't guarantee that our event's - // Dispatch() will be called. - int wakeup_pipe_read_; - int wakeup_pipe_write_; - // Use a unique_ptr to avoid needing the definition of GPollFD in the header. - std::unique_ptr wakeup_gpollfd_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpGlib); -}; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_GLIB_H_ diff --git a/message_loop/message_pump_glib_unittest.cc b/message_loop/message_pump_glib_unittest.cc deleted file mode 100644 index 512cea631..000000000 --- a/message_loop/message_pump_glib_unittest.cc +++ /dev/null @@ -1,546 +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_glib.h" - -#include -#include - -#include -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_current.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -// This class injects dummy "events" into the GLib loop. When "handled" these -// events can run tasks. This is intended to mock gtk events (the corresponding -// GLib source runs at the same priority). -class EventInjector { - public: - EventInjector() : processed_events_(0) { - source_ = static_cast(g_source_new(&SourceFuncs, sizeof(Source))); - source_->injector = this; - g_source_attach(source_, nullptr); - g_source_set_can_recurse(source_, TRUE); - } - - ~EventInjector() { - g_source_destroy(source_); - g_source_unref(source_); - } - - int HandlePrepare() { - // If the queue is empty, block. - if (events_.empty()) - return -1; - TimeDelta delta = events_[0].time - Time::NowFromSystemTime(); - return std::max(0, static_cast(ceil(delta.InMillisecondsF()))); - } - - bool HandleCheck() { - if (events_.empty()) - return false; - return events_[0].time <= Time::NowFromSystemTime(); - } - - void HandleDispatch() { - if (events_.empty()) - return; - Event event = std::move(events_[0]); - events_.erase(events_.begin()); - ++processed_events_; - if (!event.callback.is_null()) - std::move(event.callback).Run(); - else if (!event.task.is_null()) - std::move(event.task).Run(); - } - - // Adds an event to the queue. When "handled", executes |callback|. - // delay_ms is relative to the last event if any, or to Now() otherwise. - void AddEvent(int delay_ms, OnceClosure callback) { - AddEventHelper(delay_ms, std::move(callback), OnceClosure()); - } - - void AddDummyEvent(int delay_ms) { - AddEventHelper(delay_ms, OnceClosure(), OnceClosure()); - } - - void AddEventAsTask(int delay_ms, OnceClosure task) { - AddEventHelper(delay_ms, OnceClosure(), std::move(task)); - } - - void Reset() { - processed_events_ = 0; - events_.clear(); - } - - int processed_events() const { return processed_events_; } - - private: - struct Event { - Time time; - OnceClosure callback; - OnceClosure task; - }; - - struct Source : public GSource { - EventInjector* injector; - }; - - void AddEventHelper(int delay_ms, OnceClosure callback, OnceClosure task) { - Time last_time; - if (!events_.empty()) - last_time = (events_.end()-1)->time; - else - last_time = Time::NowFromSystemTime(); - - Time future = last_time + TimeDelta::FromMilliseconds(delay_ms); - EventInjector::Event event = {future, std::move(callback), std::move(task)}; - events_.push_back(std::move(event)); - } - - static gboolean Prepare(GSource* source, gint* timeout_ms) { - *timeout_ms = static_cast(source)->injector->HandlePrepare(); - return FALSE; - } - - static gboolean Check(GSource* source) { - return static_cast(source)->injector->HandleCheck(); - } - - static gboolean Dispatch(GSource* source, - GSourceFunc unused_func, - gpointer unused_data) { - static_cast(source)->injector->HandleDispatch(); - return TRUE; - } - - Source* source_; - std::vector events_; - int processed_events_; - static GSourceFuncs SourceFuncs; - DISALLOW_COPY_AND_ASSIGN(EventInjector); -}; - -GSourceFuncs EventInjector::SourceFuncs = {EventInjector::Prepare, - EventInjector::Check, - EventInjector::Dispatch, nullptr}; - -void IncrementInt(int *value) { - ++*value; -} - -// Checks how many events have been processed by the injector. -void ExpectProcessedEvents(EventInjector* injector, int count) { - EXPECT_EQ(injector->processed_events(), count); -} - -// Posts a task on the current message loop. -void PostMessageLoopTask(const Location& from_here, OnceClosure task) { - ThreadTaskRunnerHandle::Get()->PostTask(from_here, std::move(task)); -} - -// Test fixture. -class MessagePumpGLibTest : public testing::Test { - public: - MessagePumpGLibTest() : loop_(nullptr), injector_(nullptr) {} - - // Overridden from testing::Test: - void SetUp() override { - loop_ = new MessageLoop(MessageLoop::TYPE_UI); - injector_ = new EventInjector(); - } - void TearDown() override { - delete injector_; - injector_ = nullptr; - delete loop_; - loop_ = nullptr; - } - - MessageLoop* loop() const { return loop_; } - EventInjector* injector() const { return injector_; } - - private: - MessageLoop* loop_; - EventInjector* injector_; - DISALLOW_COPY_AND_ASSIGN(MessagePumpGLibTest); -}; - -} // namespace - -TEST_F(MessagePumpGLibTest, TestQuit) { - // Checks that Quit works and that the basic infrastructure is working. - - // Quit from a task - RunLoop().RunUntilIdle(); - EXPECT_EQ(0, injector()->processed_events()); - - injector()->Reset(); - // Quit from an event - RunLoop run_loop; - injector()->AddEvent(0, run_loop.QuitClosure()); - run_loop.Run(); - EXPECT_EQ(1, injector()->processed_events()); -} - -TEST_F(MessagePumpGLibTest, TestEventTaskInterleave) { - // Checks that tasks posted by events are executed before the next event if - // the posted task queue is empty. - // MessageLoop doesn't make strong guarantees that it is the case, but the - // current implementation ensures it and the tests below rely on it. - // If changes cause this test to fail, it is reasonable to change it, but - // TestWorkWhileWaitingForEvents and TestEventsWhileWaitingForWork have to be - // changed accordingly, otherwise they can become flaky. - injector()->AddEventAsTask(0, DoNothing()); - OnceClosure check_task = - BindOnce(&ExpectProcessedEvents, Unretained(injector()), 2); - OnceClosure posted_task = - BindOnce(&PostMessageLoopTask, FROM_HERE, std::move(check_task)); - injector()->AddEventAsTask(0, std::move(posted_task)); - injector()->AddEventAsTask(0, DoNothing()); - { - RunLoop run_loop; - injector()->AddEvent(0, run_loop.QuitClosure()); - run_loop.Run(); - } - EXPECT_EQ(4, injector()->processed_events()); - - injector()->Reset(); - injector()->AddEventAsTask(0, DoNothing()); - check_task = BindOnce(&ExpectProcessedEvents, Unretained(injector()), 2); - posted_task = - BindOnce(&PostMessageLoopTask, FROM_HERE, std::move(check_task)); - injector()->AddEventAsTask(0, std::move(posted_task)); - injector()->AddEventAsTask(10, DoNothing()); - { - RunLoop run_loop; - injector()->AddEvent(0, run_loop.QuitClosure()); - run_loop.Run(); - } - EXPECT_EQ(4, injector()->processed_events()); -} - -TEST_F(MessagePumpGLibTest, TestWorkWhileWaitingForEvents) { - int task_count = 0; - // Tests that we process tasks while waiting for new events. - // The event queue is empty at first. - for (int i = 0; i < 10; ++i) { - loop()->task_runner()->PostTask(FROM_HERE, - BindOnce(&IncrementInt, &task_count)); - } - // After all the previous tasks have executed, enqueue an event that will - // quit. - { - RunLoop run_loop; - loop()->task_runner()->PostTask( - FROM_HERE, BindOnce(&EventInjector::AddEvent, Unretained(injector()), 0, - run_loop.QuitClosure())); - run_loop.Run(); - } - ASSERT_EQ(10, task_count); - EXPECT_EQ(1, injector()->processed_events()); - - // Tests that we process delayed tasks while waiting for new events. - injector()->Reset(); - task_count = 0; - for (int i = 0; i < 10; ++i) { - loop()->task_runner()->PostDelayedTask(FROM_HERE, - BindOnce(&IncrementInt, &task_count), - TimeDelta::FromMilliseconds(10 * i)); - } - // After all the previous tasks have executed, enqueue an event that will - // quit. - // This relies on the fact that delayed tasks are executed in delay order. - // That is verified in message_loop_unittest.cc. - { - RunLoop run_loop; - loop()->task_runner()->PostDelayedTask( - FROM_HERE, - BindOnce(&EventInjector::AddEvent, Unretained(injector()), 0, - run_loop.QuitClosure()), - TimeDelta::FromMilliseconds(150)); - run_loop.Run(); - } - ASSERT_EQ(10, task_count); - EXPECT_EQ(1, injector()->processed_events()); -} - -TEST_F(MessagePumpGLibTest, TestEventsWhileWaitingForWork) { - // Tests that we process events while waiting for work. - // The event queue is empty at first. - for (int i = 0; i < 10; ++i) { - injector()->AddDummyEvent(0); - } - // After all the events have been processed, post a task that will check that - // the events have been processed (note: the task executes after the event - // that posted it has been handled, so we expect 11 at that point). - OnceClosure check_task = - BindOnce(&ExpectProcessedEvents, Unretained(injector()), 11); - OnceClosure posted_task = - BindOnce(&PostMessageLoopTask, FROM_HERE, std::move(check_task)); - injector()->AddEventAsTask(10, std::move(posted_task)); - - // And then quit (relies on the condition tested by TestEventTaskInterleave). - RunLoop run_loop; - injector()->AddEvent(10, run_loop.QuitClosure()); - run_loop.Run(); - - EXPECT_EQ(12, injector()->processed_events()); -} - -namespace { - -// This class is a helper for the concurrent events / posted tasks test below. -// It will quit the main loop once enough tasks and events have been processed, -// while making sure there is always work to do and events in the queue. -class ConcurrentHelper : public RefCounted { - public: - ConcurrentHelper(EventInjector* injector, OnceClosure done_closure) - : injector_(injector), - done_closure_(std::move(done_closure)), - event_count_(kStartingEventCount), - task_count_(kStartingTaskCount) {} - - void FromTask() { - if (task_count_ > 0) { - --task_count_; - } - if (task_count_ == 0 && event_count_ == 0) { - std::move(done_closure_).Run(); - } else { - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, this)); - } - } - - void FromEvent() { - if (event_count_ > 0) { - --event_count_; - } - if (task_count_ == 0 && event_count_ == 0) { - std::move(done_closure_).Run(); - } else { - injector_->AddEventAsTask(0, - BindOnce(&ConcurrentHelper::FromEvent, this)); - } - } - - int event_count() const { return event_count_; } - int task_count() const { return task_count_; } - - private: - friend class RefCounted; - - ~ConcurrentHelper() {} - - static const int kStartingEventCount = 20; - static const int kStartingTaskCount = 20; - - EventInjector* injector_; - OnceClosure done_closure_; - int event_count_; - int task_count_; -}; - -} // namespace - -TEST_F(MessagePumpGLibTest, TestConcurrentEventPostedTask) { - // Tests that posted tasks don't starve events, nor the opposite. - // We use the helper class above. We keep both event and posted task queues - // full, the helper verifies that both tasks and events get processed. - // If that is not the case, either event_count_ or task_count_ will not get - // to 0, and MessageLoop::QuitWhenIdle() will never be called. - RunLoop run_loop; - scoped_refptr helper = - new ConcurrentHelper(injector(), run_loop.QuitClosure()); - - // Add 2 events to the queue to make sure it is always full (when we remove - // the event before processing it). - injector()->AddEventAsTask(0, BindOnce(&ConcurrentHelper::FromEvent, helper)); - injector()->AddEventAsTask(0, BindOnce(&ConcurrentHelper::FromEvent, helper)); - - // Similarly post 2 tasks. - loop()->task_runner()->PostTask( - FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper)); - loop()->task_runner()->PostTask( - FROM_HERE, BindOnce(&ConcurrentHelper::FromTask, helper)); - - run_loop.Run(); - EXPECT_EQ(0, helper->event_count()); - EXPECT_EQ(0, helper->task_count()); -} - -namespace { - -void AddEventsAndDrainGLib(EventInjector* injector, OnceClosure on_drained) { - // 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, std::move(on_drained)); - - // Post a couple of dummy tasks - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing()); - - // Drain the events - while (g_main_context_pending(nullptr)) { - g_main_context_iteration(nullptr, FALSE); - } -} - -} // namespace - -TEST_F(MessagePumpGLibTest, TestDrainingGLib) { - // Tests that draining events using GLib works. - RunLoop run_loop; - loop()->task_runner()->PostTask( - FROM_HERE, BindOnce(&AddEventsAndDrainGLib, Unretained(injector()), - run_loop.QuitClosure())); - run_loop.Run(); - - EXPECT_EQ(3, injector()->processed_events()); -} - -namespace { - -// Helper class that lets us run the GLib message loop. -class GLibLoopRunner : public RefCounted { - public: - GLibLoopRunner() : quit_(false) { } - - void RunGLib() { - while (!quit_) { - g_main_context_iteration(nullptr, TRUE); - } - } - - void RunLoop() { - while (!quit_) { - g_main_context_iteration(nullptr, TRUE); - } - } - - void Quit() { - quit_ = true; - } - - void Reset() { - quit_ = false; - } - - private: - friend class RefCounted; - - ~GLibLoopRunner() {} - - bool quit_; -}; - -void TestGLibLoopInternal(EventInjector* injector, OnceClosure done) { - // Allow tasks to be processed from 'native' event loops. - MessageLoopCurrent::Get()->SetNestableTasksAllowed(true); - scoped_refptr runner = new GLibLoopRunner(); - - int task_count = 0; - // Add a couple of dummy events - injector->AddDummyEvent(0); - injector->AddDummyEvent(0); - // Post a couple of dummy tasks - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&IncrementInt, &task_count)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&IncrementInt, &task_count)); - // Delayed events - injector->AddDummyEvent(10); - injector->AddDummyEvent(10); - // Delayed work - ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, BindOnce(&IncrementInt, &task_count), - TimeDelta::FromMilliseconds(30)); - ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner), - TimeDelta::FromMilliseconds(40)); - - // Run a nested, straight GLib message loop. - runner->RunGLib(); - - ASSERT_EQ(3, task_count); - EXPECT_EQ(4, injector->processed_events()); - std::move(done).Run(); -} - -void TestGtkLoopInternal(EventInjector* injector, OnceClosure done) { - // Allow tasks to be processed from 'native' event loops. - MessageLoopCurrent::Get()->SetNestableTasksAllowed(true); - scoped_refptr runner = new GLibLoopRunner(); - - int task_count = 0; - // Add a couple of dummy events - injector->AddDummyEvent(0); - injector->AddDummyEvent(0); - // Post a couple of dummy tasks - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&IncrementInt, &task_count)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&IncrementInt, &task_count)); - // Delayed events - injector->AddDummyEvent(10); - injector->AddDummyEvent(10); - // Delayed work - ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, BindOnce(&IncrementInt, &task_count), - TimeDelta::FromMilliseconds(30)); - ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, BindOnce(&GLibLoopRunner::Quit, runner), - TimeDelta::FromMilliseconds(40)); - - // Run a nested, straight Gtk message loop. - runner->RunLoop(); - - ASSERT_EQ(3, task_count); - EXPECT_EQ(4, injector->processed_events()); - std::move(done).Run(); -} - -} // namespace - -TEST_F(MessagePumpGLibTest, TestGLibLoop) { - // Tests that events and posted tasks are correctly executed if the message - // loop is not run by MessageLoop::Run() but by a straight GLib loop. - // Note that in this case we don't make strong guarantees about niceness - // between events and posted tasks. - RunLoop run_loop; - loop()->task_runner()->PostTask( - FROM_HERE, BindOnce(&TestGLibLoopInternal, Unretained(injector()), - run_loop.QuitClosure())); - run_loop.Run(); -} - -TEST_F(MessagePumpGLibTest, TestGtkLoop) { - // Tests that events and posted tasks are correctly executed if the message - // loop is not run by MessageLoop::Run() but by a straight Gtk loop. - // Note that in this case we don't make strong guarantees about niceness - // between events and posted tasks. - RunLoop run_loop; - loop()->task_runner()->PostTask( - FROM_HERE, BindOnce(&TestGtkLoopInternal, Unretained(injector()), - run_loop.QuitClosure())); - run_loop.Run(); -} - -} // namespace base diff --git a/message_loop/message_pump_io_ios.cc b/message_loop/message_pump_io_ios.cc deleted file mode 100644 index 9b43e8edb..000000000 --- a/message_loop/message_pump_io_ios.cc +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 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_io_ios.h" - -namespace base { - -MessagePumpIOSForIO::FdWatchController::FdWatchController( - const Location& from_here) - : FdWatchControllerInterface(from_here) {} - -MessagePumpIOSForIO::FdWatchController::~FdWatchController() { - StopWatchingFileDescriptor(); -} - -bool MessagePumpIOSForIO::FdWatchController::StopWatchingFileDescriptor() { - if (fdref_ == NULL) - return true; - - CFFileDescriptorDisableCallBacks(fdref_.get(), callback_types_); - if (pump_) - pump_->RemoveRunLoopSource(fd_source_); - fd_source_.reset(); - fdref_.reset(); - callback_types_ = 0; - pump_.reset(); - watcher_ = NULL; - return true; -} - -void MessagePumpIOSForIO::FdWatchController::Init(CFFileDescriptorRef fdref, - CFOptionFlags callback_types, - CFRunLoopSourceRef fd_source, - bool is_persistent) { - DCHECK(fdref); - DCHECK(!fdref_.is_valid()); - - is_persistent_ = is_persistent; - fdref_.reset(fdref); - callback_types_ = callback_types; - fd_source_.reset(fd_source); -} - -void MessagePumpIOSForIO::FdWatchController::OnFileCanReadWithoutBlocking( - int fd, - MessagePumpIOSForIO* pump) { - DCHECK(callback_types_ & kCFFileDescriptorReadCallBack); - watcher_->OnFileCanReadWithoutBlocking(fd); -} - -void MessagePumpIOSForIO::FdWatchController::OnFileCanWriteWithoutBlocking( - int fd, - MessagePumpIOSForIO* pump) { - DCHECK(callback_types_ & kCFFileDescriptorWriteCallBack); - watcher_->OnFileCanWriteWithoutBlocking(fd); -} - -MessagePumpIOSForIO::MessagePumpIOSForIO() : weak_factory_(this) { -} - -MessagePumpIOSForIO::~MessagePumpIOSForIO() { -} - -bool MessagePumpIOSForIO::WatchFileDescriptor(int fd, - bool persistent, - int mode, - FdWatchController* controller, - FdWatcher* delegate) { - DCHECK_GE(fd, 0); - DCHECK(controller); - DCHECK(delegate); - DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE); - - // WatchFileDescriptor should be called on the pump thread. It is not - // threadsafe, and your watcher may never be registered. - DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread()); - - CFFileDescriptorContext source_context = {0}; - source_context.info = controller; - - CFOptionFlags callback_types = 0; - if (mode & WATCH_READ) { - callback_types |= kCFFileDescriptorReadCallBack; - } - if (mode & WATCH_WRITE) { - callback_types |= kCFFileDescriptorWriteCallBack; - } - - CFFileDescriptorRef fdref = controller->fdref_.get(); - if (fdref == NULL) { - base::ScopedCFTypeRef scoped_fdref( - CFFileDescriptorCreate( - kCFAllocatorDefault, fd, false, HandleFdIOEvent, &source_context)); - if (scoped_fdref == NULL) { - NOTREACHED() << "CFFileDescriptorCreate failed"; - return false; - } - - CFFileDescriptorEnableCallBacks(scoped_fdref, callback_types); - - // TODO(wtc): what should the 'order' argument be? - base::ScopedCFTypeRef scoped_fd_source( - CFFileDescriptorCreateRunLoopSource( - kCFAllocatorDefault, scoped_fdref, 0)); - if (scoped_fd_source == NULL) { - NOTREACHED() << "CFFileDescriptorCreateRunLoopSource failed"; - return false; - } - CFRunLoopAddSource(run_loop(), scoped_fd_source, kCFRunLoopCommonModes); - - // Transfer ownership of scoped_fdref and fd_source to controller. - controller->Init(scoped_fdref.release(), callback_types, - scoped_fd_source.release(), persistent); - } else { - // It's illegal to use this function to listen on 2 separate fds with the - // same |controller|. - if (CFFileDescriptorGetNativeDescriptor(fdref) != fd) { - NOTREACHED() << "FDs don't match: " - << CFFileDescriptorGetNativeDescriptor(fdref) - << " != " << fd; - return false; - } - if (persistent != controller->is_persistent_) { - NOTREACHED() << "persistent doesn't match"; - return false; - } - - // Combine old/new event masks. - CFFileDescriptorDisableCallBacks(fdref, controller->callback_types_); - controller->callback_types_ |= callback_types; - CFFileDescriptorEnableCallBacks(fdref, controller->callback_types_); - } - - controller->set_watcher(delegate); - controller->set_pump(weak_factory_.GetWeakPtr()); - - return true; -} - -void MessagePumpIOSForIO::RemoveRunLoopSource(CFRunLoopSourceRef source) { - CFRunLoopRemoveSource(run_loop(), source, kCFRunLoopCommonModes); -} - -// static -void MessagePumpIOSForIO::HandleFdIOEvent(CFFileDescriptorRef fdref, - CFOptionFlags callback_types, - void* context) { - FdWatchController* controller = static_cast(context); - DCHECK_EQ(fdref, controller->fdref_.get()); - - // Ensure that |fdref| will remain live for the duration of this function - // call even if |controller| is deleted or |StopWatchingFileDescriptor()| is - // called, either of which will cause |fdref| to be released. - ScopedCFTypeRef scoped_fdref( - fdref, base::scoped_policy::RETAIN); - - int fd = CFFileDescriptorGetNativeDescriptor(fdref); - MessagePumpIOSForIO* pump = controller->pump().get(); - DCHECK(pump); - if (callback_types & kCFFileDescriptorWriteCallBack) - controller->OnFileCanWriteWithoutBlocking(fd, pump); - - // Perform the read callback only if the file descriptor has not been - // invalidated in the write callback. As |FdWatchController| invalidates - // its file descriptor on destruction, the file descriptor being valid also - // guarantees that |controller| has not been deleted. - if (callback_types & kCFFileDescriptorReadCallBack && - CFFileDescriptorIsValid(fdref)) { - DCHECK_EQ(fdref, controller->fdref_.get()); - controller->OnFileCanReadWithoutBlocking(fd, pump); - } - - // Re-enable callbacks after the read/write if the file descriptor is still - // valid and the controller is persistent. - if (CFFileDescriptorIsValid(fdref) && controller->is_persistent_) { - DCHECK_EQ(fdref, controller->fdref_.get()); - CFFileDescriptorEnableCallBacks(fdref, callback_types); - } -} - -} // namespace base diff --git a/message_loop/message_pump_io_ios.h b/message_loop/message_pump_io_ios.h deleted file mode 100644 index b39054416..000000000 --- a/message_loop/message_pump_io_ios.h +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 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_IO_IOS_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_IO_IOS_H_ - -#include "base/base_export.h" -#include "base/mac/scoped_cffiledescriptorref.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_pump_mac.h" -#include "base/message_loop/watchable_io_message_pump_posix.h" -#include "base/threading/thread_checker.h" - -namespace base { - -// This file introduces a class to monitor sockets and issue callbacks when -// sockets are ready for I/O on iOS. -class BASE_EXPORT MessagePumpIOSForIO : public MessagePumpNSRunLoop, - public WatchableIOMessagePumpPosix { - public: - class FdWatchController : public FdWatchControllerInterface { - public: - explicit FdWatchController(const Location& from_here); - - // Implicitly calls StopWatchingFileDescriptor. - ~FdWatchController() override; - - // FdWatchControllerInterface: - bool StopWatchingFileDescriptor() override; - - private: - friend class MessagePumpIOSForIO; - friend class MessagePumpIOSForIOTest; - - // Called by MessagePumpIOSForIO, ownership of |fdref| and |fd_source| - // is transferred to this object. - void Init(CFFileDescriptorRef fdref, - CFOptionFlags callback_types, - CFRunLoopSourceRef fd_source, - bool is_persistent); - - void set_pump(base::WeakPtr pump) { pump_ = pump; } - const base::WeakPtr& pump() const { return pump_; } - - void set_watcher(FdWatcher* watcher) { watcher_ = watcher; } - - void OnFileCanReadWithoutBlocking(int fd, MessagePumpIOSForIO* pump); - void OnFileCanWriteWithoutBlocking(int fd, MessagePumpIOSForIO* pump); - - bool is_persistent_ = false; // false if this event is one-shot. - base::mac::ScopedCFFileDescriptorRef fdref_; - CFOptionFlags callback_types_ = 0; - base::ScopedCFTypeRef fd_source_; - base::WeakPtr pump_; - FdWatcher* watcher_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(FdWatchController); - }; - - MessagePumpIOSForIO(); - ~MessagePumpIOSForIO() override; - - bool WatchFileDescriptor(int fd, - bool persistent, - int mode, - FdWatchController* controller, - FdWatcher* delegate); - - void RemoveRunLoopSource(CFRunLoopSourceRef source); - - private: - friend class MessagePumpIOSForIOTest; - - static void HandleFdIOEvent(CFFileDescriptorRef fdref, - CFOptionFlags callback_types, - void* context); - - ThreadChecker watch_file_descriptor_caller_checker_; - - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIO); -}; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_IO_IOS_H_ diff --git a/message_loop/message_pump_io_ios_unittest.cc b/message_loop/message_pump_io_ios_unittest.cc deleted file mode 100644 index 4d15d44db..000000000 --- a/message_loop/message_pump_io_ios_unittest.cc +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 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_io_ios.h" - -#include - -#include "base/macros.h" -#include "base/posix/eintr_wrapper.h" -#include "base/test/gtest_util.h" -#include "base/threading/thread.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -class MessagePumpIOSForIOTest : public testing::Test { - protected: - MessagePumpIOSForIOTest() = default; - ~MessagePumpIOSForIOTest() override = default; - - void SetUp() override { - int ret = pipe(pipefds_); - ASSERT_EQ(0, ret); - ret = pipe(alternate_pipefds_); - ASSERT_EQ(0, ret); - } - - void TearDown() override { - if (IGNORE_EINTR(close(pipefds_[0])) < 0) - PLOG(ERROR) << "close"; - if (IGNORE_EINTR(close(pipefds_[1])) < 0) - PLOG(ERROR) << "close"; - } - - void HandleFdIOEvent(MessagePumpForIO::FdWatchController* watcher) { - MessagePumpIOSForIO::HandleFdIOEvent(watcher->fdref_.get(), - kCFFileDescriptorReadCallBack | kCFFileDescriptorWriteCallBack, - watcher); - } - - int pipefds_[2]; - int alternate_pipefds_[2]; - - private: - DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIOTest); -}; - -namespace { - -// Concrete implementation of MessagePumpIOSForIO::FdWatcher that does -// nothing useful. -class StupidWatcher : public MessagePumpIOSForIO::FdWatcher { - public: - ~StupidWatcher() override {} - - // base:MessagePumpIOSForIO::FdWatcher interface - void OnFileCanReadWithoutBlocking(int fd) override {} - void OnFileCanWriteWithoutBlocking(int fd) override {} -}; - -class BaseWatcher : public MessagePumpIOSForIO::FdWatcher { - public: - BaseWatcher(MessagePumpIOSForIO::FdWatchController* controller) - : controller_(controller) { - DCHECK(controller_); - } - ~BaseWatcher() override {} - - // MessagePumpIOSForIO::FdWatcher interface - void OnFileCanReadWithoutBlocking(int /* fd */) override { NOTREACHED(); } - - void OnFileCanWriteWithoutBlocking(int /* fd */) override { NOTREACHED(); } - - protected: - MessagePumpIOSForIO::FdWatchController* controller_; -}; - -class DeleteWatcher : public BaseWatcher { - public: - explicit DeleteWatcher(MessagePumpIOSForIO::FdWatchController* controller) - : BaseWatcher(controller) {} - - ~DeleteWatcher() override { DCHECK(!controller_); } - - void OnFileCanWriteWithoutBlocking(int /* fd */) override { - DCHECK(controller_); - delete controller_; - controller_ = NULL; - } -}; - -TEST_F(MessagePumpIOSForIOTest, DeleteWatcher) { - std::unique_ptr pump(new MessagePumpIOSForIO); - MessagePumpIOSForIO::FdWatchController* watcher = - new MessagePumpIOSForIO::FdWatchController(FROM_HERE); - DeleteWatcher delegate(watcher); - pump->WatchFileDescriptor(pipefds_[1], - false, MessagePumpIOSForIO::WATCH_READ_WRITE, watcher, &delegate); - - // Spoof a callback. - HandleFdIOEvent(watcher); -} - -class StopWatcher : public BaseWatcher { - public: - StopWatcher(MessagePumpIOSForIO::FdWatchController* controller, - MessagePumpIOSForIO* pump, - int fd_to_start_watching = -1) - : BaseWatcher(controller), - pump_(pump), - fd_to_start_watching_(fd_to_start_watching) {} - - ~StopWatcher() override {} - - void OnFileCanWriteWithoutBlocking(int /* fd */) override { - controller_->StopWatchingFileDescriptor(); - if (fd_to_start_watching_ >= 0) { - pump_->WatchFileDescriptor(fd_to_start_watching_, - false, MessagePumpIOSForIO::WATCH_READ_WRITE, controller_, this); - } - } - - private: - MessagePumpIOSForIO* pump_; - int fd_to_start_watching_; -}; - -TEST_F(MessagePumpIOSForIOTest, StopWatcher) { - std::unique_ptr pump(new MessagePumpIOSForIO); - MessagePumpIOSForIO::FdWatchController watcher(FROM_HERE); - StopWatcher delegate(&watcher, pump.get()); - pump->WatchFileDescriptor(pipefds_[1], - false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate); - - // Spoof a callback. - HandleFdIOEvent(&watcher); -} - -TEST_F(MessagePumpIOSForIOTest, StopWatcherAndWatchSomethingElse) { - std::unique_ptr pump(new MessagePumpIOSForIO); - MessagePumpIOSForIO::FdWatchController watcher(FROM_HERE); - StopWatcher delegate(&watcher, pump.get(), alternate_pipefds_[1]); - pump->WatchFileDescriptor(pipefds_[1], - false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate); - - // Spoof a callback. - HandleFdIOEvent(&watcher); -} - -} // namespace - -} // namespace base diff --git a/message_loop/message_pump_libevent.cc b/message_loop/message_pump_libevent.cc deleted file mode 100644 index 2a595e5fe..000000000 --- a/message_loop/message_pump_libevent.cc +++ /dev/null @@ -1,350 +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_libevent.h" - -#include -#include - -#include - -#include "base/auto_reset.h" -#include "base/compiler_specific.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#include "base/third_party/libevent/event.h" -#include "base/time/time.h" -#include "base/trace_event/trace_event.h" -#include "build/build_config.h" - -#if defined(OS_MACOSX) -#include "base/mac/scoped_nsautorelease_pool.h" -#endif - -// Lifecycle of struct event -// Libevent uses two main data structures: -// struct event_base (of which there is one per message pump), and -// struct event (of which there is roughly one per socket). -// The socket's struct event is created in -// MessagePumpLibevent::WatchFileDescriptor(), -// is owned by the FdWatchController, and is destroyed in -// StopWatchingFileDescriptor(). -// It is moved into and out of lists in struct event_base by -// the libevent functions event_add() and event_del(). -// -// TODO(dkegel): -// At the moment bad things happen if a FdWatchController -// is active after its MessagePumpLibevent has been destroyed. -// See MessageLoopTest.FdWatchControllerOutlivesMessageLoop -// Not clear yet whether that situation occurs in practice, -// but if it does, we need to fix it. - -namespace base { - -MessagePumpLibevent::FdWatchController::FdWatchController( - const Location& from_here) - : FdWatchControllerInterface(from_here) {} - -MessagePumpLibevent::FdWatchController::~FdWatchController() { - if (event_) { - StopWatchingFileDescriptor(); - } - if (was_destroyed_) { - DCHECK(!*was_destroyed_); - *was_destroyed_ = true; - } -} - -bool MessagePumpLibevent::FdWatchController::StopWatchingFileDescriptor() { - std::unique_ptr e = ReleaseEvent(); - if (!e) - return true; - - // event_del() is a no-op if the event isn't active. - int rv = event_del(e.get()); - pump_ = nullptr; - watcher_ = nullptr; - return (rv == 0); -} - -void MessagePumpLibevent::FdWatchController::Init(std::unique_ptr e) { - DCHECK(e); - DCHECK(!event_); - - event_ = std::move(e); -} - -std::unique_ptr MessagePumpLibevent::FdWatchController::ReleaseEvent() { - return std::move(event_); -} - -void MessagePumpLibevent::FdWatchController::OnFileCanReadWithoutBlocking( - int fd, - MessagePumpLibevent* pump) { - // Since OnFileCanWriteWithoutBlocking() gets called first, it can stop - // watching the file descriptor. - if (!watcher_) - return; - watcher_->OnFileCanReadWithoutBlocking(fd); -} - -void MessagePumpLibevent::FdWatchController::OnFileCanWriteWithoutBlocking( - int fd, - MessagePumpLibevent* pump) { - DCHECK(watcher_); - watcher_->OnFileCanWriteWithoutBlocking(fd); -} - -MessagePumpLibevent::MessagePumpLibevent() - : keep_running_(true), - in_run_(false), - processed_io_events_(false), - event_base_(event_base_new()), - wakeup_pipe_in_(-1), - wakeup_pipe_out_(-1) { - if (!Init()) - NOTREACHED(); -} - -MessagePumpLibevent::~MessagePumpLibevent() { - DCHECK(wakeup_event_); - DCHECK(event_base_); - event_del(wakeup_event_); - delete wakeup_event_; - if (wakeup_pipe_in_ >= 0) { - if (IGNORE_EINTR(close(wakeup_pipe_in_)) < 0) - DPLOG(ERROR) << "close"; - } - if (wakeup_pipe_out_ >= 0) { - if (IGNORE_EINTR(close(wakeup_pipe_out_)) < 0) - DPLOG(ERROR) << "close"; - } - event_base_free(event_base_); -} - -bool MessagePumpLibevent::WatchFileDescriptor(int fd, - bool persistent, - int mode, - FdWatchController* controller, - FdWatcher* delegate) { - DCHECK_GE(fd, 0); - DCHECK(controller); - DCHECK(delegate); - DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE); - // WatchFileDescriptor should be called on the pump thread. It is not - // threadsafe, and your watcher may never be registered. - DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread()); - - int event_mask = persistent ? EV_PERSIST : 0; - if (mode & WATCH_READ) { - event_mask |= EV_READ; - } - if (mode & WATCH_WRITE) { - event_mask |= EV_WRITE; - } - - std::unique_ptr evt(controller->ReleaseEvent()); - if (!evt) { - // Ownership is transferred to the controller. - evt.reset(new event); - } else { - // Make sure we don't pick up any funky internal libevent masks. - int old_interest_mask = evt->ev_events & (EV_READ | EV_WRITE | EV_PERSIST); - - // Combine old/new event masks. - event_mask |= old_interest_mask; - - // Must disarm the event before we can reuse it. - event_del(evt.get()); - - // It's illegal to use this function to listen on 2 separate fds with the - // same |controller|. - if (EVENT_FD(evt.get()) != fd) { - NOTREACHED() << "FDs don't match" << EVENT_FD(evt.get()) << "!=" << fd; - return false; - } - } - - // Set current interest mask and message pump for this event. - event_set(evt.get(), fd, event_mask, OnLibeventNotification, controller); - - // Tell libevent which message pump this socket will belong to when we add it. - if (event_base_set(event_base_, evt.get())) { - DPLOG(ERROR) << "event_base_set(fd=" << EVENT_FD(evt.get()) << ")"; - return false; - } - - // Add this socket to the list of monitored sockets. - if (event_add(evt.get(), nullptr)) { - DPLOG(ERROR) << "event_add failed(fd=" << EVENT_FD(evt.get()) << ")"; - return false; - } - - controller->Init(std::move(evt)); - controller->set_watcher(delegate); - controller->set_pump(this); - return true; -} - -// Tell libevent to break out of inner loop. -static void timer_callback(int fd, short events, void* context) { - event_base_loopbreak((struct event_base*)context); -} - -// Reentrant! -void MessagePumpLibevent::Run(Delegate* delegate) { - AutoReset auto_reset_keep_running(&keep_running_, true); - AutoReset auto_reset_in_run(&in_run_, true); - - // event_base_loopexit() + EVLOOP_ONCE is leaky, see http://crbug.com/25641. - // Instead, make our own timer and reuse it on each call to event_base_loop(). - std::unique_ptr timer_event(new event); - - for (;;) { -#if defined(OS_MACOSX) - mac::ScopedNSAutoreleasePool autorelease_pool; -#endif - - bool did_work = delegate->DoWork(); - if (!keep_running_) - break; - - event_base_loop(event_base_, EVLOOP_NONBLOCK); - did_work |= processed_io_events_; - processed_io_events_ = false; - if (!keep_running_) - break; - - did_work |= delegate->DoDelayedWork(&delayed_work_time_); - if (!keep_running_) - break; - - if (did_work) - continue; - - did_work = delegate->DoIdleWork(); - if (!keep_running_) - break; - - if (did_work) - continue; - - // EVLOOP_ONCE tells libevent to only block once, - // but to service all pending events when it wakes up. - if (delayed_work_time_.is_null()) { - event_base_loop(event_base_, EVLOOP_ONCE); - } else { - TimeDelta delay = delayed_work_time_ - TimeTicks::Now(); - if (delay > TimeDelta()) { - struct timeval poll_tv; - poll_tv.tv_sec = delay.InSeconds(); - poll_tv.tv_usec = delay.InMicroseconds() % Time::kMicrosecondsPerSecond; - event_set(timer_event.get(), -1, 0, timer_callback, event_base_); - event_base_set(event_base_, timer_event.get()); - event_add(timer_event.get(), &poll_tv); - event_base_loop(event_base_, EVLOOP_ONCE); - event_del(timer_event.get()); - } else { - // It looks like delayed_work_time_ indicates a time in the past, so we - // need to call DoDelayedWork now. - delayed_work_time_ = TimeTicks(); - } - } - - if (!keep_running_) - break; - } -} - -void MessagePumpLibevent::Quit() { - 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(); -} - -void MessagePumpLibevent::ScheduleWork() { - // Tell libevent (in a threadsafe way) that it should break out of its loop. - char buf = 0; - int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1)); - DCHECK(nwrite == 1 || errno == EAGAIN) - << "[nwrite:" << nwrite << "] [errno:" << errno << "]"; -} - -void MessagePumpLibevent::ScheduleDelayedWork( - const TimeTicks& delayed_work_time) { - // We know that we can't be blocked on Wait right now since this method can - // only be called on the same thread as Run, so we only need to update our - // record of how long to sleep when we do sleep. - delayed_work_time_ = delayed_work_time; -} - -bool MessagePumpLibevent::Init() { - int fds[2]; - if (!CreateLocalNonBlockingPipe(fds)) { - DPLOG(ERROR) << "pipe creation failed"; - return false; - } - wakeup_pipe_out_ = fds[0]; - wakeup_pipe_in_ = fds[1]; - - wakeup_event_ = new event; - event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST, - OnWakeup, this); - event_base_set(event_base_, wakeup_event_); - - if (event_add(wakeup_event_, nullptr)) - return false; - return true; -} - -// static -void MessagePumpLibevent::OnLibeventNotification(int fd, - short flags, - void* context) { - FdWatchController* controller = static_cast(context); - DCHECK(controller); - TRACE_EVENT2("toplevel", "MessagePumpLibevent::OnLibeventNotification", - "src_file", controller->created_from_location().file_name(), - "src_func", controller->created_from_location().function_name()); - TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION heap_profiler_scope( - controller->created_from_location().file_name()); - - MessagePumpLibevent* pump = controller->pump(); - pump->processed_io_events_ = true; - - if ((flags & (EV_READ | EV_WRITE)) == (EV_READ | EV_WRITE)) { - // Both callbacks will be called. It is necessary to check that |controller| - // is not destroyed. - bool controller_was_destroyed = false; - controller->was_destroyed_ = &controller_was_destroyed; - controller->OnFileCanWriteWithoutBlocking(fd, pump); - if (!controller_was_destroyed) - controller->OnFileCanReadWithoutBlocking(fd, pump); - if (!controller_was_destroyed) - controller->was_destroyed_ = nullptr; - } else if (flags & EV_WRITE) { - controller->OnFileCanWriteWithoutBlocking(fd, pump); - } else if (flags & EV_READ) { - controller->OnFileCanReadWithoutBlocking(fd, pump); - } -} - -// Called if a byte is received on the wakeup pipe. -// static -void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) { - MessagePumpLibevent* that = static_cast(context); - DCHECK(that->wakeup_pipe_out_ == socket); - - // Remove and discard the wakeup byte. - char buf; - int nread = HANDLE_EINTR(read(socket, &buf, 1)); - DCHECK_EQ(nread, 1); - that->processed_io_events_ = true; - // Tell libevent to break out of inner loop. - event_base_loopbreak(that->event_base_); -} - -} // namespace base diff --git a/message_loop/message_pump_libevent.h b/message_loop/message_pump_libevent.h deleted file mode 100644 index 002c36cb7..000000000 --- a/message_loop/message_pump_libevent.h +++ /dev/null @@ -1,123 +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_LIBEVENT_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_ - -#include - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/message_loop/message_pump.h" -#include "base/message_loop/watchable_io_message_pump_posix.h" -#include "base/threading/thread_checker.h" -#include "base/time/time.h" - -// Declare structs we need from libevent.h rather than including it -struct event_base; -struct event; - -namespace base { - -// Class to monitor sockets and issue callbacks when sockets are ready for I/O -// TODO(dkegel): add support for background file IO somehow -class BASE_EXPORT MessagePumpLibevent : public MessagePump, - public WatchableIOMessagePumpPosix { - public: - class FdWatchController : public FdWatchControllerInterface { - public: - explicit FdWatchController(const Location& from_here); - - // Implicitly calls StopWatchingFileDescriptor. - ~FdWatchController() override; - - // FdWatchControllerInterface: - bool StopWatchingFileDescriptor() override; - - private: - friend class MessagePumpLibevent; - friend class MessagePumpLibeventTest; - - // Called by MessagePumpLibevent. - void Init(std::unique_ptr e); - - // Used by MessagePumpLibevent to take ownership of |event_|. - std::unique_ptr ReleaseEvent(); - - void set_pump(MessagePumpLibevent* pump) { pump_ = pump; } - MessagePumpLibevent* pump() const { return pump_; } - - void set_watcher(FdWatcher* watcher) { watcher_ = watcher; } - - void OnFileCanReadWithoutBlocking(int fd, MessagePumpLibevent* pump); - void OnFileCanWriteWithoutBlocking(int fd, MessagePumpLibevent* pump); - - std::unique_ptr event_; - MessagePumpLibevent* pump_ = nullptr; - FdWatcher* watcher_ = nullptr; - // If this pointer is non-NULL, the pointee is set to true in the - // destructor. - bool* was_destroyed_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(FdWatchController); - }; - - MessagePumpLibevent(); - ~MessagePumpLibevent() override; - - bool WatchFileDescriptor(int fd, - bool persistent, - int mode, - FdWatchController* controller, - FdWatcher* delegate); - - // MessagePump methods: - void Run(Delegate* delegate) override; - void Quit() override; - void ScheduleWork() override; - void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; - - private: - friend class MessagePumpLibeventTest; - - // Risky part of constructor. Returns true on success. - bool Init(); - - // Called by libevent to tell us a registered FD can be read/written to. - static void OnLibeventNotification(int fd, short flags, void* context); - - // Unix pipe used to implement ScheduleWork() - // ... callback; called by libevent inside Run() when pipe is ready to read - static void OnWakeup(int socket, short flags, void* context); - - // This flag is set to false when Run should return. - bool keep_running_; - - // This flag is set when inside Run. - bool in_run_; - - // This flag is set if libevent has processed I/O events. - bool processed_io_events_; - - // The time at which we should call DoDelayedWork. - TimeTicks delayed_work_time_; - - // Libevent dispatcher. Watches all sockets registered with it, and sends - // readiness callbacks when a socket is ready for I/O. - event_base* event_base_; - - // ... write end; ScheduleWork() writes a single byte to it - int wakeup_pipe_in_; - // ... read end; OnWakeup reads it and then breaks Run() out of its sleep - int wakeup_pipe_out_; - // ... libevent wrapper for read end - event* wakeup_event_; - - ThreadChecker watch_file_descriptor_caller_checker_; - DISALLOW_COPY_AND_ASSIGN(MessagePumpLibevent); -}; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_ diff --git a/message_loop/message_pump_libevent_unittest.cc b/message_loop/message_pump_libevent_unittest.cc deleted file mode 100644 index 55eb0b4d7..000000000 --- a/message_loop/message_pump_libevent_unittest.cc +++ /dev/null @@ -1,263 +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_libevent.h" - -#include - -#include -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/files/file_util.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" -#include "base/posix/eintr_wrapper.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/synchronization/waitable_event.h" -#include "base/synchronization/waitable_event_watcher.h" -#include "base/test/gtest_util.h" -#include "base/third_party/libevent/event.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -class MessagePumpLibeventTest : public testing::Test { - protected: - MessagePumpLibeventTest() - : ui_loop_(new MessageLoop(MessageLoop::TYPE_UI)), - io_thread_("MessagePumpLibeventTestIOThread") {} - ~MessagePumpLibeventTest() override = default; - - void SetUp() override { - Thread::Options options(MessageLoop::TYPE_IO, 0); - ASSERT_TRUE(io_thread_.StartWithOptions(options)); - ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type()); - int ret = pipe(pipefds_); - ASSERT_EQ(0, ret); - } - - void TearDown() override { - if (IGNORE_EINTR(close(pipefds_[0])) < 0) - PLOG(ERROR) << "close"; - if (IGNORE_EINTR(close(pipefds_[1])) < 0) - PLOG(ERROR) << "close"; - } - - void WaitUntilIoThreadStarted() { - ASSERT_TRUE(io_thread_.WaitUntilThreadStarted()); - } - - scoped_refptr io_runner() const { - return io_thread_.task_runner(); - } - - void OnLibeventNotification( - MessagePumpLibevent* pump, - MessagePumpLibevent::FdWatchController* controller) { - pump->OnLibeventNotification(0, EV_WRITE | EV_READ, controller); - } - - int pipefds_[2]; - std::unique_ptr ui_loop_; - - private: - Thread io_thread_; -}; - -namespace { - -// Concrete implementation of MessagePumpLibevent::FdWatcher that does -// nothing useful. -class StupidWatcher : public MessagePumpLibevent::FdWatcher { - public: - ~StupidWatcher() override = default; - - // base:MessagePumpLibevent::FdWatcher interface - void OnFileCanReadWithoutBlocking(int fd) override {} - void OnFileCanWriteWithoutBlocking(int fd) override {} -}; - -TEST_F(MessagePumpLibeventTest, QuitOutsideOfRun) { - std::unique_ptr pump(new MessagePumpLibevent); - ASSERT_DCHECK_DEATH(pump->Quit()); -} - -class BaseWatcher : public MessagePumpLibevent::FdWatcher { - public: - explicit BaseWatcher(MessagePumpLibevent::FdWatchController* controller) - : controller_(controller) { - DCHECK(controller_); - } - ~BaseWatcher() override = default; - - // base:MessagePumpLibevent::FdWatcher interface - void OnFileCanReadWithoutBlocking(int /* fd */) override { NOTREACHED(); } - - void OnFileCanWriteWithoutBlocking(int /* fd */) override { NOTREACHED(); } - - protected: - MessagePumpLibevent::FdWatchController* controller_; -}; - -class DeleteWatcher : public BaseWatcher { - public: - explicit DeleteWatcher(MessagePumpLibevent::FdWatchController* controller) - : BaseWatcher(controller) {} - - ~DeleteWatcher() override { DCHECK(!controller_); } - - void OnFileCanWriteWithoutBlocking(int /* fd */) override { - DCHECK(controller_); - delete controller_; - controller_ = nullptr; - } -}; - -TEST_F(MessagePumpLibeventTest, DeleteWatcher) { - std::unique_ptr pump(new MessagePumpLibevent); - MessagePumpLibevent::FdWatchController* watcher = - new MessagePumpLibevent::FdWatchController(FROM_HERE); - DeleteWatcher delegate(watcher); - pump->WatchFileDescriptor(pipefds_[1], - false, MessagePumpLibevent::WATCH_READ_WRITE, watcher, &delegate); - - // Spoof a libevent notification. - OnLibeventNotification(pump.get(), watcher); -} - -class StopWatcher : public BaseWatcher { - public: - explicit StopWatcher(MessagePumpLibevent::FdWatchController* controller) - : BaseWatcher(controller) {} - - ~StopWatcher() override = default; - - void OnFileCanWriteWithoutBlocking(int /* fd */) override { - controller_->StopWatchingFileDescriptor(); - } -}; - -TEST_F(MessagePumpLibeventTest, StopWatcher) { - std::unique_ptr pump(new MessagePumpLibevent); - MessagePumpLibevent::FdWatchController watcher(FROM_HERE); - StopWatcher delegate(&watcher); - pump->WatchFileDescriptor(pipefds_[1], - false, MessagePumpLibevent::WATCH_READ_WRITE, &watcher, &delegate); - - // Spoof a libevent notification. - OnLibeventNotification(pump.get(), &watcher); -} - -void QuitMessageLoopAndStart(const Closure& quit_closure) { - quit_closure.Run(); - - RunLoop runloop(RunLoop::Type::kNestableTasksAllowed); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, runloop.QuitClosure()); - runloop.Run(); -} - -class NestedPumpWatcher : public MessagePumpLibevent::FdWatcher { - public: - NestedPumpWatcher() = default; - ~NestedPumpWatcher() override = default; - - void OnFileCanReadWithoutBlocking(int /* fd */) override { - RunLoop runloop; - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&QuitMessageLoopAndStart, runloop.QuitClosure())); - runloop.Run(); - } - - void OnFileCanWriteWithoutBlocking(int /* fd */) override {} -}; - -TEST_F(MessagePumpLibeventTest, NestedPumpWatcher) { - std::unique_ptr pump(new MessagePumpLibevent); - MessagePumpLibevent::FdWatchController watcher(FROM_HERE); - NestedPumpWatcher delegate; - pump->WatchFileDescriptor(pipefds_[1], - false, MessagePumpLibevent::WATCH_READ, &watcher, &delegate); - - // Spoof a libevent notification. - OnLibeventNotification(pump.get(), &watcher); -} - -void FatalClosure() { - FAIL() << "Reached fatal closure."; -} - -class QuitWatcher : public BaseWatcher { - public: - QuitWatcher(MessagePumpLibevent::FdWatchController* controller, - base::Closure quit_closure) - : BaseWatcher(controller), quit_closure_(std::move(quit_closure)) {} - - void OnFileCanReadWithoutBlocking(int /* fd */) override { - // Post a fatal closure to the MessageLoop before we quit it. - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, BindOnce(&FatalClosure)); - - quit_closure_.Run(); - } - - private: - base::Closure quit_closure_; -}; - -void WriteFDWrapper(const int fd, - const char* buf, - int size, - WaitableEvent* event) { - ASSERT_TRUE(WriteFileDescriptor(fd, buf, size)); -} - -// Tests that MessagePumpLibevent quits immediately when it is quit from -// libevent's event_base_loop(). -TEST_F(MessagePumpLibeventTest, QuitWatcher) { - // Delete the old MessageLoop so that we can manage our own one here. - ui_loop_.reset(); - - MessagePumpLibevent* pump = new MessagePumpLibevent; // owned by |loop|. - MessageLoop loop(WrapUnique(pump)); - RunLoop run_loop; - MessagePumpLibevent::FdWatchController controller(FROM_HERE); - QuitWatcher delegate(&controller, run_loop.QuitClosure()); - WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC, - WaitableEvent::InitialState::NOT_SIGNALED); - std::unique_ptr watcher(new WaitableEventWatcher); - - // Tell the pump to watch the pipe. - pump->WatchFileDescriptor(pipefds_[0], false, MessagePumpLibevent::WATCH_READ, - &controller, &delegate); - - // Make the IO thread wait for |event| before writing to pipefds[1]. - const char buf = 0; - WaitableEventWatcher::EventCallback write_fd_task = - BindOnce(&WriteFDWrapper, pipefds_[1], &buf, 1); - io_runner()->PostTask( - FROM_HERE, BindOnce(IgnoreResult(&WaitableEventWatcher::StartWatching), - Unretained(watcher.get()), &event, - std::move(write_fd_task), io_runner())); - - // Queue |event| to signal on |loop|. - loop.task_runner()->PostTask( - FROM_HERE, BindOnce(&WaitableEvent::Signal, Unretained(&event))); - - // Now run the MessageLoop. - run_loop.Run(); - - // StartWatching can move |watcher| to IO thread. Release on IO thread. - io_runner()->PostTask(FROM_HERE, BindOnce(&WaitableEventWatcher::StopWatching, - Owned(watcher.release()))); -} - -} // namespace - -} // namespace base diff --git a/message_loop/message_pump_mac.h b/message_loop/message_pump_mac.h deleted file mode 100644 index fa88c3a76..000000000 --- a/message_loop/message_pump_mac.h +++ /dev/null @@ -1,404 +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. - -// The basis for all native run loops on the Mac is the CFRunLoop. It can be -// used directly, it can be used as the driving force behind the similar -// Foundation NSRunLoop, and it can be used to implement higher-level event -// loops such as the NSApplication event loop. -// -// This file introduces a basic CFRunLoop-based implementation of the -// MessagePump interface called CFRunLoopBase. CFRunLoopBase contains all -// of the machinery necessary to dispatch events to a delegate, but does not -// implement the specific run loop. Concrete subclasses must provide their -// own DoRun and Quit implementations. -// -// A concrete subclass that just runs a CFRunLoop loop is provided in -// MessagePumpCFRunLoop. For an NSRunLoop, the similar MessagePumpNSRunLoop -// is provided. -// -// For the application's event loop, an implementation based on AppKit's -// NSApplication event system is provided in MessagePumpNSApplication. -// -// Typically, MessagePumpNSApplication only makes sense on a Cocoa -// application's main thread. If a CFRunLoop-based message pump is needed on -// any other thread, one of the other concrete subclasses is preferable. -// MessagePumpMac::Create is defined, which returns a new NSApplication-based -// or NSRunLoop-based MessagePump subclass depending on which thread it is -// called on. - -#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_ - -#include "base/message_loop/message_pump.h" - - -#include - -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/timer_slack.h" -#include "build/build_config.h" - -#if defined(__OBJC__) -#if defined(OS_IOS) -#import -#else -#import - -// Clients must subclass NSApplication and implement this protocol if they use -// MessagePumpMac. -@protocol CrAppProtocol -// Must return true if -[NSApplication sendEvent:] is currently on the stack. -// See the comment for |CreateAutoreleasePool()| in the cc file for why this is -// necessary. -- (BOOL)isHandlingSendEvent; -@end -#endif // !defined(OS_IOS) -#endif // defined(__OBJC__) - -namespace base { - -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 BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump { - public: - // MessagePump: - void Run(Delegate* delegate) override; - void ScheduleWork() override; - void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; - void SetTimerSlack(TimerSlack timer_slack) override; - - protected: - // Needs access to CreateAutoreleasePool. - friend class MessagePumpScopedAutoreleasePool; - friend class TestMessagePumpCFRunLoopBase; - - // Tasks will be pumped in the run loop modes described by - // |initial_mode_mask|, which maps bits to the index of an internal array of - // run loop mode identifiers. - explicit MessagePumpCFRunLoopBase(int initial_mode_mask); - ~MessagePumpCFRunLoopBase() override; - - // Subclasses should implement the work they need to do in MessagePump::Run - // in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly. - // This arrangement is used because MessagePumpCFRunLoopBase needs to set - // up and tear down things before and after the "meat" of DoRun. - virtual void DoRun(Delegate* delegate) = 0; - - // Accessors for private data members to be used by subclasses. - CFRunLoopRef run_loop() const { return run_loop_; } - int nesting_level() const { return nesting_level_; } - int run_nesting_level() const { return run_nesting_level_; } - - // Sets this pump's delegate. Signals the appropriate sources if - // |delegateless_work_| is true. |delegate| can be NULL. - void SetDelegate(Delegate* delegate); - - // Return an autorelease pool to wrap around any work being performed. - // 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 AutoreleasePoolType* CreateAutoreleasePool(); - - // Enable and disable entries in |enabled_modes_| to match |mode_mask|. - void SetModeMask(int mode_mask); - - // Get the current mode mask from |enabled_modes_|. - int GetModeMask() const; - - private: - class ScopedModeEnabler; - - // The maximum number of run loop modes that can be monitored. - static constexpr int kNumModes = 4; - - // Marking timers as invalid at the right time helps significantly reduce - // power use (see the comment in RunDelayedWorkTimer()), however there is no - // public API for doing so. CFRuntime.h states that CFRuntimeBase, upon which - // the above timer invalidation functions are based, can change from release - // to release and should not be accessed directly (this struct last changed at - // least in 2008 in CF-476). - // - // This function uses private API to modify a test timer's valid state and - // uses public API to confirm that the private API changed the right bit. - static bool CanInvalidateCFRunLoopTimers(); - - // Sets a Core Foundation object's "invalid" bit to |valid|. Based on code - // from CFRunLoop.c. - static void ChromeCFRunLoopTimerSetValid(CFRunLoopTimerRef timer, bool valid); - - // Timer callback scheduled by ScheduleDelayedWork. This does not do any - // work, but it signals work_source_ so that delayed work can be performed - // within the appropriate priority constraints. - static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info); - - // Perform highest-priority work. This is associated with work_source_ - // signalled by ScheduleWork or RunDelayedWorkTimer. The static method calls - // the instance method; the instance method returns true if it resignalled - // work_source_ to be called again from the loop. - static void RunWorkSource(void* info); - bool RunWork(); - - // Perform idle-priority work. This is normally called by PreWaitObserver, - // but is also associated with idle_work_source_. When this function - // actually does perform idle work, it will resignal that source. The - // static method calls the instance method; the instance method returns - // true if idle work was done. - static void RunIdleWorkSource(void* info); - bool RunIdleWork(); - - // Perform work that may have been deferred because it was not runnable - // within a nested run loop. This is associated with - // nesting_deferred_work_source_ and is signalled by - // MaybeScheduleNestingDeferredWork when returning from a nested loop, - // so that an outer loop will be able to perform the necessary tasks if it - // permits nestable tasks. - static void RunNestingDeferredWorkSource(void* info); - bool RunNestingDeferredWork(); - - // Schedules possible nesting-deferred work to be processed before the run - // loop goes to sleep, exits, or begins processing sources at the top of its - // loop. If this function detects that a nested loop had run since the - // previous attempt to schedule nesting-deferred work, it will schedule a - // call to RunNestingDeferredWorkSource. - void MaybeScheduleNestingDeferredWork(); - - // Observer callback responsible for performing idle-priority work, before - // the run loop goes to sleep. Associated with idle_work_observer_. - static void PreWaitObserver(CFRunLoopObserverRef observer, - CFRunLoopActivity activity, void* info); - - // Observer callback called before the run loop processes any sources. - // Associated with pre_source_observer_. - static void PreSourceObserver(CFRunLoopObserverRef observer, - CFRunLoopActivity activity, void* info); - - // Observer callback called when the run loop starts and stops, at the - // beginning and end of calls to CFRunLoopRun. This is used to maintain - // nesting_level_. Associated with enter_exit_observer_. - static void EnterExitObserver(CFRunLoopObserverRef observer, - CFRunLoopActivity activity, void* info); - - // Called by EnterExitObserver after performing maintenance on nesting_level_. - // This allows subclasses an opportunity to perform additional processing on - // the basis of run loops starting and stopping. - virtual void EnterExitRunLoop(CFRunLoopActivity activity); - - // The thread's run loop. - CFRunLoopRef run_loop_; - - // The enabled modes. Posted tasks may run in any non-null entry. - std::unique_ptr enabled_modes_[kNumModes]; - - // The timer, sources, and observers are described above alongside their - // callbacks. - CFRunLoopTimerRef delayed_work_timer_; - CFRunLoopSourceRef work_source_; - CFRunLoopSourceRef idle_work_source_; - CFRunLoopSourceRef nesting_deferred_work_source_; - CFRunLoopObserverRef pre_wait_observer_; - CFRunLoopObserverRef pre_source_observer_; - CFRunLoopObserverRef enter_exit_observer_; - - // (weak) Delegate passed as an argument to the innermost Run call. - Delegate* delegate_; - - // The time that delayed_work_timer_ is scheduled to fire. This is tracked - // independently of CFRunLoopTimerGetNextFireDate(delayed_work_timer_) - // to be able to reset the timer properly after waking from system sleep. - // 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. - int nesting_level_; - - // The recursion depth (calculated in the same way as nesting_level_) of the - // innermost executing CFRunLoopRun loop started by a call to Run. - int run_nesting_level_; - - // The deepest (numerically highest) recursion depth encountered since the - // most recent attempt to run nesting-deferred work. - int deepest_nesting_level_; - - // "Delegateless" work flags are set when work is ready to be performed but - // must wait until a delegate is available to process it. This can happen - // when a MessagePumpCFRunLoopBase is instantiated and work arrives without - // any call to Run on the stack. The Run method will check for delegateless - // work on entry and redispatch it as needed once a delegate is available. - bool delegateless_work_; - bool delegateless_idle_work_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase); -}; - -class BASE_EXPORT MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { - public: - MessagePumpCFRunLoop(); - ~MessagePumpCFRunLoop() override; - - void DoRun(Delegate* delegate) override; - void Quit() override; - - private: - void EnterExitRunLoop(CFRunLoopActivity activity) override; - - // True if Quit is called to stop the innermost MessagePump - // (innermost_quittable_) but some other CFRunLoopRun loop (nesting_level_) - // is running inside the MessagePump's innermost Run call. - bool quit_pending_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop); -}; - -class BASE_EXPORT MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase { - public: - MessagePumpNSRunLoop(); - ~MessagePumpNSRunLoop() override; - - void DoRun(Delegate* delegate) override; - void Quit() override; - - private: - // A source that doesn't do anything but provide something signalable - // attached to the run loop. This source will be signalled when Quit - // is called, to cause the loop to wake up so that it can stop. - CFRunLoopSourceRef quit_source_; - - // False after Quit is called. - bool keep_running_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpNSRunLoop); -}; - -#if defined(OS_IOS) -// This is a fake message pump. It attaches sources to the main thread's -// CFRunLoop, so PostTask() will work, but it is unable to drive the loop -// directly, so calling Run() or Quit() are errors. -class MessagePumpUIApplication : public MessagePumpCFRunLoopBase { - public: - MessagePumpUIApplication(); - ~MessagePumpUIApplication() override; - void DoRun(Delegate* delegate) override; - void Quit() override; - - // This message pump can not spin the main message loop directly. Instead, - // call |Attach()| to set up a delegate. It is an error to call |Run()|. - virtual void Attach(Delegate* delegate); - - private: - RunLoop* run_loop_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpUIApplication); -}; - -#else - -// While in scope, permits posted tasks to be run in private AppKit run loop -// modes that would otherwise make the UI unresponsive. E.g., menu fade out. -class BASE_EXPORT ScopedPumpMessagesInPrivateModes { - public: - ScopedPumpMessagesInPrivateModes(); - ~ScopedPumpMessagesInPrivateModes(); - - int GetModeMaskForTest(); - - private: - DISALLOW_COPY_AND_ASSIGN(ScopedPumpMessagesInPrivateModes); -}; - -class MessagePumpNSApplication : public MessagePumpCFRunLoopBase { - public: - MessagePumpNSApplication(); - ~MessagePumpNSApplication() override; - - void DoRun(Delegate* delegate) override; - void Quit() override; - - private: - friend class ScopedPumpMessagesInPrivateModes; - - // False after Quit is called. - bool keep_running_; - - // True if DoRun is managing its own run loop as opposed to letting - // -[NSApplication run] handle it. The outermost run loop in the application - // is managed by -[NSApplication run], inner run loops are handled by a loop - // in DoRun. - bool running_own_loop_; - - DISALLOW_COPY_AND_ASSIGN(MessagePumpNSApplication); -}; - -class MessagePumpCrApplication : public MessagePumpNSApplication { - public: - MessagePumpCrApplication(); - ~MessagePumpCrApplication() override; - - protected: - // Returns nil if NSApp is currently in the middle of calling - // -sendEvent. Requires NSApp implementing CrAppProtocol. - AutoreleasePoolType* CreateAutoreleasePool() override; - - private: - DISALLOW_COPY_AND_ASSIGN(MessagePumpCrApplication); -}; -#endif // !defined(OS_IOS) - -class BASE_EXPORT MessagePumpMac { - public: - // If not on the main thread, returns a new instance of - // MessagePumpNSRunLoop. - // - // On the main thread, if NSApp exists and conforms to - // CrAppProtocol, creates an instances of MessagePumpCrApplication. - // - // Otherwise creates an instance of MessagePumpNSApplication using a - // default NSApplication. - static std::unique_ptr Create(); - -#if !defined(OS_IOS) - // If a pump is created before the required CrAppProtocol is - // created, the wrong MessagePump subclass could be used. - // 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. - static bool UsingCrApp(); - - // Wrapper to query -[NSApp isHandlingSendEvent] from C++ code. - // Requires NSApp to implement CrAppProtocol. - 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/message_loop/message_pump_mac.mm b/message_loop/message_pump_mac.mm deleted file mode 100644 index fb2520179..000000000 --- a/message_loop/message_pump_mac.mm +++ /dev/null @@ -1,935 +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 "base/message_loop/message_pump_mac.h" - -#import - -#include -#include - -#include "base/auto_reset.h" -#include "base/logging.h" -#include "base/mac/call_with_eh_frame.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/macros.h" -#include "base/message_loop/timer_slack.h" -#include "base/run_loop.h" -#include "base/time/time.h" -#include "build/build_config.h" - -#if !defined(OS_IOS) -#import -#endif // !defined(OS_IOS) - -namespace base { - -const CFStringRef kMessageLoopExclusiveRunLoopMode = - CFSTR("kMessageLoopExclusiveRunLoopMode"); - -namespace { - -// Mask that determines which modes to use. -enum { kCommonModeMask = 0x1, kAllModesMask = 0xf }; - -// Modes to use for MessagePumpNSApplication that are considered "safe". -// Currently just common and exclusive modes. Ideally, messages would be pumped -// in all modes, but that interacts badly with app modal dialogs (e.g. NSAlert). -enum { kNSApplicationModalSafeModeMask = 0x3 }; - -void NoOp(void* info) { -} - -constexpr CFTimeInterval kCFTimeIntervalMax = - std::numeric_limits::max(); - -#if !defined(OS_IOS) -// Set to true if MessagePumpMac::Create() is called before NSApp is -// initialized. Only accessed from the main thread. -bool g_not_using_cr_app = false; - -// The MessagePump controlling [NSApp run]. -MessagePumpNSApplication* g_app_pump; - -// Various CoreFoundation definitions. -typedef struct __CFRuntimeBase { - uintptr_t _cfisa; - uint8_t _cfinfo[4]; - uint32_t _rc; -} CFRuntimeBase; - -#if defined(__BIG_ENDIAN__) -#define __CF_BIG_ENDIAN__ 1 -#define __CF_LITTLE_ENDIAN__ 0 -#endif - -#if defined(__LITTLE_ENDIAN__) -#define __CF_LITTLE_ENDIAN__ 1 -#define __CF_BIG_ENDIAN__ 0 -#endif - -#define CF_INFO_BITS (!!(__CF_BIG_ENDIAN__)*3) - -#define __CFBitfieldMask(N1, N2) \ - ((((UInt32)~0UL) << (31UL - (N1) + (N2))) >> (31UL - N1)) -#define __CFBitfieldSetValue(V, N1, N2, X) \ - ((V) = ((V) & ~__CFBitfieldMask(N1, N2)) | \ - (((X) << (N2)) & __CFBitfieldMask(N1, N2))) - -// Marking timers as invalid at the right time by flipping their valid bit helps -// significantly reduce power use (see the explanation in -// RunDelayedWorkTimer()), however there is no public API for doing so. -// CFRuntime.h states that CFRuntimeBase can change from release to release -// and should not be accessed directly. The last known change of this struct -// occurred in 2008 in CF-476 / 10.5; unfortunately the source for 10.11 and -// 10.12 is not available for inspection at this time. -// CanInvalidateCFRunLoopTimers() will at least prevent us from invalidating -// timers if this function starts flipping the wrong bit on a future OS release. -void __ChromeCFRunLoopTimerSetValid(CFRunLoopTimerRef timer, bool valid) { - __CFBitfieldSetValue(((CFRuntimeBase*)timer)->_cfinfo[CF_INFO_BITS], 3, 3, - valid); -} -#endif // !defined(OS_IOS) - -} // namespace - -// A scoper for autorelease pools created from message pump run loops. -// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare -// case where an autorelease pool needs to be passed in. -class MessagePumpScopedAutoreleasePool { - public: - explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : - pool_(pump->CreateAutoreleasePool()) { - } - ~MessagePumpScopedAutoreleasePool() { - [pool_ drain]; - } - - private: - NSAutoreleasePool* pool_; - DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); -}; - -class MessagePumpCFRunLoopBase::ScopedModeEnabler { - public: - ScopedModeEnabler(MessagePumpCFRunLoopBase* owner, int mode_index) - : owner_(owner), mode_index_(mode_index) { - CFRunLoopRef loop = owner_->run_loop_; - CFRunLoopAddTimer(loop, owner_->delayed_work_timer_, mode()); - CFRunLoopAddSource(loop, owner_->work_source_, mode()); - CFRunLoopAddSource(loop, owner_->idle_work_source_, mode()); - CFRunLoopAddSource(loop, owner_->nesting_deferred_work_source_, mode()); - CFRunLoopAddObserver(loop, owner_->pre_wait_observer_, mode()); - CFRunLoopAddObserver(loop, owner_->pre_source_observer_, mode()); - CFRunLoopAddObserver(loop, owner_->enter_exit_observer_, mode()); - } - - ~ScopedModeEnabler() { - CFRunLoopRef loop = owner_->run_loop_; - CFRunLoopRemoveObserver(loop, owner_->enter_exit_observer_, mode()); - CFRunLoopRemoveObserver(loop, owner_->pre_source_observer_, mode()); - CFRunLoopRemoveObserver(loop, owner_->pre_wait_observer_, mode()); - CFRunLoopRemoveSource(loop, owner_->nesting_deferred_work_source_, mode()); - CFRunLoopRemoveSource(loop, owner_->idle_work_source_, mode()); - CFRunLoopRemoveSource(loop, owner_->work_source_, mode()); - CFRunLoopRemoveTimer(loop, owner_->delayed_work_timer_, mode()); - } - - // This function knows about the AppKit RunLoop modes observed to potentially - // run tasks posted to Chrome's main thread task runner. Some are internal to - // AppKit but must be observed to keep Chrome's UI responsive. Others that may - // be interesting, but are not watched: - // - com.apple.hitoolbox.windows.transitionmode - // - com.apple.hitoolbox.windows.flushmode - const CFStringRef& mode() const { - static const CFStringRef modes[] = { - // The standard Core Foundation "common modes" constant. Must always be - // first in this list to match the value of kCommonModeMask. - kCFRunLoopCommonModes, - - // Mode that only sees Chrome work sources. - kMessageLoopExclusiveRunLoopMode, - - // Process work when NSMenus are fading out. - CFSTR("com.apple.hitoolbox.windows.windowfadingmode"), - - // Process work when AppKit is highlighting an item on the main menubar. - CFSTR("NSUnhighlightMenuRunLoopMode"), - }; - static_assert(arraysize(modes) == kNumModes, "mode size mismatch"); - static_assert((1 << kNumModes) - 1 == kAllModesMask, - "kAllModesMask not large enough"); - - return modes[mode_index_]; - } - - private: - MessagePumpCFRunLoopBase* const owner_; // Weak. Owns this. - const int mode_index_; - - DISALLOW_COPY_AND_ASSIGN(ScopedModeEnabler); -}; - -// Must be called on the run loop thread. -void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { - // nesting_level_ will be incremented in EnterExitRunLoop, so set - // run_nesting_level_ accordingly. - int last_run_nesting_level = run_nesting_level_; - run_nesting_level_ = nesting_level_ + 1; - - Delegate* last_delegate = delegate_; - SetDelegate(delegate); - - DoRun(delegate); - - // Restore the previous state of the object. - SetDelegate(last_delegate); - run_nesting_level_ = last_run_nesting_level; -} - -// May be called on any thread. -void MessagePumpCFRunLoopBase::ScheduleWork() { - CFRunLoopSourceSignal(work_source_); - CFRunLoopWakeUp(run_loop_); -} - -// Must be called on the run loop thread. -void MessagePumpCFRunLoopBase::ScheduleDelayedWork( - const TimeTicks& delayed_work_time) { - TimeDelta delta = delayed_work_time - TimeTicks::Now(); - delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); - - // Flip the timer's validation bit just before setting the new fire time. Do - // this now because CFRunLoopTimerSetNextFireDate() likely checks the validity - // of a timer before proceeding to set its fire date. Making the timer valid - // now won't have any side effects (such as a premature firing of the timer) - // because we're only flipping a bit. - // - // Please see the comment in RunDelayedWorkTimer() for more info on the whys - // of invalidation. - ChromeCFRunLoopTimerSetValid(delayed_work_timer_, true); - - CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); - if (timer_slack_ == TIMER_SLACK_MAXIMUM) { - CFRunLoopTimerSetTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5); - } else { - CFRunLoopTimerSetTolerance(delayed_work_timer_, 0); - } -} - -void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) { - timer_slack_ = timer_slack; -} - -// Must be called on the run loop thread. -MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int initial_mode_mask) - : delegate_(NULL), - delayed_work_fire_time_(kCFTimeIntervalMax), - timer_slack_(base::TIMER_SLACK_NONE), - nesting_level_(0), - run_nesting_level_(0), - deepest_nesting_level_(0), - delegateless_work_(false), - delegateless_idle_work_(false) { - run_loop_ = CFRunLoopGetCurrent(); - CFRetain(run_loop_); - - // Set a repeating timer with a preposterous firing time and interval. The - // timer will effectively never fire as-is. The firing time will be adjusted - // as needed when ScheduleDelayedWork is called. - CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); - timer_context.info = this; - delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator - kCFTimeIntervalMax, // fire time - kCFTimeIntervalMax, // interval - 0, // flags - 0, // priority - RunDelayedWorkTimer, - &timer_context); - - CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); - source_context.info = this; - source_context.perform = RunWorkSource; - work_source_ = CFRunLoopSourceCreate(NULL, // allocator - 1, // priority - &source_context); - source_context.perform = RunIdleWorkSource; - idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator - 2, // priority - &source_context); - source_context.perform = RunNestingDeferredWorkSource; - nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator - 0, // priority - &source_context); - - CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); - observer_context.info = this; - pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator - kCFRunLoopBeforeWaiting, - true, // repeat - 0, // priority - PreWaitObserver, - &observer_context); - pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator - kCFRunLoopBeforeSources, - true, // repeat - 0, // priority - PreSourceObserver, - &observer_context); - enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator - kCFRunLoopEntry | - kCFRunLoopExit, - true, // repeat - 0, // priority - EnterExitObserver, - &observer_context); - SetModeMask(initial_mode_mask); -} - -// 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() { - SetModeMask(0); - CFRelease(enter_exit_observer_); - CFRelease(pre_source_observer_); - CFRelease(pre_wait_observer_); - CFRelease(nesting_deferred_work_source_); - CFRelease(idle_work_source_); - CFRelease(work_source_); - CFRelease(delayed_work_timer_); - CFRelease(run_loop_); -} - -void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) { - delegate_ = delegate; - - if (delegate) { - // If any work showed up but could not be dispatched for want of a - // delegate, set it up for dispatch again now that a delegate is - // available. - if (delegateless_work_) { - CFRunLoopSourceSignal(work_source_); - delegateless_work_ = false; - } - if (delegateless_idle_work_) { - CFRunLoopSourceSignal(idle_work_source_); - delegateless_idle_work_ = false; - } - } -} - -// Base version returns a standard NSAutoreleasePool. -AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { - return [[NSAutoreleasePool alloc] init]; -} - -void MessagePumpCFRunLoopBase::SetModeMask(int mode_mask) { - for (size_t i = 0; i < kNumModes; ++i) { - bool enable = mode_mask & (0x1 << i); - if (enable == !enabled_modes_[i]) { - enabled_modes_[i] = - enable ? std::make_unique(this, i) : nullptr; - } - } -} - -int MessagePumpCFRunLoopBase::GetModeMask() const { - int mask = 0; - for (size_t i = 0; i < kNumModes; ++i) - mask |= enabled_modes_[i] ? (0x1 << i) : 0; - return mask; -} - -#if !defined(OS_IOS) -// This function uses private API to modify a test timer's valid state and -// uses public API to confirm that the private API changed the correct bit. -// static -bool MessagePumpCFRunLoopBase::CanInvalidateCFRunLoopTimers() { - CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); - timer_context.info = nullptr; - ScopedCFTypeRef test_timer( - CFRunLoopTimerCreate(NULL, // allocator - kCFTimeIntervalMax, // fire time - kCFTimeIntervalMax, // interval - 0, // flags - 0, // priority - nullptr, &timer_context)); - // Should be valid from the start. - if (!CFRunLoopTimerIsValid(test_timer)) { - return false; - } - // Confirm that the private API can mark the timer invalid. - __ChromeCFRunLoopTimerSetValid(test_timer, false); - if (CFRunLoopTimerIsValid(test_timer)) { - return false; - } - // Confirm that the private API can mark the timer valid. - __ChromeCFRunLoopTimerSetValid(test_timer, true); - return CFRunLoopTimerIsValid(test_timer); -} -#endif // !defined(OS_IOS) - -// static -void MessagePumpCFRunLoopBase::ChromeCFRunLoopTimerSetValid( - CFRunLoopTimerRef timer, - bool valid) { -#if !defined(OS_IOS) - static bool can_invalidate_timers = CanInvalidateCFRunLoopTimers(); - if (can_invalidate_timers) { - __ChromeCFRunLoopTimerSetValid(timer, valid); - } -#endif // !defined(OS_IOS) -} - -// Called from the run loop. -// static -void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, - void* info) { - MessagePumpCFRunLoopBase* self = static_cast(info); - - // The timer won't fire again until it's reset. - self->delayed_work_fire_time_ = kCFTimeIntervalMax; - - // The message pump's timer needs to fire at changing and unpredictable - // intervals. Creating a new timer for each firing time is very expensive, so - // the message pump instead uses a repeating timer with a very large repeat - // rate. After each firing of the timer, the run loop sets the timer's next - // firing time to the distant future, essentially pausing the timer until the - // pump sets the next firing time. This is the solution recommended by Apple. - // - // It turns out, however, that scheduling timers is also quite expensive, and - // that every one of the message pump's timer firings incurs two - // reschedulings. The first rescheduling occurs in ScheduleDelayedWork(), - // which sets the desired next firing time. The second comes after exiting - // this method (the timer's callback method), when the run loop sets the - // timer's next firing time to far in the future. - // - // The code in __CFRunLoopDoTimer() inside CFRunLoop.c calls the timer's - // callback, confirms that the timer is valid, and then sets its future - // firing time based on its repeat frequency. Flipping the valid bit here - // causes the __CFRunLoopDoTimer() to skip setting the future firing time. - // Note that there's public API to invalidate a timer but it goes beyond - // flipping the valid bit, making the timer unusable in the future. - // - // ScheduleDelayedWork() flips the valid bit back just before setting the - // timer's new firing time. - ChromeCFRunLoopTimerSetValid(self->delayed_work_timer_, false); - - // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. - // In order to establish the proper priority in which work and delayed work - // are processed one for one, the timer used to schedule delayed work must - // signal a CFRunLoopSource used to dispatch both work and delayed work. - CFRunLoopSourceSignal(self->work_source_); -} - -// Called from the run loop. -// static -void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { - MessagePumpCFRunLoopBase* self = static_cast(info); - base::mac::CallWithEHFrame(^{ - self->RunWork(); - }); -} - -// Called by MessagePumpCFRunLoopBase::RunWorkSource. -bool MessagePumpCFRunLoopBase::RunWork() { - if (!delegate_) { - // This point can be reached with a NULL delegate_ if Run is not on the - // stack but foreign code is spinning the CFRunLoop. Arrange to come back - // here when a delegate is available. - delegateless_work_ = true; - return false; - } - - // The NSApplication-based run loop only drains the autorelease pool at each - // UI event (NSEvent). The autorelease pool is not drained for each - // CFRunLoopSource target that's run. Use a local pool for any autoreleased - // objects if the app is not currently handling a UI event to ensure they're - // released promptly even in the absence of UI events. - MessagePumpScopedAutoreleasePool autorelease_pool(this); - - // Call DoWork and DoDelayedWork once, and if something was done, arrange to - // come back here again as long as the loop is still running. - bool did_work = delegate_->DoWork(); - bool resignal_work_source = did_work; - - TimeTicks next_time; - delegate_->DoDelayedWork(&next_time); - if (!did_work) { - // Determine whether there's more delayed work, and if so, if it needs to - // be done at some point in the future or if it's already time to do it. - // Only do these checks if did_work is false. If did_work is true, this - // function, and therefore any additional delayed work, will get another - // chance to run before the loop goes to sleep. - bool more_delayed_work = !next_time.is_null(); - if (more_delayed_work) { - TimeDelta delay = next_time - TimeTicks::Now(); - if (delay > TimeDelta()) { - // There's more delayed work to be done in the future. - ScheduleDelayedWork(next_time); - } else { - // There's more delayed work to be done, and its time is in the past. - // Arrange to come back here directly as long as the loop is still - // running. - resignal_work_source = true; - } - } - } - - if (resignal_work_source) { - CFRunLoopSourceSignal(work_source_); - } - - return resignal_work_source; -} - -// Called from the run loop. -// static -void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { - MessagePumpCFRunLoopBase* self = static_cast(info); - base::mac::CallWithEHFrame(^{ - self->RunIdleWork(); - }); -} - -// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. -bool MessagePumpCFRunLoopBase::RunIdleWork() { - if (!delegate_) { - // This point can be reached with a NULL delegate_ if Run is not on the - // stack but foreign code is spinning the CFRunLoop. Arrange to come back - // here when a delegate is available. - delegateless_idle_work_ = true; - return false; - } - - // The NSApplication-based run loop only drains the autorelease pool at each - // UI event (NSEvent). The autorelease pool is not drained for each - // CFRunLoopSource target that's run. Use a local pool for any autoreleased - // objects if the app is not currently handling a UI event to ensure they're - // released promptly even in the absence of UI events. - MessagePumpScopedAutoreleasePool autorelease_pool(this); - - // Call DoIdleWork once, and if something was done, arrange to come back here - // again as long as the loop is still running. - bool did_work = delegate_->DoIdleWork(); - if (did_work) { - CFRunLoopSourceSignal(idle_work_source_); - } - - return did_work; -} - -// Called from the run loop. -// static -void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { - MessagePumpCFRunLoopBase* self = static_cast(info); - base::mac::CallWithEHFrame(^{ - self->RunNestingDeferredWork(); - }); -} - -// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. -bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { - if (!delegate_) { - // This point can be reached with a NULL delegate_ if Run is not on the - // stack but foreign code is spinning the CFRunLoop. There's no sense in - // attempting to do any work or signalling the work sources because - // without a delegate, work is not possible. - return false; - } - - // Immediately try work in priority order. - if (!RunWork()) { - if (!RunIdleWork()) { - return false; - } - } else { - // Work was done. Arrange for the loop to try non-nestable idle work on - // a subsequent pass. - CFRunLoopSourceSignal(idle_work_source_); - } - - return true; -} - -// Called before the run loop goes to sleep or exits, or processes sources. -void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { - // deepest_nesting_level_ is set as run loops are entered. If the deepest - // level encountered is deeper than the current level, a nested loop - // (relative to the current level) ran since the last time nesting-deferred - // work was scheduled. When that situation is encountered, schedule - // nesting-deferred work in case any work was deferred because nested work - // was disallowed. - if (deepest_nesting_level_ > nesting_level_) { - deepest_nesting_level_ = nesting_level_; - CFRunLoopSourceSignal(nesting_deferred_work_source_); - } -} - -// Called from the run loop. -// static -void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer, - CFRunLoopActivity activity, - void* info) { - MessagePumpCFRunLoopBase* self = static_cast(info); - base::mac::CallWithEHFrame(^{ - // Attempt to do some idle work before going to sleep. - self->RunIdleWork(); - - // The run loop is about to go to sleep. If any of the work done since it - // started or woke up resulted in a nested run loop running, - // nesting-deferred work may have accumulated. Schedule it for processing - // if appropriate. - self->MaybeScheduleNestingDeferredWork(); - }); -} - -// Called from the run loop. -// static -void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer, - CFRunLoopActivity activity, - void* info) { - MessagePumpCFRunLoopBase* self = static_cast(info); - - // The run loop has reached the top of the loop and is about to begin - // processing sources. If the last iteration of the loop at this nesting - // level did not sleep or exit, nesting-deferred work may have accumulated - // if a nested loop ran. Schedule nesting-deferred work for processing if - // appropriate. - base::mac::CallWithEHFrame(^{ - self->MaybeScheduleNestingDeferredWork(); - }); -} - -// Called from the run loop. -// static -void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, - CFRunLoopActivity activity, - void* info) { - MessagePumpCFRunLoopBase* self = static_cast(info); - - switch (activity) { - case kCFRunLoopEntry: - ++self->nesting_level_; - if (self->nesting_level_ > self->deepest_nesting_level_) { - self->deepest_nesting_level_ = self->nesting_level_; - } - break; - - case kCFRunLoopExit: - // Not all run loops go to sleep. If a run loop is stopped before it - // goes to sleep due to a CFRunLoopStop call, or if the timeout passed - // to CFRunLoopRunInMode expires, the run loop may proceed directly from - // handling sources to exiting without any sleep. This most commonly - // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it - // to make a single pass through the loop and exit without sleep. Some - // native loops use CFRunLoop in this way. Because PreWaitObserver will - // not be called in these case, MaybeScheduleNestingDeferredWork needs - // to be called here, as the run loop exits. - // - // MaybeScheduleNestingDeferredWork consults self->nesting_level_ - // to determine whether to schedule nesting-deferred work. It expects - // the nesting level to be set to the depth of the loop that is going - // to sleep or exiting. It must be called before decrementing the - // value so that the value still corresponds to the level of the exiting - // loop. - base::mac::CallWithEHFrame(^{ - self->MaybeScheduleNestingDeferredWork(); - }); - --self->nesting_level_; - break; - - default: - break; - } - - base::mac::CallWithEHFrame(^{ - self->EnterExitRunLoop(activity); - }); -} - -// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default -// implementation is a no-op. -void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { -} - -MessagePumpCFRunLoop::MessagePumpCFRunLoop() - : MessagePumpCFRunLoopBase(kCommonModeMask), quit_pending_(false) {} - -MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {} - -// Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were -// running lower on the run loop thread's stack when this object was created, -// the same number of CFRunLoopRun loops must be running for the outermost call -// to Run. Run/DoRun are reentrant after that point. -void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { - // This is completely identical to calling CFRunLoopRun(), except autorelease - // pool management is introduced. - int result; - do { - MessagePumpScopedAutoreleasePool autorelease_pool(this); - result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, - kCFTimeIntervalMax, - false); - } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); -} - -// Must be called on the run loop thread. -void MessagePumpCFRunLoop::Quit() { - // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. - if (nesting_level() == run_nesting_level()) { - // This object is running the innermost loop, just stop it. - CFRunLoopStop(run_loop()); - } else { - // There's another loop running inside the loop managed by this object. - // In other words, someone else called CFRunLoopRunInMode on the same - // thread, deeper on the stack than the deepest Run call. Don't preempt - // other run loops, just mark this object to quit the innermost Run as - // soon as the other inner loops not managed by Run are done. - quit_pending_ = true; - } -} - -// Called by MessagePumpCFRunLoopBase::EnterExitObserver. -void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { - if (activity == kCFRunLoopExit && - nesting_level() == run_nesting_level() && - quit_pending_) { - // Quit was called while loops other than those managed by this object - // were running further inside a run loop managed by this object. Now - // that all unmanaged inner run loops are gone, stop the loop running - // just inside Run. - CFRunLoopStop(run_loop()); - quit_pending_ = false; - } -} - -MessagePumpNSRunLoop::MessagePumpNSRunLoop() - : MessagePumpCFRunLoopBase(kCommonModeMask), keep_running_(true) { - CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); - source_context.perform = NoOp; - quit_source_ = CFRunLoopSourceCreate(NULL, // allocator - 0, // priority - &source_context); - CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes); -} - -MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { - CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes); - CFRelease(quit_source_); -} - -void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { - AutoReset auto_reset_keep_running(&keep_running_, true); - - while (keep_running_) { - // NSRunLoop manages autorelease pools itself. - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode - beforeDate:[NSDate distantFuture]]; - } -} - -void MessagePumpNSRunLoop::Quit() { - keep_running_ = false; - CFRunLoopSourceSignal(quit_source_); - CFRunLoopWakeUp(run_loop()); -} - -#if defined(OS_IOS) -MessagePumpUIApplication::MessagePumpUIApplication() - : MessagePumpCFRunLoopBase(kCommonModeMask), run_loop_(NULL) {} - -MessagePumpUIApplication::~MessagePumpUIApplication() {} - -void MessagePumpUIApplication::DoRun(Delegate* delegate) { - NOTREACHED(); -} - -void MessagePumpUIApplication::Quit() { - NOTREACHED(); -} - -void MessagePumpUIApplication::Attach(Delegate* delegate) { - DCHECK(!run_loop_); - run_loop_ = new RunLoop(); - CHECK(run_loop_->BeforeRun()); - SetDelegate(delegate); -} - -#else - -ScopedPumpMessagesInPrivateModes::ScopedPumpMessagesInPrivateModes() { - DCHECK(g_app_pump); - DCHECK_EQ(kNSApplicationModalSafeModeMask, g_app_pump->GetModeMask()); - // Pumping events in private runloop modes is known to interact badly with - // app modal windows like NSAlert. - if (![NSApp modalWindow]) - g_app_pump->SetModeMask(kAllModesMask); -} - -ScopedPumpMessagesInPrivateModes::~ScopedPumpMessagesInPrivateModes() { - DCHECK(g_app_pump); - g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask); -} - -int ScopedPumpMessagesInPrivateModes::GetModeMaskForTest() { - return g_app_pump ? g_app_pump->GetModeMask() : -1; -} - -MessagePumpNSApplication::MessagePumpNSApplication() - : MessagePumpCFRunLoopBase(kNSApplicationModalSafeModeMask), - keep_running_(true), - running_own_loop_(false) { - DCHECK_EQ(nullptr, g_app_pump); - g_app_pump = this; -} - -MessagePumpNSApplication::~MessagePumpNSApplication() { - DCHECK_EQ(this, g_app_pump); - g_app_pump = nullptr; -} - -void MessagePumpNSApplication::DoRun(Delegate* delegate) { - AutoReset auto_reset_keep_running(&keep_running_, true); - bool last_running_own_loop_ = running_own_loop_; - - // NSApp must be initialized by calling: - // [{some class which implements CrAppProtocol} sharedApplication] - // Most likely candidates are CrApplication or BrowserCrApplication. - // These can be initialized from C++ code by calling - // RegisterCrApp() or RegisterBrowserCrApp(). - CHECK(NSApp); - - if (![NSApp isRunning]) { - running_own_loop_ = false; - // NSApplication manages autorelease pools itself when run this way. - [NSApp run]; - } else { - running_own_loop_ = true; - NSDate* distant_future = [NSDate distantFuture]; - while (keep_running_) { - MessagePumpScopedAutoreleasePool autorelease_pool(this); - NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask - untilDate:distant_future - inMode:NSDefaultRunLoopMode - dequeue:YES]; - if (event) { - [NSApp sendEvent:event]; - } - } - } - - running_own_loop_ = last_running_own_loop_; -} - -void MessagePumpNSApplication::Quit() { - if (!running_own_loop_) { - [[NSApplication sharedApplication] stop:nil]; - } else { - keep_running_ = false; - } - - // Send a fake event to wake the loop up. - [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined - location:NSZeroPoint - modifierFlags:0 - timestamp:0 - windowNumber:0 - context:NULL - subtype:0 - data1:0 - data2:0] - atStart:NO]; -} - -MessagePumpCrApplication::MessagePumpCrApplication() { -} - -MessagePumpCrApplication::~MessagePumpCrApplication() { -} - -// Prevents an autorelease pool from being created if the app is in the midst of -// handling a UI event because various parts of AppKit depend on objects that -// are created while handling a UI event to be autoreleased in the event loop. -// An example of this is NSWindowController. When a window with a window -// controller is closed it goes through a stack like this: -// (Several stack frames elided for clarity) -// -// #0 [NSWindowController autorelease] -// #1 DoAClose -// #2 MessagePumpCFRunLoopBase::DoWork() -// #3 [NSRunLoop run] -// #4 [NSButton performClick:] -// #5 [NSWindow sendEvent:] -// #6 [NSApp sendEvent:] -// #7 [NSApp run] -// -// -performClick: spins a nested run loop. If the pool created in DoWork was a -// standard NSAutoreleasePool, it would release the objects that were -// autoreleased into it once DoWork released it. This would cause the window -// controller, which autoreleased itself in frame #0, to release itself, and -// possibly free itself. Unfortunately this window controller controls the -// window in frame #5. When the stack is unwound to frame #5, the window would -// no longer exists and crashes may occur. Apple gets around this by never -// releasing the pool it creates in frame #4, and letting frame #7 clean it up -// when it cleans up the pool that wraps frame #7. When an autorelease pool is -// released it releases all other pools that were created after it on the -// autorelease pool stack. -// -// 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. -AutoreleasePoolType* MessagePumpCrApplication::CreateAutoreleasePool() { - if (MessagePumpMac::IsHandlingSendEvent()) - return nil; - return MessagePumpNSApplication::CreateAutoreleasePool(); -} - -// static -bool MessagePumpMac::UsingCrApp() { - DCHECK([NSThread isMainThread]); - - // If NSApp is still not initialized, then the subclass used cannot - // be determined. - DCHECK(NSApp); - - // The pump was created using MessagePumpNSApplication. - if (g_not_using_cr_app) - return false; - - return [NSApp conformsToProtocol:@protocol(CrAppProtocol)]; -} - -// static -bool MessagePumpMac::IsHandlingSendEvent() { - DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]); - NSObject* app = static_cast*>(NSApp); - return [app isHandlingSendEvent]; -} -#endif // !defined(OS_IOS) - -// static -std::unique_ptr MessagePumpMac::Create() { - if ([NSThread isMainThread]) { -#if defined(OS_IOS) - return std::make_unique(); -#else - if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)]) - return std::make_unique(); - - // The main-thread MessagePump implementations REQUIRE an NSApp. - // Executables which have specific requirements for their - // NSApplication subclass should initialize appropriately before - // creating an event loop. - [NSApplication sharedApplication]; - g_not_using_cr_app = true; - return std::make_unique(); -#endif - } - - return std::make_unique(); -} - -} // namespace base diff --git a/message_loop/message_pump_mac_unittest.mm b/message_loop/message_pump_mac_unittest.mm deleted file mode 100644 index 6b63aa14f..000000000 --- a/message_loop/message_pump_mac_unittest.mm +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright 2017 The Chromium 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_mac.h" - -#include "base/mac/scoped_cftyperef.h" -#import "base/mac/scoped_nsobject.h" -#include "base/macros.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_current.h" -#include "base/threading/thread_task_runner_handle.h" -#include "testing/gtest/include/gtest/gtest.h" - -@interface TestModalAlertCloser : NSObject -- (void)runTestThenCloseAlert:(NSAlert*)alert; -@end - -namespace { - -// Internal constants from message_pump_mac.mm. -constexpr int kAllModesMask = 0xf; -constexpr int kNSApplicationModalSafeModeMask = 0x3; - -} // namespace - -namespace base { - -class TestMessagePumpCFRunLoopBase { - public: - bool TestCanInvalidateTimers() { - return MessagePumpCFRunLoopBase::CanInvalidateCFRunLoopTimers(); - } - static void SetTimerValid(CFRunLoopTimerRef timer, bool valid) { - MessagePumpCFRunLoopBase::ChromeCFRunLoopTimerSetValid(timer, valid); - } - - static void PerformTimerCallback(CFRunLoopTimerRef timer, void* info) { - TestMessagePumpCFRunLoopBase* self = - static_cast(info); - self->timer_callback_called_ = true; - - if (self->invalidate_timer_in_callback_) { - SetTimerValid(timer, false); - } - } - - bool invalidate_timer_in_callback_; - - bool timer_callback_called_; -}; - -TEST(MessagePumpMacTest, TestCanInvalidateTimers) { - TestMessagePumpCFRunLoopBase message_pump_test; - - // Catch whether or not the use of private API ever starts failing. - EXPECT_TRUE(message_pump_test.TestCanInvalidateTimers()); -} - -TEST(MessagePumpMacTest, TestInvalidatedTimerReuse) { - TestMessagePumpCFRunLoopBase message_pump_test; - - CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); - timer_context.info = &message_pump_test; - const CFTimeInterval kCFTimeIntervalMax = - std::numeric_limits::max(); - ScopedCFTypeRef test_timer(CFRunLoopTimerCreate( - NULL, // allocator - kCFTimeIntervalMax, // fire time - kCFTimeIntervalMax, // interval - 0, // flags - 0, // priority - TestMessagePumpCFRunLoopBase::PerformTimerCallback, &timer_context)); - CFRunLoopAddTimer(CFRunLoopGetCurrent(), test_timer, - kMessageLoopExclusiveRunLoopMode); - - // Sanity check. - EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer)); - - // Confirm that the timer fires as expected, and that it's not a one-time-use - // timer (those timers are invalidated after they fire). - CFAbsoluteTime next_fire_time = CFAbsoluteTimeGetCurrent() + 0.01; - CFRunLoopTimerSetNextFireDate(test_timer, next_fire_time); - message_pump_test.timer_callback_called_ = false; - message_pump_test.invalidate_timer_in_callback_ = false; - CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0.02, true); - EXPECT_TRUE(message_pump_test.timer_callback_called_); - EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer)); - - // As a repeating timer, the timer should have a new fire date set in the - // future. - EXPECT_GT(CFRunLoopTimerGetNextFireDate(test_timer), next_fire_time); - - // Try firing the timer, and invalidating it within its callback. - next_fire_time = CFAbsoluteTimeGetCurrent() + 0.01; - CFRunLoopTimerSetNextFireDate(test_timer, next_fire_time); - message_pump_test.timer_callback_called_ = false; - message_pump_test.invalidate_timer_in_callback_ = true; - CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0.02, true); - EXPECT_TRUE(message_pump_test.timer_callback_called_); - EXPECT_FALSE(CFRunLoopTimerIsValid(test_timer)); - - // The CFRunLoop believes the timer is invalid, so it should not have a - // fire date. - EXPECT_EQ(0, CFRunLoopTimerGetNextFireDate(test_timer)); - - // Now mark the timer as valid and confirm that it still fires correctly. - TestMessagePumpCFRunLoopBase::SetTimerValid(test_timer, true); - EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer)); - next_fire_time = CFAbsoluteTimeGetCurrent() + 0.01; - CFRunLoopTimerSetNextFireDate(test_timer, next_fire_time); - message_pump_test.timer_callback_called_ = false; - message_pump_test.invalidate_timer_in_callback_ = false; - CFRunLoopRunInMode(kMessageLoopExclusiveRunLoopMode, 0.02, true); - EXPECT_TRUE(message_pump_test.timer_callback_called_); - EXPECT_TRUE(CFRunLoopTimerIsValid(test_timer)); - - // Confirm that the run loop again gave it a new fire date in the future. - EXPECT_GT(CFRunLoopTimerGetNextFireDate(test_timer), next_fire_time); - - CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), test_timer, - kMessageLoopExclusiveRunLoopMode); -} - -namespace { - -// PostedTasks are only executed while the message pump has a delegate. That is, -// when a base::RunLoop is running, so in order to test whether posted tasks -// are run by CFRunLoopRunInMode and *not* by the regular RunLoop, we need to -// be inside a task that is also calling CFRunLoopRunInMode. This task runs the -// given |mode| after posting a task to increment a counter, then checks whether -// the counter incremented after emptying that run loop mode. -void IncrementInModeAndExpect(CFRunLoopMode mode, int result) { - // Since this task is "ours" rather than a system task, allow nesting. - MessageLoopCurrent::ScopedNestableTaskAllower allow; - int counter = 0; - auto increment = BindRepeating([](int* i) { ++*i; }, &counter); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, increment); - while (CFRunLoopRunInMode(mode, 0, true) == kCFRunLoopRunHandledSource) - ; - ASSERT_EQ(result, counter); -} - -} // namespace - -// Tests the correct behavior of ScopedPumpMessagesInPrivateModes. -TEST(MessagePumpMacTest, ScopedPumpMessagesInPrivateModes) { - MessageLoopForUI message_loop; - - CFRunLoopMode kRegular = kCFRunLoopDefaultMode; - CFRunLoopMode kPrivate = CFSTR("NSUnhighlightMenuRunLoopMode"); - - // Work is seen when running in the default mode. - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&IncrementInModeAndExpect, kRegular, 1)); - EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle()); - - // But not seen when running in a private mode. - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&IncrementInModeAndExpect, kPrivate, 0)); - EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle()); - - { - ScopedPumpMessagesInPrivateModes allow_private; - // Now the work should be seen. - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&IncrementInModeAndExpect, kPrivate, 1)); - EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle()); - - // The regular mode should also work the same. - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&IncrementInModeAndExpect, kRegular, 1)); - EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle()); - } - - // And now the scoper is out of scope, private modes should no longer see it. - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&IncrementInModeAndExpect, kPrivate, 0)); - EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle()); - - // Only regular modes see it. - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce(&IncrementInModeAndExpect, kRegular, 1)); - EXPECT_NO_FATAL_FAILURE(RunLoop().RunUntilIdle()); -} - -// Tests that private message loop modes are not pumped while a modal dialog is -// present. -TEST(MessagePumpMacTest, ScopedPumpMessagesAttemptWithModalDialog) { - MessageLoopForUI message_loop; - - { - base::ScopedPumpMessagesInPrivateModes allow_private; - // No modal window, so all modes should be pumped. - EXPECT_EQ(kAllModesMask, allow_private.GetModeMaskForTest()); - } - - base::scoped_nsobject alert([[NSAlert alloc] init]); - [alert addButtonWithTitle:@"OK"]; - base::scoped_nsobject closer( - [[TestModalAlertCloser alloc] init]); - [closer performSelector:@selector(runTestThenCloseAlert:) - withObject:alert - afterDelay:0 - inModes:@[ NSModalPanelRunLoopMode ]]; - NSInteger result = [alert runModal]; - EXPECT_EQ(NSAlertFirstButtonReturn, result); -} - -} // namespace base - -@implementation TestModalAlertCloser - -- (void)runTestThenCloseAlert:(NSAlert*)alert { - EXPECT_TRUE([NSApp modalWindow]); - { - base::ScopedPumpMessagesInPrivateModes allow_private; - // With a modal window, only safe modes should be pumped. - EXPECT_EQ(kNSApplicationModalSafeModeMask, - allow_private.GetModeMaskForTest()); - } - [[alert buttons][0] performClick:nil]; -} - -@end diff --git a/message_loop/message_pump_perftest.cc b/message_loop/message_pump_perftest.cc deleted file mode 100644 index 71ed4912d..000000000 --- a/message_loop/message_pump_perftest.cc +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include -#include - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/format_macros.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" -#include "base/single_thread_task_runner.h" -#include "base/strings/stringprintf.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_ANDROID) -#include "base/android/java_handler_thread.h" -#endif - -namespace base { - -class ScheduleWorkTest : public testing::Test { - public: - ScheduleWorkTest() : counter_(0) {} - - void SetUp() override { - if (base::ThreadTicks::IsSupported()) - base::ThreadTicks::WaitUntilInitialized(); - } - - void Increment(uint64_t amount) { counter_ += amount; } - - void Schedule(int index) { - base::TimeTicks start = base::TimeTicks::Now(); - base::ThreadTicks thread_start; - if (ThreadTicks::IsSupported()) - thread_start = base::ThreadTicks::Now(); - base::TimeDelta minimum = base::TimeDelta::Max(); - base::TimeDelta maximum = base::TimeDelta(); - base::TimeTicks now, lastnow = start; - uint64_t schedule_calls = 0u; - do { - for (size_t i = 0; i < kBatchSize; ++i) { - target_message_loop()->ScheduleWork(); - schedule_calls++; - } - now = base::TimeTicks::Now(); - base::TimeDelta laptime = now - lastnow; - lastnow = now; - minimum = std::min(minimum, laptime); - maximum = std::max(maximum, laptime); - } while (now - start < base::TimeDelta::FromSeconds(kTargetTimeSec)); - - scheduling_times_[index] = now - start; - if (ThreadTicks::IsSupported()) - scheduling_thread_times_[index] = - base::ThreadTicks::Now() - thread_start; - min_batch_times_[index] = minimum; - max_batch_times_[index] = maximum; - target_message_loop()->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ScheduleWorkTest::Increment, - base::Unretained(this), schedule_calls)); - } - - void ScheduleWork(MessageLoop::Type target_type, int num_scheduling_threads) { -#if defined(OS_ANDROID) - if (target_type == MessageLoop::TYPE_JAVA) { - java_thread_.reset(new android::JavaHandlerThread("target")); - java_thread_->Start(); - } else -#endif - { - target_.reset(new Thread("target")); - target_->StartWithOptions(Thread::Options(target_type, 0u)); - - // Without this, it's possible for the scheduling threads to start and run - // before the target thread. In this case, the scheduling threads will - // call target_message_loop()->ScheduleWork(), which dereferences the - // loop's message pump, which is only created after the target thread has - // finished starting. - target_->WaitUntilThreadStarted(); - } - - std::vector> scheduling_threads; - scheduling_times_.reset(new base::TimeDelta[num_scheduling_threads]); - scheduling_thread_times_.reset(new base::TimeDelta[num_scheduling_threads]); - min_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]); - max_batch_times_.reset(new base::TimeDelta[num_scheduling_threads]); - - for (int i = 0; i < num_scheduling_threads; ++i) { - scheduling_threads.push_back(std::make_unique("posting thread")); - scheduling_threads[i]->Start(); - } - - for (int i = 0; i < num_scheduling_threads; ++i) { - scheduling_threads[i]->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ScheduleWorkTest::Schedule, - base::Unretained(this), i)); - } - - for (int i = 0; i < num_scheduling_threads; ++i) { - scheduling_threads[i]->Stop(); - } -#if defined(OS_ANDROID) - if (target_type == MessageLoop::TYPE_JAVA) { - java_thread_->Stop(); - java_thread_.reset(); - } else -#endif - { - target_->Stop(); - target_.reset(); - } - base::TimeDelta total_time; - base::TimeDelta total_thread_time; - base::TimeDelta min_batch_time = base::TimeDelta::Max(); - base::TimeDelta max_batch_time = base::TimeDelta(); - for (int i = 0; i < num_scheduling_threads; ++i) { - total_time += scheduling_times_[i]; - total_thread_time += scheduling_thread_times_[i]; - min_batch_time = std::min(min_batch_time, min_batch_times_[i]); - max_batch_time = std::max(max_batch_time, max_batch_times_[i]); - } - std::string trace = StringPrintf( - "%d_threads_scheduling_to_%s_pump", - num_scheduling_threads, - target_type == MessageLoop::TYPE_IO - ? "io" - : (target_type == MessageLoop::TYPE_UI ? "ui" : "default")); - perf_test::PrintResult( - "task", - "", - trace, - total_time.InMicroseconds() / static_cast(counter_), - "us/task", - true); - perf_test::PrintResult( - "task", - "_min_batch_time", - trace, - min_batch_time.InMicroseconds() / static_cast(kBatchSize), - "us/task", - false); - perf_test::PrintResult( - "task", - "_max_batch_time", - trace, - max_batch_time.InMicroseconds() / static_cast(kBatchSize), - "us/task", - false); - if (ThreadTicks::IsSupported()) { - perf_test::PrintResult( - "task", - "_thread_time", - trace, - total_thread_time.InMicroseconds() / static_cast(counter_), - "us/task", - true); - } - } - - MessageLoop* target_message_loop() { -#if defined(OS_ANDROID) - if (java_thread_) - return java_thread_->message_loop(); -#endif - return target_->message_loop(); - } - - private: - std::unique_ptr target_; -#if defined(OS_ANDROID) - std::unique_ptr java_thread_; -#endif - std::unique_ptr scheduling_times_; - std::unique_ptr scheduling_thread_times_; - std::unique_ptr min_batch_times_; - std::unique_ptr max_batch_times_; - uint64_t counter_; - - static const size_t kTargetTimeSec = 5; - static const size_t kBatchSize = 1000; -}; - -TEST_F(ScheduleWorkTest, ThreadTimeToIOFromOneThread) { - ScheduleWork(MessageLoop::TYPE_IO, 1); -} - -TEST_F(ScheduleWorkTest, ThreadTimeToIOFromTwoThreads) { - ScheduleWork(MessageLoop::TYPE_IO, 2); -} - -TEST_F(ScheduleWorkTest, ThreadTimeToIOFromFourThreads) { - ScheduleWork(MessageLoop::TYPE_IO, 4); -} - -TEST_F(ScheduleWorkTest, ThreadTimeToUIFromOneThread) { - ScheduleWork(MessageLoop::TYPE_UI, 1); -} - -TEST_F(ScheduleWorkTest, ThreadTimeToUIFromTwoThreads) { - ScheduleWork(MessageLoop::TYPE_UI, 2); -} - -TEST_F(ScheduleWorkTest, ThreadTimeToUIFromFourThreads) { - ScheduleWork(MessageLoop::TYPE_UI, 4); -} - -TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromOneThread) { - ScheduleWork(MessageLoop::TYPE_DEFAULT, 1); -} - -TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromTwoThreads) { - ScheduleWork(MessageLoop::TYPE_DEFAULT, 2); -} - -TEST_F(ScheduleWorkTest, ThreadTimeToDefaultFromFourThreads) { - ScheduleWork(MessageLoop::TYPE_DEFAULT, 4); -} - -#if defined(OS_ANDROID) -TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromOneThread) { - ScheduleWork(MessageLoop::TYPE_JAVA, 1); -} - -TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromTwoThreads) { - ScheduleWork(MessageLoop::TYPE_JAVA, 2); -} - -TEST_F(ScheduleWorkTest, ThreadTimeToJavaFromFourThreads) { - ScheduleWork(MessageLoop::TYPE_JAVA, 4); -} -#endif - -} // namespace base diff --git a/message_loop/message_pump_win.cc b/message_loop/message_pump_win.cc deleted file mode 100644 index c95f85abf..000000000 --- a/message_loop/message_pump_win.cc +++ /dev/null @@ -1,589 +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_win.h" - -#include -#include - -#include - -#include "base/debug/alias.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/histogram_macros.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/trace_event.h" -#include "base/win/current_module.h" -#include "base/win/wrapped_window_proc.h" - -namespace base { - -namespace { - -enum MessageLoopProblems { - MESSAGE_POST_ERROR, - COMPLETION_POST_ERROR, - SET_TIMER_ERROR, - RECEIVED_WM_QUIT_ERROR, - MESSAGE_LOOP_PROBLEM_MAX, -}; - -} // namespace - -// Message sent to get an additional time slice for pumping (processing) another -// task (a series of such messages creates a continuous task pump). -static const int kMsgHaveWork = WM_USER + 1; - -//----------------------------------------------------------------------------- -// MessagePumpWin public: - -MessagePumpWin::MessagePumpWin() = default; - -void MessagePumpWin::Run(Delegate* delegate) { - RunState s; - s.delegate = delegate; - s.should_quit = false; - s.run_depth = state_ ? state_->run_depth + 1 : 1; - - RunState* previous_state = state_; - state_ = &s; - - DoRunLoop(); - - state_ = previous_state; -} - -void MessagePumpWin::Quit() { - DCHECK(state_); - state_->should_quit = true; -} - -//----------------------------------------------------------------------------- -// MessagePumpWin protected: - -int MessagePumpWin::GetCurrentDelay() const { - if (delayed_work_time_.is_null()) - return -1; - - // Be careful here. TimeDelta has a precision of microseconds, but we want a - // value in milliseconds. If there are 5.5ms left, should the delay be 5 or - // 6? It should be 6 to avoid executing delayed work too early. - double timeout = - ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF()); - - // Range check the |timeout| while converting to an integer. If the |timeout| - // is negative, then we need to run delayed work soon. If the |timeout| is - // "overflowingly" large, that means a delayed task was posted with a - // super-long delay. - return timeout < 0 ? 0 : - (timeout > std::numeric_limits::max() ? - std::numeric_limits::max() : static_cast(timeout)); -} - -//----------------------------------------------------------------------------- -// MessagePumpForUI public: - -MessagePumpForUI::MessagePumpForUI() { - bool succeeded = message_window_.Create( - BindRepeating(&MessagePumpForUI::MessageCallback, Unretained(this))); - DCHECK(succeeded); -} - -MessagePumpForUI::~MessagePumpForUI() = default; - -void MessagePumpForUI::ScheduleWork() { - if (InterlockedExchange(&work_state_, HAVE_WORK) != READY) - return; // Someone else continued the pumping. - - // Make sure the MessagePump does some work for us. - BOOL ret = PostMessage(message_window_.hwnd(), kMsgHaveWork, 0, 0); - if (ret) - return; // There was room in the Window Message queue. - - // We have failed to insert a have-work message, so there is a chance that we - // will starve tasks/timers while sitting in a nested run loop. Nested - // loops only look at Windows Message queues, and don't look at *our* task - // queues, etc., so we might not get a time slice in such. :-( - // We could abort here, but the fear is that this failure mode is plausibly - // common (queue is full, of about 2000 messages), so we'll do a near-graceful - // recovery. Nested loops are pretty transient (we think), so this will - // probably be recoverable. - - // Clarify that we didn't really insert. - InterlockedExchange(&work_state_, READY); - UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR, - MESSAGE_LOOP_PROBLEM_MAX); -} - -void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { - delayed_work_time_ = delayed_work_time; - RescheduleTimer(); -} - -void MessagePumpForUI::EnableWmQuit() { - enable_wm_quit_ = true; -} - -//----------------------------------------------------------------------------- -// MessagePumpForUI private: - -bool MessagePumpForUI::MessageCallback( - UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) { - switch (message) { - case kMsgHaveWork: - HandleWorkMessage(); - break; - case WM_TIMER: - HandleTimerMessage(); - break; - } - return false; -} - -void MessagePumpForUI::DoRunLoop() { - // IF this was just a simple PeekMessage() loop (servicing all possible work - // queues), then Windows would try to achieve the following order according - // to MSDN documentation about PeekMessage with no filter): - // * Sent messages - // * Posted messages - // * Sent messages (again) - // * WM_PAINT messages - // * WM_TIMER messages - // - // Summary: none of the above classes is starved, and sent messages has twice - // the chance of being processed (i.e., reduced service time). - - for (;;) { - // If we do any work, we may create more messages etc., and more work may - // possibly be waiting in another task group. When we (for example) - // ProcessNextWindowsMessage(), there is a good chance there are still more - // messages waiting. On the other hand, when any of these methods return - // having done no work, then it is pretty unlikely that calling them again - // quickly will find any work to do. Finally, if they all say they had no - // work, then it is a good time to consider sleeping (waiting) for more - // work. - - bool more_work_is_plausible = ProcessNextWindowsMessage(); - 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 we did not process any delayed work, then we can assume that our - // existing WM_TIMER if any will fire when delayed work should run. We - // don't want to disturb that timer if it is already in flight. However, - // if we did do all remaining delayed work, then lets kill the WM_TIMER. - if (more_work_is_plausible && delayed_work_time_.is_null()) - KillTimer(message_window_.hwnd(), reinterpret_cast(this)); - if (state_->should_quit) - break; - - if (more_work_is_plausible) - continue; - - more_work_is_plausible = state_->delegate->DoIdleWork(); - if (state_->should_quit) - break; - - if (more_work_is_plausible) - continue; - - WaitForWork(); // Wait (sleep) until we have work to do again. - } -} - -void MessagePumpForUI::WaitForWork() { - // Wait until a message is available, up to the time needed by the timer - // manager to fire the next set of timers. - int delay; - DWORD wait_flags = MWMO_INPUTAVAILABLE; - - while ((delay = GetCurrentDelay()) != 0) { - if (delay < 0) // Negative value means no timers waiting. - delay = INFINITE; - - // Tell the optimizer to retain these values to simplify analyzing hangs. - base::debug::Alias(&delay); - base::debug::Alias(&wait_flags); - DWORD result = MsgWaitForMultipleObjectsEx(0, nullptr, delay, QS_ALLINPUT, - wait_flags); - - if (WAIT_OBJECT_0 == result) { - // A WM_* message is available. - // If a parent child relationship exists between windows across threads - // then their thread inputs are implicitly attached. - // This causes the MsgWaitForMultipleObjectsEx API to return indicating - // that messages are ready for processing (Specifically, mouse messages - // intended for the child window may appear if the child window has - // capture). - // The subsequent PeekMessages call may fail to return any messages thus - // causing us to enter a tight loop at times. - // The code below is a workaround to give the child window - // some time to process its input messages by looping back to - // MsgWaitForMultipleObjectsEx above when there are no messages for the - // current thread. - MSG msg = {0}; - bool has_pending_sent_message = - (HIWORD(GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0; - if (has_pending_sent_message || - PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE)) { - return; - } - - // We know there are no more messages for this thread because PeekMessage - // has returned false. Reset |wait_flags| so that we wait for a *new* - // message. - wait_flags = 0; - } - - DCHECK_NE(WAIT_FAILED, result) << GetLastError(); - } -} - -void MessagePumpForUI::HandleWorkMessage() { - // If we are being called outside of the context of Run, then don't try to do - // any work. This could correspond to a MessageBox call or something of that - // sort. - if (!state_) { - // Since we handled a kMsgHaveWork message, we must still update this flag. - InterlockedExchange(&work_state_, READY); - return; - } - - // Let whatever would have run had we not been putting messages in the queue - // run now. This is an attempt to make our dummy message not starve other - // messages that may be in the Windows message queue. - ProcessPumpReplacementMessage(); - - // Now give the delegate a chance to do some work. It'll let us know if it - // needs to do more work. - if (state_->delegate->DoWork()) - ScheduleWork(); - state_->delegate->DoDelayedWork(&delayed_work_time_); - RescheduleTimer(); -} - -void MessagePumpForUI::HandleTimerMessage() { - KillTimer(message_window_.hwnd(), reinterpret_cast(this)); - - // If we are being called outside of the context of Run, then don't do - // anything. This could correspond to a MessageBox call or something of - // that sort. - if (!state_) - return; - - state_->delegate->DoDelayedWork(&delayed_work_time_); - RescheduleTimer(); -} - -void MessagePumpForUI::RescheduleTimer() { - if (delayed_work_time_.is_null()) - return; - // - // We would *like* to provide high resolution timers. Windows timers using - // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup - // mechanism because the application can enter modal windows loops where it - // is not running our MessageLoop; the only way to have our timers fire in - // these cases is to post messages there. - // - // To provide sub-10ms timers, we process timers directly from our run loop. - // For the common case, timers will be processed there as the run loop does - // its normal work. However, we *also* set the system timer so that WM_TIMER - // events fire. This mops up the case of timers not being able to work in - // modal message loops. It is possible for the SetTimer to pop and have no - // pending timers, because they could have already been processed by the - // run loop itself. - // - // We use a single SetTimer corresponding to the timer that will expire - // soonest. As new timers are created and destroyed, we update SetTimer. - // Getting a spurious SetTimer event firing is benign, as we'll just be - // processing an empty timer queue. - // - int delay_msec = GetCurrentDelay(); - DCHECK_GE(delay_msec, 0); - if (delay_msec == 0) { - ScheduleWork(); - } else { - if (delay_msec < USER_TIMER_MINIMUM) - delay_msec = USER_TIMER_MINIMUM; - - // Tell the optimizer to retain these values to simplify analyzing hangs. - base::debug::Alias(&delay_msec); - // Create a WM_TIMER event that will wake us up to check for any pending - // timers (in case we are running within a nested, external sub-pump). - UINT_PTR ret = SetTimer(message_window_.hwnd(), 0, delay_msec, nullptr); - if (ret) - return; - // If we can't set timers, we are in big trouble... but cross our fingers - // for now. - // TODO(jar): If we don't see this error, use a CHECK() here instead. - UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR, - MESSAGE_LOOP_PROBLEM_MAX); - } -} - -bool MessagePumpForUI::ProcessNextWindowsMessage() { - // If there are sent messages in the queue then PeekMessage internally - // dispatches the message and returns false. We return true in this - // case to ensure that the message loop peeks again instead of calling - // MsgWaitForMultipleObjectsEx again. - bool sent_messages_in_queue = false; - DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE); - if (HIWORD(queue_status) & QS_SENDMESSAGE) - sent_messages_in_queue = true; - - MSG msg; - if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) != FALSE) - return ProcessMessageHelper(msg); - - return sent_messages_in_queue; -} - -bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { - TRACE_EVENT1("base,toplevel", "MessagePumpForUI::ProcessMessageHelper", - "message", msg.message); - if (WM_QUIT == msg.message) { - if (enable_wm_quit_) { - // Repost the QUIT message so that it will be retrieved by the primary - // GetMessage() loop. - state_->should_quit = true; - PostQuitMessage(static_cast(msg.wParam)); - return false; - } - - // WM_QUIT is the standard way to exit a GetMessage() loop. Our MessageLoop - // has its own quit mechanism, so WM_QUIT is unexpected and should be - // ignored when |enable_wm_quit_| is set to false. - UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", - RECEIVED_WM_QUIT_ERROR, MESSAGE_LOOP_PROBLEM_MAX); - return true; - } - - // While running our main message pump, we discard kMsgHaveWork messages. - if (msg.message == kMsgHaveWork && msg.hwnd == message_window_.hwnd()) - return ProcessPumpReplacementMessage(); - - TranslateMessage(&msg); - DispatchMessage(&msg); - - return true; -} - -bool MessagePumpForUI::ProcessPumpReplacementMessage() { - // When we encounter a kMsgHaveWork message, this method is called to peek and - // process a replacement message. The goal is to make the kMsgHaveWork as non- - // intrusive as possible, even though a continuous stream of such messages are - // posted. This method carefully peeks a message while there is no chance for - // a kMsgHaveWork to be pending, then resets the |have_work_| flag (allowing a - // replacement kMsgHaveWork to possibly be posted), and finally dispatches - // that peeked replacement. Note that the re-post of kMsgHaveWork may be - // asynchronous to this thread!! - - MSG msg; - const bool have_message = - PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) != FALSE; - - // Expect no message or a message different than kMsgHaveWork. - DCHECK(!have_message || kMsgHaveWork != msg.message || - msg.hwnd != message_window_.hwnd()); - - // Since we discarded a kMsgHaveWork message, we must update the flag. - int old_work_state_ = InterlockedExchange(&work_state_, READY); - DCHECK_EQ(HAVE_WORK, old_work_state_); - - // We don't need a special time slice if we didn't have_message to process. - if (!have_message) - return false; - - // Guarantee we'll get another time slice in the case where we go into native - // windows code. This ScheduleWork() may hurt performance a tiny bit when - // tasks appear very infrequently, but when the event queue is busy, the - // kMsgHaveWork events get (percentage wise) rarer and rarer. - ScheduleWork(); - return ProcessMessageHelper(msg); -} - -//----------------------------------------------------------------------------- -// MessagePumpForIO public: - -MessagePumpForIO::IOContext::IOContext() { - memset(&overlapped, 0, sizeof(overlapped)); -} - -MessagePumpForIO::MessagePumpForIO() { - port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, - reinterpret_cast(nullptr), 1)); - DCHECK(port_.IsValid()); -} - -MessagePumpForIO::~MessagePumpForIO() = default; - -void MessagePumpForIO::ScheduleWork() { - if (InterlockedExchange(&work_state_, HAVE_WORK) != READY) - return; // Someone else continued the pumping. - - // Make sure the MessagePump does some work for us. - BOOL ret = PostQueuedCompletionStatus(port_.Get(), 0, - reinterpret_cast(this), - reinterpret_cast(this)); - if (ret) - return; // Post worked perfectly. - - // See comment in MessagePumpForUI::ScheduleWork() for this error recovery. - InterlockedExchange(&work_state_, READY); // Clarify that we didn't succeed. - UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR, - MESSAGE_LOOP_PROBLEM_MAX); -} - -void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { - // We know that we can't be blocked right now since this method can only be - // called on the same thread as Run, so we only need to update our record of - // how long to sleep when we do sleep. - delayed_work_time_ = delayed_work_time; -} - -HRESULT MessagePumpForIO::RegisterIOHandler(HANDLE file_handle, - IOHandler* handler) { - HANDLE port = CreateIoCompletionPort(file_handle, port_.Get(), - reinterpret_cast(handler), 1); - return (port != nullptr) ? S_OK : HRESULT_FROM_WIN32(GetLastError()); -} - -bool MessagePumpForIO::RegisterJobObject(HANDLE job_handle, - IOHandler* handler) { - JOBOBJECT_ASSOCIATE_COMPLETION_PORT info; - info.CompletionKey = handler; - info.CompletionPort = port_.Get(); - return SetInformationJobObject(job_handle, - JobObjectAssociateCompletionPortInformation, - &info, - sizeof(info)) != FALSE; -} - -//----------------------------------------------------------------------------- -// MessagePumpForIO private: - -void MessagePumpForIO::DoRunLoop() { - for (;;) { - // If we do any work, we may create more messages etc., and more work may - // possibly be waiting in another task group. When we (for example) - // WaitForIOCompletion(), there is a good chance there are still more - // messages waiting. On the other hand, when any of these methods return - // having done no work, then it is pretty unlikely that calling them - // again quickly will find any work to do. Finally, if they all say they - // had no work, then it is a good time to consider sleeping (waiting) for - // more work. - - bool more_work_is_plausible = state_->delegate->DoWork(); - if (state_->should_quit) - break; - - more_work_is_plausible |= WaitForIOCompletion(0, nullptr); - 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; - - if (more_work_is_plausible) - continue; - - WaitForWork(); // Wait (sleep) until we have work to do again. - } -} - -// Wait until IO completes, up to the time needed by the timer manager to fire -// the next set of timers. -void MessagePumpForIO::WaitForWork() { - // We do not support nested IO message loops. This is to avoid messy - // recursion problems. - DCHECK_EQ(1, state_->run_depth) << "Cannot nest an IO message loop!"; - - int timeout = GetCurrentDelay(); - if (timeout < 0) // Negative value means no timers waiting. - timeout = INFINITE; - - // Tell the optimizer to retain these values to simplify analyzing hangs. - base::debug::Alias(&timeout); - WaitForIOCompletion(timeout, nullptr); -} - -bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { - IOItem item; - if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) { - // We have to ask the system for another IO completion. - if (!GetIOItem(timeout, &item)) - return false; - - if (ProcessInternalIOItem(item)) - return true; - } - - if (filter && item.handler != filter) { - // Save this item for later - completed_io_.push_back(item); - } else { - item.handler->OnIOCompleted(item.context, item.bytes_transfered, - item.error); - } - return true; -} - -// Asks the OS for another IO completion result. -bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) { - memset(item, 0, sizeof(*item)); - ULONG_PTR key = reinterpret_cast(nullptr); - OVERLAPPED* overlapped = nullptr; - if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key, - &overlapped, timeout)) { - if (!overlapped) - return false; // Nothing in the queue. - item->error = GetLastError(); - item->bytes_transfered = 0; - } - - item->handler = reinterpret_cast(key); - item->context = reinterpret_cast(overlapped); - return true; -} - -bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { - if (reinterpret_cast(this) == reinterpret_cast(item.context) && - reinterpret_cast(this) == reinterpret_cast(item.handler)) { - // This is our internal completion. - DCHECK(!item.bytes_transfered); - InterlockedExchange(&work_state_, READY); - return true; - } - return false; -} - -// Returns a completion item that was previously received. -bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { - DCHECK(!completed_io_.empty()); - for (std::list::iterator it = completed_io_.begin(); - it != completed_io_.end(); ++it) { - if (!filter || it->handler == filter) { - *item = *it; - completed_io_.erase(it); - return true; - } - } - return false; -} - -} // namespace base diff --git a/message_loop/message_pump_win.h b/message_loop/message_pump_win.h deleted file mode 100644 index 900fcc0e3..000000000 --- a/message_loop/message_pump_win.h +++ /dev/null @@ -1,256 +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_WIN_H_ -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_WIN_H_ - -#include - -#include -#include - -#include "base/base_export.h" -#include "base/message_loop/message_pump.h" -#include "base/time/time.h" -#include "base/win/message_window.h" -#include "base/win/scoped_handle.h" - -namespace base { - -// MessagePumpWin serves as the base for specialized versions of the MessagePump -// for Windows. It provides basic functionality like handling of observers and -// controlling the lifetime of the message pump. -class BASE_EXPORT MessagePumpWin : public MessagePump { - public: - MessagePumpWin(); - - // MessagePump methods: - void Run(Delegate* delegate) override; - void Quit() override; - - protected: - struct RunState { - Delegate* delegate; - - // Used to flag that the current Run() invocation should return ASAP. - bool should_quit; - - // Used to count how many Run() invocations are on the stack. - int run_depth; - }; - - // State used with |work_state_| variable. - enum WorkState { - READY = 0, // Ready to accept new work. - HAVE_WORK = 1, // New work has been signalled. - WORKING = 2 // Handling the work. - }; - - virtual void DoRunLoop() = 0; - int GetCurrentDelay() const; - - // The time at which delayed work should run. - TimeTicks delayed_work_time_; - - // A value used to indicate if there is a kMsgDoWork message pending - // in the Windows Message queue. There is at most one such message, and it - // can drive execution of tasks when a native message pump is running. - LONG work_state_ = READY; - - // State for the current invocation of Run. - RunState* state_ = nullptr; -}; - -//----------------------------------------------------------------------------- -// MessagePumpForUI extends MessagePumpWin with methods that are particular to a -// MessageLoop instantiated with TYPE_UI. -// -// MessagePumpForUI implements a "traditional" Windows message pump. It contains -// a nearly infinite loop that peeks out messages, and then dispatches them. -// Intermixed with those peeks are callouts to DoWork for pending tasks, and -// DoDelayedWork for pending timers. When there are no events to be serviced, -// this pump goes into a wait state. In most cases, this message pump handles -// all processing. -// -// However, when a task, or windows event, invokes on the stack a native dialog -// box or such, that window typically provides a bare bones (native?) message -// pump. That bare-bones message pump generally supports little more than a -// peek of the Windows message queue, followed by a dispatch of the peeked -// message. MessageLoop extends that bare-bones message pump to also service -// Tasks, at the cost of some complexity. -// -// The basic structure of the extension (referred to as a sub-pump) is that a -// special message, kMsgHaveWork, is repeatedly injected into the Windows -// Message queue. Each time the kMsgHaveWork message is peeked, checks are -// made for an extended set of events, including the availability of Tasks to -// run. -// -// After running a task, the special message kMsgHaveWork is again posted to -// the Windows Message queue, ensuring a future time slice for processing a -// future event. To prevent flooding the Windows Message queue, care is taken -// to be sure that at most one kMsgHaveWork message is EVER pending in the -// Window's Message queue. -// -// There are a few additional complexities in this system where, when there are -// no Tasks to run, this otherwise infinite stream of messages which drives the -// sub-pump is halted. The pump is automatically re-started when Tasks are -// queued. -// -// A second complexity is that the presence of this stream of posted tasks may -// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER. -// Such paint and timer events always give priority to a posted message, such as -// kMsgHaveWork messages. As a result, care is taken to do some peeking in -// between the posting of each kMsgHaveWork message (i.e., after kMsgHaveWork -// is peeked, and before a replacement kMsgHaveWork is posted). -// -// NOTE: Although it may seem odd that messages are used to start and stop this -// flow (as opposed to signaling objects, etc.), it should be understood that -// the native message pump will *only* respond to messages. As a result, it is -// an excellent choice. It is also helpful that the starter messages that are -// placed in the queue when new task arrive also awakens DoRunLoop. -// -class BASE_EXPORT MessagePumpForUI : public MessagePumpWin { - public: - MessagePumpForUI(); - ~MessagePumpForUI() override; - - // MessagePump methods: - void ScheduleWork() override; - void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; - - // Make the MessagePumpForUI respond to WM_QUIT messages. - void EnableWmQuit(); - - private: - bool MessageCallback( - UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result); - void DoRunLoop() override; - void WaitForWork(); - void HandleWorkMessage(); - void HandleTimerMessage(); - void RescheduleTimer(); - bool ProcessNextWindowsMessage(); - bool ProcessMessageHelper(const MSG& msg); - bool ProcessPumpReplacementMessage(); - - base::win::MessageWindow message_window_; - - // Whether MessagePumpForUI responds to WM_QUIT messages or not. - // TODO(thestig): Remove when the Cloud Print Service goes away. - bool enable_wm_quit_ = false; -}; - -//----------------------------------------------------------------------------- -// MessagePumpForIO extends MessagePumpWin with methods that are particular to a -// MessageLoop instantiated with TYPE_IO. This version of MessagePump does not -// deal with Windows mesagges, and instead has a Run loop based on Completion -// Ports so it is better suited for IO operations. -// -class BASE_EXPORT MessagePumpForIO : public MessagePumpWin { - public: - struct BASE_EXPORT IOContext { - IOContext(); - OVERLAPPED overlapped; - }; - - // Clients interested in receiving OS notifications when asynchronous IO - // operations complete should implement this interface and register themselves - // with the message pump. - // - // Typical use #1: - // class MyFile : public IOHandler { - // MyFile() { - // ... - // message_pump->RegisterIOHandler(file_, this); - // } - // // Plus some code to make sure that this destructor is not called - // // while there are pending IO operations. - // ~MyFile() { - // } - // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, - // DWORD error) { - // ... - // delete context; - // } - // void DoSomeIo() { - // ... - // IOContext* context = new IOContext; - // ReadFile(file_, buffer, num_bytes, &read, &context); - // } - // HANDLE file_; - // }; - // - // Typical use #2: - // Same as the previous example, except that in order to deal with the - // requirement stated for the destructor, the class calls WaitForIOCompletion - // from the destructor to block until all IO finishes. - // ~MyFile() { - // while(pending_) - // message_pump->WaitForIOCompletion(INFINITE, this); - // } - // - class IOHandler { - public: - virtual ~IOHandler() {} - // This will be called once the pending IO operation associated with - // |context| completes. |error| is the Win32 error code of the IO operation - // (ERROR_SUCCESS if there was no error). |bytes_transfered| will be zero - // on error. - virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, - DWORD error) = 0; - }; - - MessagePumpForIO(); - ~MessagePumpForIO() override; - - // MessagePump methods: - void ScheduleWork() override; - void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; - - // Register the handler to be used when asynchronous IO for the given file - // completes. The registration persists as long as |file_handle| is valid, so - // |handler| must be valid as long as there is pending IO for the given file. - HRESULT RegisterIOHandler(HANDLE file_handle, IOHandler* handler); - - // Register the handler to be used to process job events. The registration - // persists as long as the job object is live, so |handler| must be valid - // until the job object is destroyed. Returns true if the registration - // succeeded, and false otherwise. - bool RegisterJobObject(HANDLE job_handle, IOHandler* handler); - - // Waits for the next IO completion that should be processed by |filter|, for - // up to |timeout| milliseconds. Return true if any IO operation completed, - // regardless of the involved handler, and false if the timeout expired. If - // the completion port received any message and the involved IO handler - // matches |filter|, the callback is called before returning from this code; - // if the handler is not the one that we are looking for, the callback will - // be postponed for another time, so reentrancy problems can be avoided. - // External use of this method should be reserved for the rare case when the - // caller is willing to allow pausing regular task dispatching on this thread. - bool WaitForIOCompletion(DWORD timeout, IOHandler* filter); - - private: - struct IOItem { - IOHandler* handler; - IOContext* context; - DWORD bytes_transfered; - DWORD error; - }; - - void DoRunLoop() override; - void WaitForWork(); - bool MatchCompletedIOItem(IOHandler* filter, IOItem* item); - bool GetIOItem(DWORD timeout, IOItem* item); - bool ProcessInternalIOItem(const IOItem& item); - - // The completion port associated with this thread. - win::ScopedHandle port_; - // This list will be empty almost always. It stores IO completions that have - // not been delivered yet because somebody was doing cleanup. - std::list completed_io_; -}; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_WIN_H_ diff --git a/message_loop/timer_slack.h b/message_loop/timer_slack.h deleted file mode 100644 index 1ad6ca94a..000000000 --- a/message_loop/timer_slack.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_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/message_loop/watchable_io_message_pump_posix.cc b/message_loop/watchable_io_message_pump_posix.cc deleted file mode 100644 index 185013760..000000000 --- a/message_loop/watchable_io_message_pump_posix.cc +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018 The Chromium 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/watchable_io_message_pump_posix.h" - -namespace base { - -WatchableIOMessagePumpPosix::FdWatchControllerInterface:: - FdWatchControllerInterface(const Location& from_here) - : created_from_location_(from_here) {} - -WatchableIOMessagePumpPosix::FdWatchControllerInterface:: - ~FdWatchControllerInterface() = default; - -} // namespace base diff --git a/message_loop/watchable_io_message_pump_posix.h b/message_loop/watchable_io_message_pump_posix.h deleted file mode 100644 index 74583d9a2..000000000 --- a/message_loop/watchable_io_message_pump_posix.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2018 The Chromium 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_WATCHABLE_IO_MESSAGE_PUMP_POSIX_H_ -#define BASE_MESSAGE_LOOP_WATCHABLE_IO_MESSAGE_PUMP_POSIX_H_ - -#include "base/location.h" -#include "base/macros.h" - -namespace base { - -class WatchableIOMessagePumpPosix { - public: - // Used with WatchFileDescriptor to asynchronously monitor the I/O readiness - // of a file descriptor. - class FdWatcher { - public: - virtual void OnFileCanReadWithoutBlocking(int fd) = 0; - virtual void OnFileCanWriteWithoutBlocking(int fd) = 0; - - protected: - virtual ~FdWatcher() = default; - }; - - class FdWatchControllerInterface { - public: - explicit FdWatchControllerInterface(const Location& from_here); - // Subclasses must call StopWatchingFileDescriptor() in their destructor - // (this parent class cannot generically do it for them as it must usually - // be invoked before they destroy their state which happens before the - // parent destructor is invoked). - virtual ~FdWatchControllerInterface(); - - // NOTE: This method isn't called StopWatching() to avoid confusion with the - // win32 ObjectWatcher class. While this doesn't really need to be virtual - // as there's only one impl per platform and users don't use pointers to the - // base class. Having this interface forces implementers to share similar - // implementations (a problem in the past). - - // Stop watching the FD, always safe to call. No-op if there's nothing to - // do. - virtual bool StopWatchingFileDescriptor() = 0; - - const Location& created_from_location() const { - return created_from_location_; - } - - private: - const Location created_from_location_; - - DISALLOW_COPY_AND_ASSIGN(FdWatchControllerInterface); - }; - - enum Mode { - WATCH_READ = 1 << 0, - WATCH_WRITE = 1 << 1, - WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE - }; - - // Every subclass of WatchableIOMessagePumpPosix must provide a - // WatchFileDescriptor() which has the following signature where - // |FdWatchController| must be the complete type based on - // FdWatchControllerInterface. - - // Registers |delegate| with the current thread's message loop so that its - // methods are invoked when file descriptor |fd| becomes ready for reading or - // writing (or both) without blocking. |mode| selects ready for reading, for - // writing, or both. See "enum Mode" above. |controller| manages the - // lifetime of registrations. ("Registrations" are also ambiguously called - // "events" in many places, for instance in libevent.) It is an error to use - // the same |controller| for different file descriptors; however, the same - // controller can be reused to add registrations with a different |mode|. If - // |controller| is already attached to one or more registrations, the new - // registration is added onto those. If an error occurs while calling this - // method, any registration previously attached to |controller| is removed. - // Returns true on success. Must be called on the same thread the MessagePump - // is running on. - // bool WatchFileDescriptor(int fd, - // bool persistent, - // int mode, - // FdWatchController* controller, - // FdWatcher* delegate) = 0; -}; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_WATCHABLE_IO_MESSAGE_PUMP_POSIX_H_ diff --git a/metrics/OWNERS b/metrics/OWNERS deleted file mode 100644 index 4cc69ff06..000000000 --- a/metrics/OWNERS +++ /dev/null @@ -1,10 +0,0 @@ -asvitkine@chromium.org -bcwhite@chromium.org -gayane@chromium.org -holte@chromium.org -isherman@chromium.org -jwd@chromium.org -mpearson@chromium.org -rkaplow@chromium.org - -# COMPONENT: Internals>Metrics diff --git a/metrics/bucket_ranges.cc b/metrics/bucket_ranges.cc deleted file mode 100644 index 39b379320..000000000 --- a/metrics/bucket_ranges.cc +++ /dev/null @@ -1,124 +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/metrics/bucket_ranges.h" - -#include - -#include "base/logging.h" - -namespace base { - -// Static table of checksums for all possible 8 bit bytes. -const uint32_t kCrcTable[256] = { - 0x0, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x76dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x6b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL, -}; - -// We generate the CRC-32 using the low order bits to select whether to XOR in -// the reversed polynomial 0xedb88320L. This is nice and simple, and allows us -// to keep the quotient in a uint32_t. Since we're not concerned about the -// nature of corruptions (i.e., we don't care about bit sequencing, since we are -// handling memory changes, which are more grotesque) so we don't bother to get -// the CRC correct for big-endian vs little-ending calculations. All we need is -// a nice hash, that tends to depend on all the bits of the sample, with very -// little chance of changes in one place impacting changes in another place. -static uint32_t Crc32(uint32_t sum, HistogramBase::Sample value) { - union { - HistogramBase::Sample range; - unsigned char bytes[sizeof(HistogramBase::Sample)]; - } converter; - converter.range = value; - for (size_t i = 0; i < sizeof(converter); ++i) { - sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8); - } - return sum; -} - -BucketRanges::BucketRanges(size_t num_ranges) - : ranges_(num_ranges, 0), - checksum_(0) {} - -BucketRanges::~BucketRanges() = default; - -uint32_t BucketRanges::CalculateChecksum() const { - // Seed checksum. - uint32_t checksum = static_cast(ranges_.size()); - - for (size_t index = 0; index < ranges_.size(); ++index) - checksum = Crc32(checksum, ranges_[index]); - return checksum; -} - -bool BucketRanges::HasValidChecksum() const { - return CalculateChecksum() == checksum_; -} - -void BucketRanges::ResetChecksum() { - checksum_ = CalculateChecksum(); -} - -bool BucketRanges::Equals(const BucketRanges* other) const { - if (checksum_ != other->checksum_) - return false; - if (ranges_.size() != other->ranges_.size()) - return false; - for (size_t index = 0; index < ranges_.size(); ++index) { - if (ranges_[index] != other->ranges_[index]) - return false; - } - return true; -} - -} // namespace base diff --git a/metrics/bucket_ranges.h b/metrics/bucket_ranges.h deleted file mode 100644 index 1b6d069bd..000000000 --- a/metrics/bucket_ranges.h +++ /dev/null @@ -1,105 +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. -// -// BucketRanges stores the vector of ranges that delimit what samples are -// tallied in the corresponding buckets of a histogram. Histograms that have -// same ranges for all their corresponding buckets should share the same -// BucketRanges object. -// -// E.g. A 5 buckets LinearHistogram with 1 as minimal value and 4 as maximal -// value will need a BucketRanges with 6 ranges: -// 0, 1, 2, 3, 4, INT_MAX -// -// TODO(kaiwang): Currently we keep all negative values in 0~1 bucket. Consider -// changing 0 to INT_MIN. - -#ifndef BASE_METRICS_BUCKET_RANGES_H_ -#define BASE_METRICS_BUCKET_RANGES_H_ - -#include -#include - -#include - -#include - -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/macros.h" -#include "base/metrics/histogram_base.h" - -namespace base { - -class BASE_EXPORT BucketRanges { - public: - typedef std::vector Ranges; - - explicit BucketRanges(size_t num_ranges); - ~BucketRanges(); - - size_t size() const { return ranges_.size(); } - HistogramBase::Sample range(size_t i) const { return ranges_[i]; } - void set_range(size_t i, HistogramBase::Sample value) { - DCHECK_LT(i, ranges_.size()); - DCHECK_GE(value, 0); - ranges_[i] = value; - } - uint32_t checksum() const { return checksum_; } - void set_checksum(uint32_t checksum) { checksum_ = checksum; } - - // A bucket is defined by a consecutive pair of entries in |ranges|, so there - // is one fewer bucket than there are ranges. For example, if |ranges| is - // [0, 1, 3, 7, INT_MAX], then the buckets in this histogram are - // [0, 1), [1, 3), [3, 7), and [7, INT_MAX). - size_t bucket_count() const { return ranges_.size() - 1; } - - // Checksum methods to verify whether the ranges are corrupted (e.g. bad - // memory access). - uint32_t CalculateChecksum() const; - bool HasValidChecksum() const; - void ResetChecksum(); - - // Return true iff |other| object has same ranges_ as |this| object's ranges_. - bool Equals(const BucketRanges* other) const; - - // Set and get a reference into persistent memory where this bucket data - // can be found (and re-used). These calls are internally atomic with no - // safety against overwriting an existing value since though it is wasteful - // to have multiple identical persistent records, it is still safe. - void set_persistent_reference(uint32_t ref) const { - subtle::Release_Store(&persistent_reference_, ref); - } - uint32_t persistent_reference() const { - return subtle::Acquire_Load(&persistent_reference_); - } - - private: - // A monotonically increasing list of values which determine which bucket to - // put a sample into. For each index, show the smallest sample that can be - // added to the corresponding bucket. - Ranges ranges_; - - // Checksum for the conntents of ranges_. Used to detect random over-writes - // of our data, and to quickly see if some other BucketRanges instance is - // possibly Equal() to this instance. - // TODO(kaiwang): Consider change this to uint64_t. Because we see a lot of - // noise on UMA dashboard. - uint32_t checksum_; - - // A reference into a global PersistentMemoryAllocator where the ranges - // information is stored. This allows for the record to be created once and - // re-used simply by having all histograms with the same ranges use the - // same reference. - mutable subtle::Atomic32 persistent_reference_ = 0; - - DISALLOW_COPY_AND_ASSIGN(BucketRanges); -}; - -////////////////////////////////////////////////////////////////////////////// -// Expose only for test. -BASE_EXPORT extern const uint32_t kCrcTable[256]; - -} // namespace base - -#endif // BASE_METRICS_BUCKET_RANGES_H_ diff --git a/metrics/bucket_ranges_unittest.cc b/metrics/bucket_ranges_unittest.cc deleted file mode 100644 index 481054c5d..000000000 --- a/metrics/bucket_ranges_unittest.cc +++ /dev/null @@ -1,94 +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/metrics/bucket_ranges.h" - -#include - -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -TEST(BucketRangesTest, NormalSetup) { - BucketRanges ranges(5); - ASSERT_EQ(5u, ranges.size()); - ASSERT_EQ(4u, ranges.bucket_count()); - - for (int i = 0; i < 5; ++i) { - EXPECT_EQ(0, ranges.range(i)); - } - EXPECT_EQ(0u, ranges.checksum()); - - ranges.set_range(3, 100); - EXPECT_EQ(100, ranges.range(3)); -} - -TEST(BucketRangesTest, Equals) { - // Compare empty ranges. - BucketRanges ranges1(3); - BucketRanges ranges2(3); - BucketRanges ranges3(5); - - EXPECT_TRUE(ranges1.Equals(&ranges2)); - EXPECT_FALSE(ranges1.Equals(&ranges3)); - EXPECT_FALSE(ranges2.Equals(&ranges3)); - - // Compare full filled ranges. - ranges1.set_range(0, 0); - ranges1.set_range(1, 1); - ranges1.set_range(2, 2); - ranges1.set_checksum(100); - ranges2.set_range(0, 0); - ranges2.set_range(1, 1); - ranges2.set_range(2, 2); - ranges2.set_checksum(100); - - EXPECT_TRUE(ranges1.Equals(&ranges2)); - - // Checksum does not match. - ranges1.set_checksum(99); - EXPECT_FALSE(ranges1.Equals(&ranges2)); - ranges1.set_checksum(100); - - // Range does not match. - ranges1.set_range(1, 3); - EXPECT_FALSE(ranges1.Equals(&ranges2)); -} - -TEST(BucketRangesTest, Checksum) { - BucketRanges ranges(3); - ranges.set_range(0, 0); - ranges.set_range(1, 1); - ranges.set_range(2, 2); - - ranges.ResetChecksum(); - EXPECT_EQ(289217253u, ranges.checksum()); - - ranges.set_range(2, 3); - EXPECT_FALSE(ranges.HasValidChecksum()); - - ranges.ResetChecksum(); - EXPECT_EQ(2843835776u, ranges.checksum()); - EXPECT_TRUE(ranges.HasValidChecksum()); -} - -// Table was generated similarly to sample code for CRC-32 given on: -// http://www.w3.org/TR/PNG/#D-CRCAppendix. -TEST(BucketRangesTest, Crc32TableTest) { - for (int i = 0; i < 256; ++i) { - uint32_t checksum = i; - for (int j = 0; j < 8; ++j) { - const uint32_t kReversedPolynomial = 0xedb88320L; - if (checksum & 1) - checksum = kReversedPolynomial ^ (checksum >> 1); - else - checksum >>= 1; - } - EXPECT_EQ(kCrcTable[i], checksum); - } -} - -} // namespace -} // namespace base diff --git a/metrics/dummy_histogram.cc b/metrics/dummy_histogram.cc deleted file mode 100644 index 2707733b2..000000000 --- a/metrics/dummy_histogram.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2017 The Chromium 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/dummy_histogram.h" - -#include - -#include "base/logging.h" -#include "base/metrics/histogram_samples.h" -#include "base/metrics/metrics_hashes.h" - -namespace base { - -namespace { - -// Helper classes for DummyHistogram. -class DummySampleCountIterator : public SampleCountIterator { - public: - DummySampleCountIterator() {} - ~DummySampleCountIterator() override {} - - // SampleCountIterator: - bool Done() const override { return true; } - void Next() override { NOTREACHED(); } - void Get(HistogramBase::Sample* min, - int64_t* max, - HistogramBase::Count* count) const override { - NOTREACHED(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(DummySampleCountIterator); -}; - -class DummyHistogramSamples : public HistogramSamples { - public: - explicit DummyHistogramSamples() : HistogramSamples(0, new LocalMetadata()) {} - ~DummyHistogramSamples() override { - delete static_cast(meta()); - } - - // HistogramSamples: - void Accumulate(HistogramBase::Sample value, - HistogramBase::Count count) override {} - HistogramBase::Count GetCount(HistogramBase::Sample value) const override { - return HistogramBase::Count(); - } - HistogramBase::Count TotalCount() const override { - return HistogramBase::Count(); - } - std::unique_ptr Iterator() const override { - return std::make_unique(); - } - bool AddSubtractImpl(SampleCountIterator* iter, Operator op) override { - return true; - } - - private: - DISALLOW_COPY_AND_ASSIGN(DummyHistogramSamples); -}; - -} // namespace - -// static -DummyHistogram* DummyHistogram::GetInstance() { - static base::NoDestructor dummy_histogram; - return dummy_histogram.get(); -} - -uint64_t DummyHistogram::name_hash() const { - return HashMetricName(histogram_name()); -} - -HistogramType DummyHistogram::GetHistogramType() const { - return DUMMY_HISTOGRAM; -} - -bool DummyHistogram::HasConstructionArguments( - Sample expected_minimum, - Sample expected_maximum, - uint32_t expected_bucket_count) const { - return true; -} - -bool DummyHistogram::AddSamplesFromPickle(PickleIterator* iter) { - return true; -} - -std::unique_ptr DummyHistogram::SnapshotSamples() const { - return std::make_unique(); -} - -std::unique_ptr DummyHistogram::SnapshotDelta() { - return std::make_unique(); -} - -std::unique_ptr DummyHistogram::SnapshotFinalDelta() const { - return std::make_unique(); -} - -} // namespace base diff --git a/metrics/dummy_histogram.h b/metrics/dummy_histogram.h deleted file mode 100644 index e2cb64ecb..000000000 --- a/metrics/dummy_histogram.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2017 The Chromium 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_DUMMY_HISTOGRAM_H_ -#define BASE_METRICS_DUMMY_HISTOGRAM_H_ - -#include - -#include -#include - -#include "base/base_export.h" -#include "base/metrics/histogram_base.h" -#include "base/no_destructor.h" - -namespace base { - -// DummyHistogram is used for mocking histogram objects for histograms that -// shouldn't be recorded. It doesn't do any actual processing. -class BASE_EXPORT DummyHistogram : public HistogramBase { - public: - static DummyHistogram* GetInstance(); - - // HistogramBase: - void CheckName(const StringPiece& name) const override {} - uint64_t name_hash() const override; - HistogramType GetHistogramType() const override; - bool HasConstructionArguments(Sample expected_minimum, - Sample expected_maximum, - uint32_t expected_bucket_count) const override; - void Add(Sample value) override {} - void AddCount(Sample value, int count) override {} - void AddSamples(const HistogramSamples& samples) override {} - bool AddSamplesFromPickle(PickleIterator* iter) override; - std::unique_ptr SnapshotSamples() const override; - std::unique_ptr SnapshotDelta() override; - std::unique_ptr SnapshotFinalDelta() const override; - void WriteHTMLGraph(std::string* output) const override {} - void WriteAscii(std::string* output) const override {} - - protected: - // HistogramBase: - void SerializeInfoImpl(Pickle* pickle) const override {} - void GetParameters(DictionaryValue* params) const override {} - void GetCountAndBucketData(Count* count, - int64_t* sum, - ListValue* buckets) const override {} - - private: - friend class NoDestructor; - - DummyHistogram() : HistogramBase("dummy_histogram") {} - ~DummyHistogram() override {} - - DISALLOW_COPY_AND_ASSIGN(DummyHistogram); -}; - -} // namespace base - -#endif // BASE_METRICS_DUMMY_HISTOGRAM_H_ diff --git a/metrics/field_trial.cc b/metrics/field_trial.cc deleted file mode 100644 index ff37880c5..000000000 --- a/metrics/field_trial.cc +++ /dev/null @@ -1,1522 +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/metrics/field_trial.h" - -#include -#include - -#include "base/base_switches.h" -#include "base/build_time.h" -#include "base/command_line.h" -#include "base/debug/activity_tracker.h" -#include "base/logging.h" -#include "base/metrics/field_trial_param_associator.h" -#include "base/process/memory.h" -#include "base/process/process_handle.h" -#include "base/process/process_info.h" -#include "base/rand_util.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/unguessable_token.h" - -// On POSIX, the fd is shared using the mapping in GlobalDescriptors. -#if defined(OS_POSIX) && !defined(OS_NACL) -#include "base/posix/global_descriptors.h" -#endif - -namespace base { - -namespace { - -// Define a separator character to use when creating a persistent form of an -// instance. This is intended for use as a command line argument, passed to a -// second process to mimic our state (i.e., provide the same group name). -const char kPersistentStringSeparator = '/'; // Currently a slash. - -// Define a marker character to be used as a prefix to a trial name on the -// command line which forces its activation. -const char kActivationMarker = '*'; - -// Use shared memory to communicate field trial (experiment) state. Set to false -// for now while the implementation is fleshed out (e.g. data format, single -// shared memory segment). See https://codereview.chromium.org/2365273004/ and -// crbug.com/653874 -// The browser is the only process that has write access to the shared memory. -// This is safe from race conditions because MakeIterable is a release operation -// and GetNextOfType is an acquire operation, so memory writes before -// MakeIterable happen before memory reads after GetNextOfType. -#if defined(OS_FUCHSIA) // TODO(752368): Not yet supported on Fuchsia. -const bool kUseSharedMemoryForFieldTrials = false; -#else -const bool kUseSharedMemoryForFieldTrials = true; -#endif - -// Constants for the field trial allocator. -const char kAllocatorName[] = "FieldTrialAllocator"; - -// We allocate 128 KiB to hold all the field trial data. This should be enough, -// as most people use 3 - 25 KiB for field trials (as of 11/25/2016). -// This also doesn't allocate all 128 KiB at once -- the pages only get mapped -// to physical memory when they are touched. If the size of the allocated field -// trials does get larger than 128 KiB, then we will drop some field trials in -// child processes, leading to an inconsistent view between browser and child -// processes and possibly causing crashes (see crbug.com/661617). -const size_t kFieldTrialAllocationSize = 128 << 10; // 128 KiB - -// Writes out string1 and then string2 to pickle. -void WriteStringPair(Pickle* pickle, - const StringPiece& string1, - const StringPiece& string2) { - pickle->WriteString(string1); - pickle->WriteString(string2); -} - -// Writes out the field trial's contents (via trial_state) to the pickle. The -// format of the pickle looks like: -// TrialName, GroupName, ParamKey1, ParamValue1, ParamKey2, ParamValue2, ... -// If there are no parameters, then it just ends at GroupName. -void PickleFieldTrial(const FieldTrial::State& trial_state, Pickle* pickle) { - WriteStringPair(pickle, *trial_state.trial_name, *trial_state.group_name); - - // Get field trial params. - std::map params; - FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback( - *trial_state.trial_name, *trial_state.group_name, ¶ms); - - // Write params to pickle. - for (const auto& param : params) - WriteStringPair(pickle, param.first, param.second); -} - -// Created a time value based on |year|, |month| and |day_of_month| parameters. -Time CreateTimeFromParams(int year, int month, int day_of_month) { - DCHECK_GT(year, 1970); - DCHECK_GT(month, 0); - DCHECK_LT(month, 13); - DCHECK_GT(day_of_month, 0); - DCHECK_LT(day_of_month, 32); - - Time::Exploded exploded; - exploded.year = year; - exploded.month = month; - exploded.day_of_week = 0; // Should be unused. - exploded.day_of_month = day_of_month; - exploded.hour = 0; - exploded.minute = 0; - exploded.second = 0; - exploded.millisecond = 0; - Time out_time; - if (!Time::FromLocalExploded(exploded, &out_time)) { - // TODO(maksims): implement failure handling. - // We might just return |out_time|, which is Time(0). - NOTIMPLEMENTED(); - } - - return out_time; -} - -// Returns the boundary value for comparing against the FieldTrial's added -// groups for a given |divisor| (total probability) and |entropy_value|. -FieldTrial::Probability GetGroupBoundaryValue( - FieldTrial::Probability divisor, - double entropy_value) { - // Add a tiny epsilon value to get consistent results when converting floating - // points to int. Without it, boundary values have inconsistent results, e.g.: - // - // static_cast(100 * 0.56) == 56 - // static_cast(100 * 0.57) == 56 - // static_cast(100 * 0.58) == 57 - // static_cast(100 * 0.59) == 59 - const double kEpsilon = 1e-8; - const FieldTrial::Probability result = - static_cast(divisor * entropy_value + kEpsilon); - // Ensure that adding the epsilon still results in a value < |divisor|. - return std::min(result, divisor - 1); -} - -// Separate type from FieldTrial::State so that it can use StringPieces. -struct FieldTrialStringEntry { - StringPiece trial_name; - StringPiece group_name; - bool activated = false; -}; - -// Parses the --force-fieldtrials string |trials_string| into |entries|. -// Returns true if the string was parsed correctly. On failure, the |entries| -// array may end up being partially filled. -bool ParseFieldTrialsString(const std::string& trials_string, - std::vector* entries) { - const StringPiece trials_string_piece(trials_string); - - size_t next_item = 0; - while (next_item < trials_string.length()) { - size_t name_end = trials_string.find(kPersistentStringSeparator, next_item); - if (name_end == trials_string.npos || next_item == name_end) - return false; - size_t group_name_end = - trials_string.find(kPersistentStringSeparator, name_end + 1); - if (name_end + 1 == group_name_end) - return false; - if (group_name_end == trials_string.npos) - group_name_end = trials_string.length(); - - FieldTrialStringEntry entry; - // Verify if the trial should be activated or not. - if (trials_string[next_item] == kActivationMarker) { - // Name cannot be only the indicator. - if (name_end - next_item == 1) - return false; - next_item++; - entry.activated = true; - } - entry.trial_name = - trials_string_piece.substr(next_item, name_end - next_item); - entry.group_name = - trials_string_piece.substr(name_end + 1, group_name_end - name_end - 1); - next_item = group_name_end + 1; - - entries->push_back(std::move(entry)); - } - return true; -} - -void AddFeatureAndFieldTrialFlags(const char* enable_features_switch, - const char* disable_features_switch, - CommandLine* cmd_line) { - std::string enabled_features; - std::string disabled_features; - FeatureList::GetInstance()->GetFeatureOverrides(&enabled_features, - &disabled_features); - - if (!enabled_features.empty()) - cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features); - if (!disabled_features.empty()) - cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features); - - std::string field_trial_states; - FieldTrialList::AllStatesToString(&field_trial_states, false); - if (!field_trial_states.empty()) { - cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, - field_trial_states); - } -} - -void OnOutOfMemory(size_t size) { -#if defined(OS_NACL) - NOTREACHED(); -#else - TerminateBecauseOutOfMemory(size); -#endif -} - -#if !defined(OS_NACL) -// Returns whether the operation succeeded. -bool DeserializeGUIDFromStringPieces(base::StringPiece first, - base::StringPiece second, - base::UnguessableToken* guid) { - uint64_t high = 0; - uint64_t low = 0; - if (!base::StringToUint64(first, &high) || - !base::StringToUint64(second, &low)) { - return false; - } - - *guid = base::UnguessableToken::Deserialize(high, low); - return true; -} - -// Extract a read-only SharedMemoryHandle from an existing |shared_memory| -// handle. Note that on Android, this also makes the whole region read-only. -SharedMemoryHandle GetSharedMemoryReadOnlyHandle(SharedMemory* shared_memory) { - SharedMemoryHandle result = shared_memory->GetReadOnlyHandle(); -#if defined(OS_ANDROID) - // On Android, turn the region read-only. This prevents any future - // writable mapping attempts, but the original one in |shm| survives - // and is still usable in the current process. - result.SetRegionReadOnly(); -#endif // OS_ANDROID - return result; -} -#endif // !OS_NACL - -} // namespace - -// statics -const int FieldTrial::kNotFinalized = -1; -const int FieldTrial::kDefaultGroupNumber = 0; -bool FieldTrial::enable_benchmarking_ = false; - -int FieldTrialList::kNoExpirationYear = 0; - -//------------------------------------------------------------------------------ -// FieldTrial methods and members. - -FieldTrial::EntropyProvider::~EntropyProvider() = default; - -FieldTrial::State::State() = default; - -FieldTrial::State::State(const State& other) = default; - -FieldTrial::State::~State() = default; - -bool FieldTrial::FieldTrialEntry::GetTrialAndGroupName( - StringPiece* trial_name, - StringPiece* group_name) const { - PickleIterator iter = GetPickleIterator(); - return ReadStringPair(&iter, trial_name, group_name); -} - -bool FieldTrial::FieldTrialEntry::GetParams( - std::map* params) const { - PickleIterator iter = GetPickleIterator(); - StringPiece tmp; - // Skip reading trial and group name. - if (!ReadStringPair(&iter, &tmp, &tmp)) - return false; - - while (true) { - StringPiece key; - StringPiece value; - if (!ReadStringPair(&iter, &key, &value)) - return key.empty(); // Non-empty is bad: got one of a pair. - (*params)[key.as_string()] = value.as_string(); - } -} - -PickleIterator FieldTrial::FieldTrialEntry::GetPickleIterator() const { - const char* src = - reinterpret_cast(this) + sizeof(FieldTrialEntry); - - Pickle pickle(src, pickle_size); - return PickleIterator(pickle); -} - -bool FieldTrial::FieldTrialEntry::ReadStringPair( - PickleIterator* iter, - StringPiece* trial_name, - StringPiece* group_name) const { - if (!iter->ReadStringPiece(trial_name)) - return false; - if (!iter->ReadStringPiece(group_name)) - return false; - return true; -} - -void FieldTrial::Disable() { - DCHECK(!group_reported_); - enable_field_trial_ = false; - - // In case we are disabled after initialization, we need to switch - // the trial to the default group. - if (group_ != kNotFinalized) { - // Only reset when not already the default group, because in case we were - // forced to the default group, the group number may not be - // kDefaultGroupNumber, so we should keep it as is. - if (group_name_ != default_group_name_) - SetGroupChoice(default_group_name_, kDefaultGroupNumber); - } -} - -int FieldTrial::AppendGroup(const std::string& name, - Probability group_probability) { - // When the group choice was previously forced, we only need to return the - // the id of the chosen group, and anything can be returned for the others. - if (forced_) { - DCHECK(!group_name_.empty()); - if (name == group_name_) { - // Note that while |group_| may be equal to |kDefaultGroupNumber| on the - // forced trial, it will not have the same value as the default group - // number returned from the non-forced |FactoryGetFieldTrial()| call, - // which takes care to ensure that this does not happen. - return group_; - } - DCHECK_NE(next_group_number_, group_); - // We still return different numbers each time, in case some caller need - // them to be different. - return next_group_number_++; - } - - DCHECK_LE(group_probability, divisor_); - DCHECK_GE(group_probability, 0); - - if (enable_benchmarking_ || !enable_field_trial_) - group_probability = 0; - - accumulated_group_probability_ += group_probability; - - DCHECK_LE(accumulated_group_probability_, divisor_); - if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { - // This is the group that crossed the random line, so we do the assignment. - SetGroupChoice(name, next_group_number_); - } - return next_group_number_++; -} - -int FieldTrial::group() { - FinalizeGroupChoice(); - if (trial_registered_) - FieldTrialList::NotifyFieldTrialGroupSelection(this); - return group_; -} - -const std::string& FieldTrial::group_name() { - // Call |group()| to ensure group gets assigned and observers are notified. - group(); - DCHECK(!group_name_.empty()); - return group_name_; -} - -const std::string& FieldTrial::GetGroupNameWithoutActivation() { - FinalizeGroupChoice(); - return group_name_; -} - -void FieldTrial::SetForced() { - // We might have been forced before (e.g., by CreateFieldTrial) and it's - // first come first served, e.g., command line switch has precedence. - if (forced_) - return; - - // And we must finalize the group choice before we mark ourselves as forced. - FinalizeGroupChoice(); - forced_ = true; -} - -// static -void FieldTrial::EnableBenchmarking() { - DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount()); - enable_benchmarking_ = true; -} - -// static -FieldTrial* FieldTrial::CreateSimulatedFieldTrial( - const std::string& trial_name, - Probability total_probability, - const std::string& default_group_name, - double entropy_value) { - return new FieldTrial(trial_name, total_probability, default_group_name, - entropy_value); -} - -FieldTrial::FieldTrial(const std::string& trial_name, - const Probability total_probability, - const std::string& default_group_name, - double entropy_value) - : trial_name_(trial_name), - divisor_(total_probability), - default_group_name_(default_group_name), - random_(GetGroupBoundaryValue(total_probability, entropy_value)), - accumulated_group_probability_(0), - next_group_number_(kDefaultGroupNumber + 1), - group_(kNotFinalized), - enable_field_trial_(true), - forced_(false), - group_reported_(false), - trial_registered_(false), - ref_(FieldTrialList::FieldTrialAllocator::kReferenceNull) { - DCHECK_GT(total_probability, 0); - DCHECK(!trial_name_.empty()); - DCHECK(!default_group_name_.empty()) - << "Trial " << trial_name << " is missing a default group name."; -} - -FieldTrial::~FieldTrial() = default; - -void FieldTrial::SetTrialRegistered() { - DCHECK_EQ(kNotFinalized, group_); - DCHECK(!trial_registered_); - trial_registered_ = true; -} - -void FieldTrial::SetGroupChoice(const std::string& group_name, int number) { - group_ = number; - if (group_name.empty()) - StringAppendF(&group_name_, "%d", group_); - else - group_name_ = group_name; - DVLOG(1) << "Field trial: " << trial_name_ << " Group choice:" << group_name_; -} - -void FieldTrial::FinalizeGroupChoice() { - FinalizeGroupChoiceImpl(false); -} - -void FieldTrial::FinalizeGroupChoiceImpl(bool is_locked) { - if (group_ != kNotFinalized) - return; - accumulated_group_probability_ = divisor_; - // Here it's OK to use |kDefaultGroupNumber| since we can't be forced and not - // finalized. - DCHECK(!forced_); - SetGroupChoice(default_group_name_, kDefaultGroupNumber); - - // Add the field trial to shared memory. - if (kUseSharedMemoryForFieldTrials && trial_registered_) - FieldTrialList::OnGroupFinalized(is_locked, this); -} - -bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { - if (!group_reported_ || !enable_field_trial_) - return false; - DCHECK_NE(group_, kNotFinalized); - active_group->trial_name = trial_name_; - active_group->group_name = group_name_; - return true; -} - -bool FieldTrial::GetStateWhileLocked(State* field_trial_state, - bool include_expired) { - if (!include_expired && !enable_field_trial_) - return false; - FinalizeGroupChoiceImpl(true); - field_trial_state->trial_name = &trial_name_; - field_trial_state->group_name = &group_name_; - field_trial_state->activated = group_reported_; - return true; -} - -//------------------------------------------------------------------------------ -// FieldTrialList methods and members. - -// static -FieldTrialList* FieldTrialList::global_ = nullptr; - -// static -bool FieldTrialList::used_without_global_ = false; - -FieldTrialList::Observer::~Observer() = default; - -FieldTrialList::FieldTrialList( - std::unique_ptr entropy_provider) - : entropy_provider_(std::move(entropy_provider)), - observer_list_(new ObserverListThreadSafe( - ObserverListPolicy::EXISTING_ONLY)) { - DCHECK(!global_); - DCHECK(!used_without_global_); - global_ = this; - - Time two_years_from_build_time = GetBuildTime() + TimeDelta::FromDays(730); - Time::Exploded exploded; - two_years_from_build_time.LocalExplode(&exploded); - kNoExpirationYear = exploded.year; -} - -FieldTrialList::~FieldTrialList() { - AutoLock auto_lock(lock_); - while (!registered_.empty()) { - RegistrationMap::iterator it = registered_.begin(); - it->second->Release(); - registered_.erase(it->first); - } - DCHECK_EQ(this, global_); - global_ = nullptr; -} - -// static -FieldTrial* FieldTrialList::FactoryGetFieldTrial( - const std::string& trial_name, - FieldTrial::Probability total_probability, - const std::string& default_group_name, - const int year, - const int month, - const int day_of_month, - FieldTrial::RandomizationType randomization_type, - int* default_group_number) { - return FactoryGetFieldTrialWithRandomizationSeed( - trial_name, total_probability, default_group_name, year, month, - day_of_month, randomization_type, 0, default_group_number, nullptr); -} - -// static -FieldTrial* FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed( - const std::string& trial_name, - FieldTrial::Probability total_probability, - const std::string& default_group_name, - const int year, - const int month, - const int day_of_month, - FieldTrial::RandomizationType randomization_type, - uint32_t randomization_seed, - int* default_group_number, - const FieldTrial::EntropyProvider* override_entropy_provider) { - if (default_group_number) - *default_group_number = FieldTrial::kDefaultGroupNumber; - // Check if the field trial has already been created in some other way. - FieldTrial* existing_trial = Find(trial_name); - if (existing_trial) { - CHECK(existing_trial->forced_); - // If the default group name differs between the existing forced trial - // and this trial, then use a different value for the default group number. - if (default_group_number && - default_group_name != existing_trial->default_group_name()) { - // If the new default group number corresponds to the group that was - // chosen for the forced trial (which has been finalized when it was - // forced), then set the default group number to that. - if (default_group_name == existing_trial->group_name_internal()) { - *default_group_number = existing_trial->group_; - } else { - // Otherwise, use |kNonConflictingGroupNumber| (-2) for the default - // group number, so that it does not conflict with the |AppendGroup()| - // result for the chosen group. - const int kNonConflictingGroupNumber = -2; - static_assert( - kNonConflictingGroupNumber != FieldTrial::kDefaultGroupNumber, - "The 'non-conflicting' group number conflicts"); - static_assert(kNonConflictingGroupNumber != FieldTrial::kNotFinalized, - "The 'non-conflicting' group number conflicts"); - *default_group_number = kNonConflictingGroupNumber; - } - } - return existing_trial; - } - - double entropy_value; - if (randomization_type == FieldTrial::ONE_TIME_RANDOMIZED) { - // If an override entropy provider is given, use it. - const FieldTrial::EntropyProvider* entropy_provider = - override_entropy_provider ? override_entropy_provider - : GetEntropyProviderForOneTimeRandomization(); - CHECK(entropy_provider); - entropy_value = entropy_provider->GetEntropyForTrial(trial_name, - randomization_seed); - } else { - DCHECK_EQ(FieldTrial::SESSION_RANDOMIZED, randomization_type); - DCHECK_EQ(0U, randomization_seed); - entropy_value = RandDouble(); - } - - FieldTrial* field_trial = new FieldTrial(trial_name, total_probability, - default_group_name, entropy_value); - if (GetBuildTime() > CreateTimeFromParams(year, month, day_of_month)) - field_trial->Disable(); - FieldTrialList::Register(field_trial); - return field_trial; -} - -// static -FieldTrial* FieldTrialList::Find(const std::string& trial_name) { - if (!global_) - return nullptr; - AutoLock auto_lock(global_->lock_); - return global_->PreLockedFind(trial_name); -} - -// static -int FieldTrialList::FindValue(const std::string& trial_name) { - FieldTrial* field_trial = Find(trial_name); - if (field_trial) - return field_trial->group(); - return FieldTrial::kNotFinalized; -} - -// static -std::string FieldTrialList::FindFullName(const std::string& trial_name) { - FieldTrial* field_trial = Find(trial_name); - if (field_trial) - return field_trial->group_name(); - return std::string(); -} - -// static -bool FieldTrialList::TrialExists(const std::string& trial_name) { - return Find(trial_name) != nullptr; -} - -// static -bool FieldTrialList::IsTrialActive(const std::string& trial_name) { - FieldTrial* field_trial = Find(trial_name); - FieldTrial::ActiveGroup active_group; - return field_trial && field_trial->GetActiveGroup(&active_group); -} - -// static -void FieldTrialList::StatesToString(std::string* output) { - FieldTrial::ActiveGroups active_groups; - GetActiveFieldTrialGroups(&active_groups); - for (FieldTrial::ActiveGroups::const_iterator it = active_groups.begin(); - it != active_groups.end(); ++it) { - DCHECK_EQ(std::string::npos, - it->trial_name.find(kPersistentStringSeparator)); - DCHECK_EQ(std::string::npos, - it->group_name.find(kPersistentStringSeparator)); - output->append(it->trial_name); - output->append(1, kPersistentStringSeparator); - output->append(it->group_name); - output->append(1, kPersistentStringSeparator); - } -} - -// static -void FieldTrialList::AllStatesToString(std::string* output, - bool include_expired) { - if (!global_) - return; - AutoLock auto_lock(global_->lock_); - - for (const auto& registered : global_->registered_) { - FieldTrial::State trial; - if (!registered.second->GetStateWhileLocked(&trial, include_expired)) - continue; - DCHECK_EQ(std::string::npos, - trial.trial_name->find(kPersistentStringSeparator)); - DCHECK_EQ(std::string::npos, - trial.group_name->find(kPersistentStringSeparator)); - if (trial.activated) - output->append(1, kActivationMarker); - output->append(*trial.trial_name); - output->append(1, kPersistentStringSeparator); - output->append(*trial.group_name); - output->append(1, kPersistentStringSeparator); - } -} - -// static -std::string FieldTrialList::AllParamsToString(bool include_expired, - EscapeDataFunc encode_data_func) { - FieldTrialParamAssociator* params_associator = - FieldTrialParamAssociator::GetInstance(); - std::string output; - for (const auto& registered : GetRegisteredTrials()) { - FieldTrial::State trial; - if (!registered.second->GetStateWhileLocked(&trial, include_expired)) - continue; - DCHECK_EQ(std::string::npos, - trial.trial_name->find(kPersistentStringSeparator)); - DCHECK_EQ(std::string::npos, - trial.group_name->find(kPersistentStringSeparator)); - std::map params; - if (params_associator->GetFieldTrialParamsWithoutFallback( - *trial.trial_name, *trial.group_name, ¶ms)) { - if (params.size() > 0) { - // Add comma to seprate from previous entry if it exists. - if (!output.empty()) - output.append(1, ','); - - output.append(encode_data_func(*trial.trial_name)); - output.append(1, '.'); - output.append(encode_data_func(*trial.group_name)); - output.append(1, ':'); - - std::string param_str; - for (const auto& param : params) { - // Add separator from previous param information if it exists. - if (!param_str.empty()) - param_str.append(1, kPersistentStringSeparator); - param_str.append(encode_data_func(param.first)); - param_str.append(1, kPersistentStringSeparator); - param_str.append(encode_data_func(param.second)); - } - - output.append(param_str); - } - } - } - return output; -} - -// static -void FieldTrialList::GetActiveFieldTrialGroups( - FieldTrial::ActiveGroups* active_groups) { - DCHECK(active_groups->empty()); - if (!global_) - return; - AutoLock auto_lock(global_->lock_); - - for (RegistrationMap::iterator it = global_->registered_.begin(); - it != global_->registered_.end(); ++it) { - FieldTrial::ActiveGroup active_group; - if (it->second->GetActiveGroup(&active_group)) - active_groups->push_back(active_group); - } -} - -// static -void FieldTrialList::GetActiveFieldTrialGroupsFromString( - const std::string& trials_string, - FieldTrial::ActiveGroups* active_groups) { - std::vector entries; - if (!ParseFieldTrialsString(trials_string, &entries)) - return; - - for (const auto& entry : entries) { - if (entry.activated) { - FieldTrial::ActiveGroup group; - group.trial_name = entry.trial_name.as_string(); - group.group_name = entry.group_name.as_string(); - active_groups->push_back(group); - } - } -} - -// static -void FieldTrialList::GetInitiallyActiveFieldTrials( - const base::CommandLine& command_line, - FieldTrial::ActiveGroups* active_groups) { - DCHECK(global_); - DCHECK(global_->create_trials_from_command_line_called_); - - if (!global_->field_trial_allocator_) { - GetActiveFieldTrialGroupsFromString( - command_line.GetSwitchValueASCII(switches::kForceFieldTrials), - active_groups); - return; - } - - FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); - FieldTrialAllocator::Iterator mem_iter(allocator); - const FieldTrial::FieldTrialEntry* entry; - while ((entry = mem_iter.GetNextOfObject()) != - nullptr) { - StringPiece trial_name; - StringPiece group_name; - if (subtle::NoBarrier_Load(&entry->activated) && - entry->GetTrialAndGroupName(&trial_name, &group_name)) { - FieldTrial::ActiveGroup group; - group.trial_name = trial_name.as_string(); - group.group_name = group_name.as_string(); - active_groups->push_back(group); - } - } -} - -// static -bool FieldTrialList::CreateTrialsFromString( - const std::string& trials_string, - const std::set& ignored_trial_names) { - DCHECK(global_); - if (trials_string.empty() || !global_) - return true; - - std::vector entries; - if (!ParseFieldTrialsString(trials_string, &entries)) - return false; - - for (const auto& entry : entries) { - const std::string trial_name = entry.trial_name.as_string(); - const std::string group_name = entry.group_name.as_string(); - - if (ContainsKey(ignored_trial_names, trial_name)) { - // This is to warn that the field trial forced through command-line - // input is unforcable. - // Use --enable-logging or --enable-logging=stderr to see this warning. - LOG(WARNING) << "Field trial: " << trial_name << " cannot be forced."; - continue; - } - - FieldTrial* trial = CreateFieldTrial(trial_name, group_name); - if (!trial) - return false; - if (entry.activated) { - // Call |group()| to mark the trial as "used" and notify observers, if - // any. This is useful to ensure that field trials created in child - // processes are properly reported in crash reports. - trial->group(); - } - } - return true; -} - -// static -void FieldTrialList::CreateTrialsFromCommandLine( - const CommandLine& cmd_line, - const char* field_trial_handle_switch, - int fd_key) { - global_->create_trials_from_command_line_called_ = true; - -#if defined(OS_WIN) || defined(OS_FUCHSIA) - if (cmd_line.HasSwitch(field_trial_handle_switch)) { - std::string switch_value = - cmd_line.GetSwitchValueASCII(field_trial_handle_switch); - bool result = CreateTrialsFromSwitchValue(switch_value); - DCHECK(result); - } -#elif defined(OS_POSIX) && !defined(OS_NACL) - // On POSIX, we check if the handle is valid by seeing if the browser process - // sent over the switch (we don't care about the value). Invalid handles - // occur in some browser tests which don't initialize the allocator. - if (cmd_line.HasSwitch(field_trial_handle_switch)) { - std::string switch_value = - cmd_line.GetSwitchValueASCII(field_trial_handle_switch); - bool result = CreateTrialsFromDescriptor(fd_key, switch_value); - DCHECK(result); - } -#endif - - if (cmd_line.HasSwitch(switches::kForceFieldTrials)) { - bool result = FieldTrialList::CreateTrialsFromString( - cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials), - std::set()); - DCHECK(result); - } -} - -// static -void FieldTrialList::CreateFeaturesFromCommandLine( - const base::CommandLine& command_line, - const char* enable_features_switch, - const char* disable_features_switch, - FeatureList* feature_list) { - // Fallback to command line if not using shared memory. - if (!kUseSharedMemoryForFieldTrials || - !global_->field_trial_allocator_.get()) { - return feature_list->InitializeFromCommandLine( - command_line.GetSwitchValueASCII(enable_features_switch), - command_line.GetSwitchValueASCII(disable_features_switch)); - } - - feature_list->InitializeFromSharedMemory( - global_->field_trial_allocator_.get()); -} - -#if defined(OS_WIN) -// static -void FieldTrialList::AppendFieldTrialHandleIfNeeded( - HandlesToInheritVector* handles) { - if (!global_) - return; - if (kUseSharedMemoryForFieldTrials) { - InstantiateFieldTrialAllocatorIfNeeded(); - if (global_->readonly_allocator_handle_.IsValid()) - handles->push_back(global_->readonly_allocator_handle_.GetHandle()); - } -} -#elif defined(OS_FUCHSIA) -// TODO(fuchsia): Implement shared-memory configuration (crbug.com/752368). -#elif defined(OS_POSIX) && !defined(OS_NACL) -// static -SharedMemoryHandle FieldTrialList::GetFieldTrialHandle() { - if (global_ && kUseSharedMemoryForFieldTrials) { - InstantiateFieldTrialAllocatorIfNeeded(); - // We check for an invalid handle where this gets called. - return global_->readonly_allocator_handle_; - } - return SharedMemoryHandle(); -} -#endif - -// static -void FieldTrialList::CopyFieldTrialStateToFlags( - const char* field_trial_handle_switch, - const char* enable_features_switch, - const char* disable_features_switch, - CommandLine* cmd_line) { - // TODO(lawrencewu): Ideally, having the global would be guaranteed. However, - // content browser tests currently don't create a FieldTrialList because they - // don't run ChromeBrowserMainParts code where it's done for Chrome. - // Some tests depend on the enable and disable features flag switch, though, - // so we can still add those even though AllStatesToString() will be a no-op. - if (!global_) { - AddFeatureAndFieldTrialFlags(enable_features_switch, - disable_features_switch, cmd_line); - return; - } - - // Use shared memory to pass the state if the feature is enabled, otherwise - // fallback to passing it via the command line as a string. - if (kUseSharedMemoryForFieldTrials) { - InstantiateFieldTrialAllocatorIfNeeded(); - // If the readonly handle didn't get duplicated properly, then fallback to - // original behavior. - if (!global_->readonly_allocator_handle_.IsValid()) { - AddFeatureAndFieldTrialFlags(enable_features_switch, - disable_features_switch, cmd_line); - return; - } - - global_->field_trial_allocator_->UpdateTrackingHistograms(); - std::string switch_value = SerializeSharedMemoryHandleMetadata( - global_->readonly_allocator_handle_); - cmd_line->AppendSwitchASCII(field_trial_handle_switch, switch_value); - - // Append --enable-features and --disable-features switches corresponding - // to the features enabled on the command-line, so that child and browser - // process command lines match and clearly show what has been specified - // explicitly by the user. - std::string enabled_features; - std::string disabled_features; - FeatureList::GetInstance()->GetCommandLineFeatureOverrides( - &enabled_features, &disabled_features); - - if (!enabled_features.empty()) - cmd_line->AppendSwitchASCII(enable_features_switch, enabled_features); - if (!disabled_features.empty()) - cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features); - - return; - } - - AddFeatureAndFieldTrialFlags(enable_features_switch, disable_features_switch, - cmd_line); -} - -// static -FieldTrial* FieldTrialList::CreateFieldTrial( - const std::string& name, - const std::string& group_name) { - DCHECK(global_); - DCHECK_GE(name.size(), 0u); - DCHECK_GE(group_name.size(), 0u); - if (name.empty() || group_name.empty() || !global_) - return nullptr; - - FieldTrial* field_trial = FieldTrialList::Find(name); - if (field_trial) { - // In single process mode, or when we force them from the command line, - // we may have already created the field trial. - if (field_trial->group_name_internal() != group_name) - return nullptr; - return field_trial; - } - const int kTotalProbability = 100; - field_trial = new FieldTrial(name, kTotalProbability, group_name, 0); - FieldTrialList::Register(field_trial); - // Force the trial, which will also finalize the group choice. - field_trial->SetForced(); - return field_trial; -} - -// static -bool FieldTrialList::AddObserver(Observer* observer) { - if (!global_) - return false; - global_->observer_list_->AddObserver(observer); - return true; -} - -// static -void FieldTrialList::RemoveObserver(Observer* observer) { - if (!global_) - return; - global_->observer_list_->RemoveObserver(observer); -} - -// static -void FieldTrialList::SetSynchronousObserver(Observer* observer) { - DCHECK(!global_->synchronous_observer_); - global_->synchronous_observer_ = observer; -} - -// static -void FieldTrialList::RemoveSynchronousObserver(Observer* observer) { - DCHECK_EQ(global_->synchronous_observer_, observer); - global_->synchronous_observer_ = nullptr; -} - -// static -void FieldTrialList::OnGroupFinalized(bool is_locked, FieldTrial* field_trial) { - if (!global_) - return; - if (is_locked) { - AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), - field_trial); - } else { - AutoLock auto_lock(global_->lock_); - AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), - field_trial); - } -} - -// static -void FieldTrialList::NotifyFieldTrialGroupSelection(FieldTrial* field_trial) { - if (!global_) - return; - - { - AutoLock auto_lock(global_->lock_); - if (field_trial->group_reported_) - return; - field_trial->group_reported_ = true; - - if (!field_trial->enable_field_trial_) - return; - - if (kUseSharedMemoryForFieldTrials) - ActivateFieldTrialEntryWhileLocked(field_trial); - } - - // Recording for stability debugging has to be done inline as a task posted - // to an observer may not get executed before a crash. - base::debug::GlobalActivityTracker* tracker = - base::debug::GlobalActivityTracker::Get(); - if (tracker) { - tracker->RecordFieldTrial(field_trial->trial_name(), - field_trial->group_name_internal()); - } - - if (global_->synchronous_observer_) { - global_->synchronous_observer_->OnFieldTrialGroupFinalized( - field_trial->trial_name(), field_trial->group_name_internal()); - } - - global_->observer_list_->Notify( - FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized, - field_trial->trial_name(), field_trial->group_name_internal()); -} - -// static -size_t FieldTrialList::GetFieldTrialCount() { - if (!global_) - return 0; - AutoLock auto_lock(global_->lock_); - return global_->registered_.size(); -} - -// static -bool FieldTrialList::GetParamsFromSharedMemory( - FieldTrial* field_trial, - std::map* params) { - DCHECK(global_); - // If the field trial allocator is not set up yet, then there are several - // cases: - // - We are in the browser process and the allocator has not been set up - // yet. If we got here, then we couldn't find the params in - // FieldTrialParamAssociator, so it's definitely not here. Return false. - // - Using shared memory for field trials is not enabled. If we got here, - // then there's nothing in shared memory. Return false. - // - We are in the child process and the allocator has not been set up yet. - // If this is the case, then you are calling this too early. The field trial - // allocator should get set up very early in the lifecycle. Try to see if - // you can call it after it's been set up. - AutoLock auto_lock(global_->lock_); - if (!global_->field_trial_allocator_) - return false; - - // If ref_ isn't set, then the field trial data can't be in shared memory. - if (!field_trial->ref_) - return false; - - const FieldTrial::FieldTrialEntry* entry = - global_->field_trial_allocator_->GetAsObject( - field_trial->ref_); - - size_t allocated_size = - global_->field_trial_allocator_->GetAllocSize(field_trial->ref_); - size_t actual_size = sizeof(FieldTrial::FieldTrialEntry) + entry->pickle_size; - if (allocated_size < actual_size) - return false; - - return entry->GetParams(params); -} - -// static -void FieldTrialList::ClearParamsFromSharedMemoryForTesting() { - if (!global_) - return; - - AutoLock auto_lock(global_->lock_); - if (!global_->field_trial_allocator_) - return; - - // To clear the params, we iterate through every item in the allocator, copy - // just the trial and group name into a newly-allocated segment and then clear - // the existing item. - FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); - FieldTrialAllocator::Iterator mem_iter(allocator); - - // List of refs to eventually be made iterable. We can't make it in the loop, - // since it would go on forever. - std::vector new_refs; - - FieldTrial::FieldTrialRef prev_ref; - while ((prev_ref = mem_iter.GetNextOfType()) != - FieldTrialAllocator::kReferenceNull) { - // Get the existing field trial entry in shared memory. - const FieldTrial::FieldTrialEntry* prev_entry = - allocator->GetAsObject(prev_ref); - StringPiece trial_name; - StringPiece group_name; - if (!prev_entry->GetTrialAndGroupName(&trial_name, &group_name)) - continue; - - // Write a new entry, minus the params. - Pickle pickle; - pickle.WriteString(trial_name); - pickle.WriteString(group_name); - size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); - FieldTrial::FieldTrialEntry* new_entry = - allocator->New(total_size); - subtle::NoBarrier_Store(&new_entry->activated, - subtle::NoBarrier_Load(&prev_entry->activated)); - new_entry->pickle_size = pickle.size(); - - // TODO(lawrencewu): Modify base::Pickle to be able to write over a section - // in memory, so we can avoid this memcpy. - char* dst = reinterpret_cast(new_entry) + - sizeof(FieldTrial::FieldTrialEntry); - memcpy(dst, pickle.data(), pickle.size()); - - // Update the ref on the field trial and add it to the list to be made - // iterable. - FieldTrial::FieldTrialRef new_ref = allocator->GetAsReference(new_entry); - FieldTrial* trial = global_->PreLockedFind(trial_name.as_string()); - trial->ref_ = new_ref; - new_refs.push_back(new_ref); - - // Mark the existing entry as unused. - allocator->ChangeType(prev_ref, 0, - FieldTrial::FieldTrialEntry::kPersistentTypeId, - /*clear=*/false); - } - - for (const auto& ref : new_refs) { - allocator->MakeIterable(ref); - } -} - -// static -void FieldTrialList::DumpAllFieldTrialsToPersistentAllocator( - PersistentMemoryAllocator* allocator) { - if (!global_) - return; - AutoLock auto_lock(global_->lock_); - for (const auto& registered : global_->registered_) { - AddToAllocatorWhileLocked(allocator, registered.second); - } -} - -// static -std::vector -FieldTrialList::GetAllFieldTrialsFromPersistentAllocator( - PersistentMemoryAllocator const& allocator) { - std::vector entries; - FieldTrialAllocator::Iterator iter(&allocator); - const FieldTrial::FieldTrialEntry* entry; - while ((entry = iter.GetNextOfObject()) != - nullptr) { - entries.push_back(entry); - } - return entries; -} - -// static -bool FieldTrialList::IsGlobalSetForTesting() { - return global_ != nullptr; -} - -// static -std::string FieldTrialList::SerializeSharedMemoryHandleMetadata( - const SharedMemoryHandle& shm) { - std::stringstream ss; -#if defined(OS_WIN) - // Tell the child process the name of the inherited HANDLE. - uintptr_t uintptr_handle = reinterpret_cast(shm.GetHandle()); - ss << uintptr_handle << ","; -#elif defined(OS_FUCHSIA) - ss << shm.GetHandle() << ","; -#elif !defined(OS_POSIX) -#error Unsupported OS -#endif - - base::UnguessableToken guid = shm.GetGUID(); - ss << guid.GetHighForSerialization() << "," << guid.GetLowForSerialization(); - ss << "," << shm.GetSize(); - return ss.str(); -} - -#if defined(OS_WIN) || defined(OS_FUCHSIA) - -// static -SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata( - const std::string& switch_value) { - std::vector tokens = base::SplitStringPiece( - switch_value, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); - - if (tokens.size() != 4) - return SharedMemoryHandle(); - - int field_trial_handle = 0; - if (!base::StringToInt(tokens[0], &field_trial_handle)) - return SharedMemoryHandle(); -#if defined(OS_FUCHSIA) - zx_handle_t handle = static_cast(field_trial_handle); -#elif defined(OS_WIN) - HANDLE handle = reinterpret_cast(field_trial_handle); - if (base::IsCurrentProcessElevated()) { - // base::LaunchElevatedProcess doesn't have a way to duplicate the handle, - // but this process can since by definition it's not sandboxed. - base::ProcessId parent_pid = base::GetParentProcessId(GetCurrentProcess()); - HANDLE parent_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, parent_pid); - DuplicateHandle(parent_handle, handle, GetCurrentProcess(), &handle, 0, - FALSE, DUPLICATE_SAME_ACCESS); - CloseHandle(parent_handle); - } -#endif // defined(OS_WIN) - - base::UnguessableToken guid; - if (!DeserializeGUIDFromStringPieces(tokens[1], tokens[2], &guid)) - return SharedMemoryHandle(); - - int size; - if (!base::StringToInt(tokens[3], &size)) - return SharedMemoryHandle(); - - return SharedMemoryHandle(handle, static_cast(size), guid); -} - -#elif defined(OS_POSIX) && !defined(OS_NACL) - -// static -SharedMemoryHandle FieldTrialList::DeserializeSharedMemoryHandleMetadata( - int fd, - const std::string& switch_value) { - std::vector tokens = base::SplitStringPiece( - switch_value, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); - - if (tokens.size() != 3) - return SharedMemoryHandle(); - - base::UnguessableToken guid; - if (!DeserializeGUIDFromStringPieces(tokens[0], tokens[1], &guid)) - return SharedMemoryHandle(); - - int size; - if (!base::StringToInt(tokens[2], &size)) - return SharedMemoryHandle(); - - return SharedMemoryHandle(FileDescriptor(fd, true), static_cast(size), - guid); -} - -#endif - -#if defined(OS_WIN) || defined(OS_FUCHSIA) -// static -bool FieldTrialList::CreateTrialsFromSwitchValue( - const std::string& switch_value) { - SharedMemoryHandle shm = DeserializeSharedMemoryHandleMetadata(switch_value); - if (!shm.IsValid()) - return false; - return FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm); -} -#elif defined(OS_POSIX) && !defined(OS_NACL) -// static -bool FieldTrialList::CreateTrialsFromDescriptor( - int fd_key, - const std::string& switch_value) { - if (!kUseSharedMemoryForFieldTrials) - return false; - - if (fd_key == -1) - return false; - - int fd = GlobalDescriptors::GetInstance()->MaybeGet(fd_key); - if (fd == -1) - return false; - - SharedMemoryHandle shm = - DeserializeSharedMemoryHandleMetadata(fd, switch_value); - if (!shm.IsValid()) - return false; - - bool result = FieldTrialList::CreateTrialsFromSharedMemoryHandle(shm); - DCHECK(result); - return true; -} -#endif // defined(OS_POSIX) && !defined(OS_NACL) - -// static -bool FieldTrialList::CreateTrialsFromSharedMemoryHandle( - SharedMemoryHandle shm_handle) { - // shm gets deleted when it gets out of scope, but that's OK because we need - // it only for the duration of this method. - std::unique_ptr shm(new SharedMemory(shm_handle, true)); - if (!shm.get()->Map(kFieldTrialAllocationSize)) - OnOutOfMemory(kFieldTrialAllocationSize); - - return FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); -} - -// static -bool FieldTrialList::CreateTrialsFromSharedMemory( - std::unique_ptr shm) { - global_->field_trial_allocator_.reset( - new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, true)); - FieldTrialAllocator* shalloc = global_->field_trial_allocator_.get(); - FieldTrialAllocator::Iterator mem_iter(shalloc); - - const FieldTrial::FieldTrialEntry* entry; - while ((entry = mem_iter.GetNextOfObject()) != - nullptr) { - StringPiece trial_name; - StringPiece group_name; - if (!entry->GetTrialAndGroupName(&trial_name, &group_name)) - return false; - - // TODO(lawrencewu): Convert the API for CreateFieldTrial to take - // StringPieces. - FieldTrial* trial = - CreateFieldTrial(trial_name.as_string(), group_name.as_string()); - - trial->ref_ = mem_iter.GetAsReference(entry); - if (subtle::NoBarrier_Load(&entry->activated)) { - // Call |group()| to mark the trial as "used" and notify observers, if - // any. This is useful to ensure that field trials created in child - // processes are properly reported in crash reports. - trial->group(); - } - } - return true; -} - -// static -void FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded() { - if (!global_) - return; - AutoLock auto_lock(global_->lock_); - // Create the allocator if not already created and add all existing trials. - if (global_->field_trial_allocator_ != nullptr) - return; - - SharedMemoryCreateOptions options; - options.size = kFieldTrialAllocationSize; - options.share_read_only = true; -#if defined(OS_MACOSX) && !defined(OS_IOS) - options.type = SharedMemoryHandle::POSIX; -#endif - - std::unique_ptr shm(new SharedMemory()); - if (!shm->Create(options)) - OnOutOfMemory(kFieldTrialAllocationSize); - - if (!shm->Map(kFieldTrialAllocationSize)) - OnOutOfMemory(kFieldTrialAllocationSize); - - global_->field_trial_allocator_.reset( - new FieldTrialAllocator(std::move(shm), 0, kAllocatorName, false)); - global_->field_trial_allocator_->CreateTrackingHistograms(kAllocatorName); - - // Add all existing field trials. - for (const auto& registered : global_->registered_) { - AddToAllocatorWhileLocked(global_->field_trial_allocator_.get(), - registered.second); - } - - // Add all existing features. - FeatureList::GetInstance()->AddFeaturesToAllocator( - global_->field_trial_allocator_.get()); - -#if !defined(OS_NACL) - global_->readonly_allocator_handle_ = GetSharedMemoryReadOnlyHandle( - global_->field_trial_allocator_->shared_memory()); -#endif -} - -// static -void FieldTrialList::AddToAllocatorWhileLocked( - PersistentMemoryAllocator* allocator, - FieldTrial* field_trial) { - // Don't do anything if the allocator hasn't been instantiated yet. - if (allocator == nullptr) - return; - - // Or if the allocator is read only, which means we are in a child process and - // shouldn't be writing to it. - if (allocator->IsReadonly()) - return; - - FieldTrial::State trial_state; - if (!field_trial->GetStateWhileLocked(&trial_state, false)) - return; - - // Or if we've already added it. We must check after GetState since it can - // also add to the allocator. - if (field_trial->ref_) - return; - - Pickle pickle; - PickleFieldTrial(trial_state, &pickle); - - size_t total_size = sizeof(FieldTrial::FieldTrialEntry) + pickle.size(); - FieldTrial::FieldTrialRef ref = allocator->Allocate( - total_size, FieldTrial::FieldTrialEntry::kPersistentTypeId); - if (ref == FieldTrialAllocator::kReferenceNull) { - NOTREACHED(); - return; - } - - FieldTrial::FieldTrialEntry* entry = - allocator->GetAsObject(ref); - subtle::NoBarrier_Store(&entry->activated, trial_state.activated); - entry->pickle_size = pickle.size(); - - // TODO(lawrencewu): Modify base::Pickle to be able to write over a section in - // memory, so we can avoid this memcpy. - char* dst = - reinterpret_cast(entry) + sizeof(FieldTrial::FieldTrialEntry); - memcpy(dst, pickle.data(), pickle.size()); - - allocator->MakeIterable(ref); - field_trial->ref_ = ref; -} - -// static -void FieldTrialList::ActivateFieldTrialEntryWhileLocked( - FieldTrial* field_trial) { - FieldTrialAllocator* allocator = global_->field_trial_allocator_.get(); - - // Check if we're in the child process and return early if so. - if (!allocator || allocator->IsReadonly()) - return; - - FieldTrial::FieldTrialRef ref = field_trial->ref_; - if (ref == FieldTrialAllocator::kReferenceNull) { - // It's fine to do this even if the allocator hasn't been instantiated - // yet -- it'll just return early. - AddToAllocatorWhileLocked(allocator, field_trial); - } else { - // It's also okay to do this even though the callee doesn't have a lock -- - // the only thing that happens on a stale read here is a slight performance - // hit from the child re-synchronizing activation state. - FieldTrial::FieldTrialEntry* entry = - allocator->GetAsObject(ref); - subtle::NoBarrier_Store(&entry->activated, 1); - } -} - -// static -const FieldTrial::EntropyProvider* - FieldTrialList::GetEntropyProviderForOneTimeRandomization() { - if (!global_) { - used_without_global_ = true; - return nullptr; - } - - return global_->entropy_provider_.get(); -} - -FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { - RegistrationMap::iterator it = registered_.find(name); - if (registered_.end() == it) - return nullptr; - return it->second; -} - -// static -void FieldTrialList::Register(FieldTrial* trial) { - if (!global_) { - used_without_global_ = true; - return; - } - AutoLock auto_lock(global_->lock_); - CHECK(!global_->PreLockedFind(trial->trial_name())) << trial->trial_name(); - trial->AddRef(); - trial->SetTrialRegistered(); - global_->registered_[trial->trial_name()] = trial; -} - -// static -FieldTrialList::RegistrationMap FieldTrialList::GetRegisteredTrials() { - RegistrationMap output; - if (global_) { - AutoLock auto_lock(global_->lock_); - output = global_->registered_; - } - return output; -} - -} // namespace base diff --git a/metrics/field_trial.h b/metrics/field_trial.h deleted file mode 100644 index ac4ea1c04..000000000 --- a/metrics/field_trial.h +++ /dev/null @@ -1,802 +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. - -// FieldTrial is a class for handling details of statistical experiments -// performed by actual users in the field (i.e., in a shipped or beta product). -// All code is called exclusively on the UI thread currently. -// -// The simplest example is an experiment to see whether one of two options -// produces "better" results across our user population. In that scenario, UMA -// data is uploaded to aggregate the test results, and this FieldTrial class -// manages the state of each such experiment (state == which option was -// pseudo-randomly selected). -// -// States are typically generated randomly, either based on a one time -// randomization (which will yield the same results, in terms of selecting -// the client for a field trial or not, for every run of the program on a -// given machine), or by a session randomization (generated each time the -// application starts up, but held constant during the duration of the -// process). - -//------------------------------------------------------------------------------ -// Example: Suppose we have an experiment involving memory, such as determining -// the impact of some pruning algorithm. -// We assume that we already have a histogram of memory usage, such as: - -// UMA_HISTOGRAM_COUNTS("Memory.RendererTotal", count); - -// Somewhere in main thread initialization code, we'd probably define an -// instance of a FieldTrial, with code such as: - -// // FieldTrials are reference counted, and persist automagically until -// // process teardown, courtesy of their automatic registration in -// // FieldTrialList. -// // Note: This field trial will run in Chrome instances compiled through -// // 8 July, 2015, and after that all instances will be in "StandardMem". -// scoped_refptr trial( -// base::FieldTrialList::FactoryGetFieldTrial( -// "MemoryExperiment", 1000, "StandardMem", 2015, 7, 8, -// base::FieldTrial::ONE_TIME_RANDOMIZED, NULL)); -// -// const int high_mem_group = -// trial->AppendGroup("HighMem", 20); // 2% in HighMem group. -// const int low_mem_group = -// trial->AppendGroup("LowMem", 20); // 2% in LowMem group. -// // Take action depending of which group we randomly land in. -// if (trial->group() == high_mem_group) -// SetPruningAlgorithm(kType1); // Sample setting of browser state. -// else if (trial->group() == low_mem_group) -// SetPruningAlgorithm(kType2); // Sample alternate setting. - -//------------------------------------------------------------------------------ - -#ifndef BASE_METRICS_FIELD_TRIAL_H_ -#define BASE_METRICS_FIELD_TRIAL_H_ - -#include -#include - -#include -#include -#include -#include -#include - -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/command_line.h" -#include "base/feature_list.h" -#include "base/files/file.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/shared_memory.h" -#include "base/memory/shared_memory_handle.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/observer_list_threadsafe.h" -#include "base/pickle.h" -#include "base/process/launch.h" -#include "base/strings/string_piece.h" -#include "base/synchronization/lock.h" -#include "base/time/time.h" -#include "build/build_config.h" - -namespace base { - -class FieldTrialList; - -class BASE_EXPORT FieldTrial : public RefCounted { - public: - typedef int Probability; // Probability type for being selected in a trial. - - // TODO(665129): Make private again after crash has been resolved. - typedef SharedPersistentMemoryAllocator::Reference FieldTrialRef; - - // Specifies the persistence of the field trial group choice. - enum RandomizationType { - // One time randomized trials will persist the group choice between - // restarts, which is recommended for most trials, especially those that - // change user visible behavior. - ONE_TIME_RANDOMIZED, - // Session randomized trials will roll the dice to select a group on every - // process restart. - SESSION_RANDOMIZED, - }; - - // EntropyProvider is an interface for providing entropy for one-time - // randomized (persistent) field trials. - class BASE_EXPORT EntropyProvider { - public: - virtual ~EntropyProvider(); - - // Returns a double in the range of [0, 1) to be used for the dice roll for - // the specified field trial. If |randomization_seed| is not 0, it will be - // used in preference to |trial_name| for generating the entropy by entropy - // providers that support it. A given instance should always return the same - // value given the same input |trial_name| and |randomization_seed| values. - virtual double GetEntropyForTrial(const std::string& trial_name, - uint32_t randomization_seed) const = 0; - }; - - // A pair representing a Field Trial and its selected group. - struct ActiveGroup { - std::string trial_name; - std::string group_name; - }; - - // A triplet representing a FieldTrial, its selected group and whether it's - // active. String members are pointers to the underlying strings owned by the - // FieldTrial object. Does not use StringPiece to avoid conversions back to - // std::string. - struct BASE_EXPORT State { - const std::string* trial_name = nullptr; - const std::string* group_name = nullptr; - bool activated = false; - - State(); - State(const State& other); - ~State(); - }; - - // We create one FieldTrialEntry per field trial in shared memory, via - // AddToAllocatorWhileLocked. The FieldTrialEntry is followed by a - // base::Pickle object that we unpickle and read from. - struct BASE_EXPORT FieldTrialEntry { - // SHA1(FieldTrialEntry): Increment this if structure changes! - static constexpr uint32_t kPersistentTypeId = 0xABA17E13 + 2; - - // Expected size for 32/64-bit check. - static constexpr size_t kExpectedInstanceSize = 8; - - // Whether or not this field trial is activated. This is really just a - // boolean but using a 32 bit value for portability reasons. It should be - // accessed via NoBarrier_Load()/NoBarrier_Store() to prevent the compiler - // from doing unexpected optimizations because it thinks that only one - // thread is accessing the memory location. - subtle::Atomic32 activated; - - // Size of the pickled structure, NOT the total size of this entry. - uint32_t pickle_size; - - // Calling this is only valid when the entry is initialized. That is, it - // resides in shared memory and has a pickle containing the trial name and - // group name following it. - bool GetTrialAndGroupName(StringPiece* trial_name, - StringPiece* group_name) const; - - // Calling this is only valid when the entry is initialized as well. Reads - // the parameters following the trial and group name and stores them as - // key-value mappings in |params|. - bool GetParams(std::map* params) const; - - private: - // Returns an iterator over the data containing names and params. - PickleIterator GetPickleIterator() const; - - // Takes the iterator and writes out the first two items into |trial_name| - // and |group_name|. - bool ReadStringPair(PickleIterator* iter, - StringPiece* trial_name, - StringPiece* group_name) const; - }; - - typedef std::vector ActiveGroups; - - // A return value to indicate that a given instance has not yet had a group - // assignment (and hence is not yet participating in the trial). - static const int kNotFinalized; - - // Disables this trial, meaning it always determines the default group - // has been selected. May be called immediately after construction, or - // at any time after initialization (should not be interleaved with - // AppendGroup calls). Once disabled, there is no way to re-enable a - // trial. - // TODO(mad): http://code.google.com/p/chromium/issues/detail?id=121446 - // This doesn't properly reset to Default when a group was forced. - void Disable(); - - // Establish the name and probability of the next group in this trial. - // Sometimes, based on construction randomization, this call may cause the - // provided group to be *THE* group selected for use in this instance. - // The return value is the group number of the new group. - int AppendGroup(const std::string& name, Probability group_probability); - - // Return the name of the FieldTrial (excluding the group name). - const std::string& trial_name() const { return trial_name_; } - - // Return the randomly selected group number that was assigned, and notify - // any/all observers that this finalized group number has presumably been used - // (queried), and will never change. Note that this will force an instance to - // participate, and make it illegal to attempt to probabilistically add any - // other groups to the trial. - int group(); - - // If the group's name is empty, a string version containing the group number - // is used as the group name. This causes a winner to be chosen if none was. - const std::string& group_name(); - - // Finalizes the group choice and returns the chosen group, but does not mark - // the trial as active - so its state will not be reported until group_name() - // or similar is called. - const std::string& GetGroupNameWithoutActivation(); - - // Set the field trial as forced, meaning that it was setup earlier than - // the hard coded registration of the field trial to override it. - // This allows the code that was hard coded to register the field trial to - // still succeed even though the field trial has already been registered. - // This must be called after appending all the groups, since we will make - // the group choice here. Note that this is a NOOP for already forced trials. - // And, as the rest of the FieldTrial code, this is not thread safe and must - // be done from the UI thread. - void SetForced(); - - // Enable benchmarking sets field trials to a common setting. - static void EnableBenchmarking(); - - // Creates a FieldTrial object with the specified parameters, to be used for - // simulation of group assignment without actually affecting global field - // trial state in the running process. Group assignment will be done based on - // |entropy_value|, which must have a range of [0, 1). - // - // Note: Using this function will not register the field trial globally in the - // running process - for that, use FieldTrialList::FactoryGetFieldTrial(). - // - // The ownership of the returned FieldTrial is transfered to the caller which - // is responsible for deref'ing it (e.g. by using scoped_refptr). - static FieldTrial* CreateSimulatedFieldTrial( - const std::string& trial_name, - Probability total_probability, - const std::string& default_group_name, - double entropy_value); - - private: - // Allow tests to access our innards for testing purposes. - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Registration); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AbsoluteProbabilities); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, RemainingProbability); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FiftyFiftyProbability); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, MiddleProbabilities); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, OneWinner); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DisableProbability); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroups); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, AllGroups); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroupsNotFinalized); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Save); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SaveAll); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DuplicateRestore); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOff); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOn); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_Default); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_NonDefault); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes); - FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DoesNotSurpassTotalProbability); - FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, - DoNotAddSimulatedFieldTrialsToAllocator); - FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory); - - friend class base::FieldTrialList; - - friend class RefCounted; - - // This is the group number of the 'default' group when a choice wasn't forced - // by a call to FieldTrialList::CreateFieldTrial. It is kept private so that - // consumers don't use it by mistake in cases where the group was forced. - static const int kDefaultGroupNumber; - - // Creates a field trial with the specified parameters. Group assignment will - // be done based on |entropy_value|, which must have a range of [0, 1). - FieldTrial(const std::string& trial_name, - Probability total_probability, - const std::string& default_group_name, - double entropy_value); - virtual ~FieldTrial(); - - // Return the default group name of the FieldTrial. - std::string default_group_name() const { return default_group_name_; } - - // Marks this trial as having been registered with the FieldTrialList. Must be - // called no more than once and before any |group()| calls have occurred. - void SetTrialRegistered(); - - // Sets the chosen group name and number. - void SetGroupChoice(const std::string& group_name, int number); - - // Ensures that a group is chosen, if it hasn't yet been. The field trial - // might yet be disabled, so this call will *not* notify observers of the - // status. - void FinalizeGroupChoice(); - - // Implements FinalizeGroupChoice() with the added flexibility of being - // deadlock-free if |is_locked| is true and the caller is holding a lock. - void FinalizeGroupChoiceImpl(bool is_locked); - - // Returns the trial name and selected group name for this field trial via - // the output parameter |active_group|, but only if the group has already - // been chosen and has been externally observed via |group()| and the trial - // has not been disabled. In that case, true is returned and |active_group| - // is filled in; otherwise, the result is false and |active_group| is left - // untouched. - bool GetActiveGroup(ActiveGroup* active_group) const; - - // Returns the trial name and selected group name for this field trial via - // the output parameter |field_trial_state| for all the studies when - // |bool include_expired| is true. In case when |bool include_expired| is - // false, if the trial has not been disabled true is returned and - // |field_trial_state| is filled in; otherwise, the result is false and - // |field_trial_state| is left untouched. - // This function is deadlock-free if the caller is holding a lock. - bool GetStateWhileLocked(State* field_trial_state, bool include_expired); - - // Returns the group_name. A winner need not have been chosen. - std::string group_name_internal() const { return group_name_; } - - // The name of the field trial, as can be found via the FieldTrialList. - const std::string trial_name_; - - // The maximum sum of all probabilities supplied, which corresponds to 100%. - // This is the scaling factor used to adjust supplied probabilities. - const Probability divisor_; - - // The name of the default group. - const std::string default_group_name_; - - // The randomly selected probability that is used to select a group (or have - // the instance not participate). It is the product of divisor_ and a random - // number between [0, 1). - Probability random_; - - // Sum of the probabilities of all appended groups. - Probability accumulated_group_probability_; - - // The number that will be returned by the next AppendGroup() call. - int next_group_number_; - - // The pseudo-randomly assigned group number. - // This is kNotFinalized if no group has been assigned. - int group_; - - // A textual name for the randomly selected group. Valid after |group()| - // has been called. - std::string group_name_; - - // When enable_field_trial_ is false, field trial reverts to the 'default' - // group. - bool enable_field_trial_; - - // When forced_ is true, we return the chosen group from AppendGroup when - // appropriate. - bool forced_; - - // Specifies whether the group choice has been reported to observers. - bool group_reported_; - - // Whether this trial is registered with the global FieldTrialList and thus - // should notify it when its group is queried. - bool trial_registered_; - - // Reference to related field trial struct and data in shared memory. - FieldTrialRef ref_; - - // When benchmarking is enabled, field trials all revert to the 'default' - // group. - static bool enable_benchmarking_; - - DISALLOW_COPY_AND_ASSIGN(FieldTrial); -}; - -//------------------------------------------------------------------------------ -// Class with a list of all active field trials. A trial is active if it has -// been registered, which includes evaluating its state based on its probaility. -// Only one instance of this class exists and outside of testing, will live for -// the entire life time of the process. -class BASE_EXPORT FieldTrialList { - public: - typedef SharedPersistentMemoryAllocator FieldTrialAllocator; - - // Type for function pointer passed to |AllParamsToString| used to escape - // special characters from |input|. - typedef std::string (*EscapeDataFunc)(const std::string& input); - - // Year that is guaranteed to not be expired when instantiating a field trial - // via |FactoryGetFieldTrial()|. Set to two years from the build date. - static int kNoExpirationYear; - - // Observer is notified when a FieldTrial's group is selected. - class BASE_EXPORT Observer { - public: - // Notify observers when FieldTrials's group is selected. - virtual void OnFieldTrialGroupFinalized(const std::string& trial_name, - const std::string& group_name) = 0; - - protected: - virtual ~Observer(); - }; - - // This singleton holds the global list of registered FieldTrials. - // - // To support one-time randomized field trials, specify a non-null - // |entropy_provider| which should be a source of uniformly distributed - // entropy values. If one time randomization is not desired, pass in null for - // |entropy_provider|. - explicit FieldTrialList( - std::unique_ptr entropy_provider); - - // Destructor Release()'s references to all registered FieldTrial instances. - ~FieldTrialList(); - - // Get a FieldTrial instance from the factory. - // - // |name| is used to register the instance with the FieldTrialList class, - // and can be used to find the trial (only one trial can be present for each - // name). |default_group_name| is the name of the default group which will - // be chosen if none of the subsequent appended groups get to be chosen. - // |default_group_number| can receive the group number of the default group as - // AppendGroup returns the number of the subsequence groups. |trial_name| and - // |default_group_name| may not be empty but |default_group_number| can be - // NULL if the value is not needed. - // - // Group probabilities that are later supplied must sum to less than or equal - // to the |total_probability|. Arguments |year|, |month| and |day_of_month| - // specify the expiration time. If the build time is after the expiration time - // then the field trial reverts to the 'default' group. - // - // Use this static method to get a startup-randomized FieldTrial or a - // previously created forced FieldTrial. - static FieldTrial* FactoryGetFieldTrial( - const std::string& trial_name, - FieldTrial::Probability total_probability, - const std::string& default_group_name, - const int year, - const int month, - const int day_of_month, - FieldTrial::RandomizationType randomization_type, - int* default_group_number); - - // Same as FactoryGetFieldTrial(), but allows specifying a custom seed to be - // used on one-time randomized field trials (instead of a hash of the trial - // name, which is used otherwise or if |randomization_seed| has value 0). The - // |randomization_seed| value (other than 0) should never be the same for two - // trials, else this would result in correlated group assignments. Note: - // Using a custom randomization seed is only supported by the - // PermutedEntropyProvider (which is used when UMA is not enabled). If - // |override_entropy_provider| is not null, then it will be used for - // randomization instead of the provider given when the FieldTrialList was - // instantiated. - static FieldTrial* FactoryGetFieldTrialWithRandomizationSeed( - const std::string& trial_name, - FieldTrial::Probability total_probability, - const std::string& default_group_name, - const int year, - const int month, - const int day_of_month, - FieldTrial::RandomizationType randomization_type, - uint32_t randomization_seed, - int* default_group_number, - const FieldTrial::EntropyProvider* override_entropy_provider); - - // The Find() method can be used to test to see if a named trial was already - // registered, or to retrieve a pointer to it from the global map. - static FieldTrial* Find(const std::string& trial_name); - - // Returns the group number chosen for the named trial, or - // FieldTrial::kNotFinalized if the trial does not exist. - static int FindValue(const std::string& trial_name); - - // Returns the group name chosen for the named trial, or the empty string if - // the trial does not exist. The first call of this function on a given field - // trial will mark it as active, so that its state will be reported with usage - // metrics, crashes, etc. - static std::string FindFullName(const std::string& trial_name); - - // Returns true if the named trial has been registered. - static bool TrialExists(const std::string& trial_name); - - // Returns true if the named trial exists and has been activated. - static bool IsTrialActive(const std::string& trial_name); - - // Creates a persistent representation of active FieldTrial instances for - // resurrection in another process. This allows randomization to be done in - // one process, and secondary processes can be synchronized on the result. - // The resulting string contains the name and group name pairs of all - // registered FieldTrials for which the group has been chosen and externally - // observed (via |group()|) and which have not been disabled, with "/" used - // to separate all names and to terminate the string. This string is parsed - // by |CreateTrialsFromString()|. - static void StatesToString(std::string* output); - - // Creates a persistent representation of all FieldTrial instances for - // resurrection in another process. This allows randomization to be done in - // one process, and secondary processes can be synchronized on the result. - // The resulting string contains the name and group name pairs of all - // registered FieldTrials including disabled based on |include_expired|, - // with "/" used to separate all names and to terminate the string. All - // activated trials have their name prefixed with "*". This string is parsed - // by |CreateTrialsFromString()|. - static void AllStatesToString(std::string* output, bool include_expired); - - // Creates a persistent representation of all FieldTrial params for - // resurrection in another process. The returned string contains the trial - // name and group name pairs of all registered FieldTrials including disabled - // based on |include_expired| separated by '.'. The pair is followed by ':' - // separator and list of param name and values separated by '/'. It also takes - // |encode_data_func| function pointer for encodeing special charactors. - // This string is parsed by |AssociateParamsFromString()|. - static std::string AllParamsToString(bool include_expired, - EscapeDataFunc encode_data_func); - - // Fills in the supplied vector |active_groups| (which must be empty when - // called) with a snapshot of all registered FieldTrials for which the group - // has been chosen and externally observed (via |group()|) and which have - // not been disabled. - static void GetActiveFieldTrialGroups( - FieldTrial::ActiveGroups* active_groups); - - // Returns the field trials that are marked active in |trials_string|. - static void GetActiveFieldTrialGroupsFromString( - const std::string& trials_string, - FieldTrial::ActiveGroups* active_groups); - - // Returns the field trials that were active when the process was - // created. Either parses the field trial string or the shared memory - // holding field trial information. - // Must be called only after a call to CreateTrialsFromCommandLine(). - static void GetInitiallyActiveFieldTrials( - const base::CommandLine& command_line, - FieldTrial::ActiveGroups* active_groups); - - // Use a state string (re: StatesToString()) to augment the current list of - // field trials to include the supplied trials, and using a 100% probability - // for each trial, force them to have the same group string. This is commonly - // used in a non-browser process, to carry randomly selected state in a - // browser process into this non-browser process, but could also be invoked - // through a command line argument to the browser process. Created field - // trials will be marked "used" for the purposes of active trial reporting - // if they are prefixed with |kActivationMarker|. Trial names in - // |ignored_trial_names| are ignored when parsing |trials_string|. - static bool CreateTrialsFromString( - const std::string& trials_string, - const std::set& ignored_trial_names); - - // Achieves the same thing as CreateTrialsFromString, except wraps the logic - // by taking in the trials from the command line, either via shared memory - // handle or command line argument. A bit of a misnomer since on POSIX we - // simply get the trials from opening |fd_key| if using shared memory. On - // Windows, we expect the |cmd_line| switch for |field_trial_handle_switch| to - // contain the shared memory handle that contains the field trial allocator. - // We need the |field_trial_handle_switch| and |fd_key| arguments to be passed - // in since base/ can't depend on content/. - static void CreateTrialsFromCommandLine(const base::CommandLine& cmd_line, - const char* field_trial_handle_switch, - int fd_key); - - // Creates base::Feature overrides from the command line by first trying to - // use shared memory and then falling back to the command line if it fails. - static void CreateFeaturesFromCommandLine( - const base::CommandLine& command_line, - const char* enable_features_switch, - const char* disable_features_switch, - FeatureList* feature_list); - -#if defined(OS_WIN) - // On Windows, we need to explicitly pass down any handles to be inherited. - // This function adds the shared memory handle to field trial state to the - // list of handles to be inherited. - static void AppendFieldTrialHandleIfNeeded( - base::HandlesToInheritVector* handles); -#elif defined(OS_FUCHSIA) - // TODO(fuchsia): Implement shared-memory configuration (crbug.com/752368). -#elif defined(OS_POSIX) && !defined(OS_NACL) - // On POSIX, we also need to explicitly pass down this file descriptor that - // should be shared with the child process. Returns an invalid handle if it - // was not initialized properly. - static base::SharedMemoryHandle GetFieldTrialHandle(); -#endif - - // Adds a switch to the command line containing the field trial state as a - // string (if not using shared memory to share field trial state), or the - // shared memory handle + length. - // Needs the |field_trial_handle_switch| argument to be passed in since base/ - // can't depend on content/. - static void CopyFieldTrialStateToFlags(const char* field_trial_handle_switch, - const char* enable_features_switch, - const char* disable_features_switch, - base::CommandLine* cmd_line); - - // Create a FieldTrial with the given |name| and using 100% probability for - // the FieldTrial, force FieldTrial to have the same group string as - // |group_name|. This is commonly used in a non-browser process, to carry - // randomly selected state in a browser process into this non-browser process. - // It returns NULL if there is a FieldTrial that is already registered with - // the same |name| but has different finalized group string (|group_name|). - static FieldTrial* CreateFieldTrial(const std::string& name, - const std::string& group_name); - - // Add an observer to be notified when a field trial is irrevocably committed - // to being part of some specific field_group (and hence the group_name is - // also finalized for that field_trial). Returns false and does nothing if - // there is no FieldTrialList singleton. - static bool AddObserver(Observer* observer); - - // Remove an observer. - static void RemoveObserver(Observer* observer); - - // Similar to AddObserver(), but the passed observer will be notified - // synchronously when a field trial is activated and its group selected. It - // will be notified synchronously on the same thread where the activation and - // group selection happened. It is the responsibility of the observer to make - // sure that this is a safe operation and the operation must be fast, as this - // work is done synchronously as part of group() or related APIs. Only a - // single such observer is supported, exposed specifically for crash - // reporting. Must be called on the main thread before any other threads - // have been started. - static void SetSynchronousObserver(Observer* observer); - - // Removes the single synchronous observer. - static void RemoveSynchronousObserver(Observer* observer); - - // Grabs the lock if necessary and adds the field trial to the allocator. This - // should only be called from FinalizeGroupChoice(). - static void OnGroupFinalized(bool is_locked, FieldTrial* field_trial); - - // Notify all observers that a group has been finalized for |field_trial|. - static void NotifyFieldTrialGroupSelection(FieldTrial* field_trial); - - // Return the number of active field trials. - static size_t GetFieldTrialCount(); - - // Gets the parameters for |field_trial| from shared memory and stores them in - // |params|. This is only exposed for use by FieldTrialParamAssociator and - // shouldn't be used by anything else. - static bool GetParamsFromSharedMemory( - FieldTrial* field_trial, - std::map* params); - - // Clears all the params in the allocator. - static void ClearParamsFromSharedMemoryForTesting(); - - // Dumps field trial state to an allocator so that it can be analyzed after a - // crash. - static void DumpAllFieldTrialsToPersistentAllocator( - PersistentMemoryAllocator* allocator); - - // Retrieves field trial state from an allocator so that it can be analyzed - // after a crash. The pointers in the returned vector are into the persistent - // memory segment and so are only valid as long as the allocator is valid. - static std::vector - GetAllFieldTrialsFromPersistentAllocator( - PersistentMemoryAllocator const& allocator); - - // Returns true if a global field trial list is set. Only used for testing. - static bool IsGlobalSetForTesting(); - - private: - // Allow tests to access our innards for testing purposes. - FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, InstantiateAllocator); - FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AddTrialsToAllocator); - FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, - DoNotAddSimulatedFieldTrialsToAllocator); - FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, AssociateFieldTrialParams); - FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, ClearParamsFromSharedMemory); - FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, - SerializeSharedMemoryHandleMetadata); - FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest, CheckReadOnlySharedMemoryHandle); - - // Serialization is used to pass information about the handle to child - // processes. It passes a reference to the relevant OS resource, and it passes - // a GUID. Serialization and deserialization doesn't actually transport the - // underlying OS resource - that must be done by the Process launcher. - static std::string SerializeSharedMemoryHandleMetadata( - const SharedMemoryHandle& shm); -#if defined(OS_WIN) || defined(OS_FUCHSIA) - static SharedMemoryHandle DeserializeSharedMemoryHandleMetadata( - const std::string& switch_value); -#elif defined(OS_POSIX) && !defined(OS_NACL) - static SharedMemoryHandle DeserializeSharedMemoryHandleMetadata( - int fd, - const std::string& switch_value); -#endif - -#if defined(OS_WIN) || defined(OS_FUCHSIA) - // Takes in |handle_switch| from the command line which represents the shared - // memory handle for field trials, parses it, and creates the field trials. - // Returns true on success, false on failure. - // |switch_value| also contains the serialized GUID. - static bool CreateTrialsFromSwitchValue(const std::string& switch_value); -#elif defined(OS_POSIX) && !defined(OS_NACL) - // On POSIX systems that use the zygote, we look up the correct fd that backs - // the shared memory segment containing the field trials by looking it up via - // an fd key in GlobalDescriptors. Returns true on success, false on failure. - // |switch_value| also contains the serialized GUID. - static bool CreateTrialsFromDescriptor(int fd_key, - const std::string& switch_value); -#endif - - // Takes an unmapped SharedMemoryHandle, creates a SharedMemory object from it - // and maps it with the correct size. - static bool CreateTrialsFromSharedMemoryHandle(SharedMemoryHandle shm_handle); - - // Expects a mapped piece of shared memory |shm| that was created from the - // browser process's field_trial_allocator and shared via the command line. - // This function recreates the allocator, iterates through all the field - // trials in it, and creates them via CreateFieldTrial(). Returns true if - // successful and false otherwise. - static bool CreateTrialsFromSharedMemory( - std::unique_ptr shm); - - // Instantiate the field trial allocator, add all existing field trials to it, - // and duplicates its handle to a read-only handle, which gets stored in - // |readonly_allocator_handle|. - static void InstantiateFieldTrialAllocatorIfNeeded(); - - // Adds the field trial to the allocator. Caller must hold a lock before - // calling this. - static void AddToAllocatorWhileLocked(PersistentMemoryAllocator* allocator, - FieldTrial* field_trial); - - // Activate the corresponding field trial entry struct in shared memory. - static void ActivateFieldTrialEntryWhileLocked(FieldTrial* field_trial); - - // A map from FieldTrial names to the actual instances. - typedef std::map RegistrationMap; - - // If one-time randomization is enabled, returns a weak pointer to the - // corresponding EntropyProvider. Otherwise, returns NULL. - static const FieldTrial::EntropyProvider* - GetEntropyProviderForOneTimeRandomization(); - - // Helper function should be called only while holding lock_. - FieldTrial* PreLockedFind(const std::string& name); - - // Register() stores a pointer to the given trial in a global map. - // This method also AddRef's the indicated trial. - // This should always be called after creating a new FieldTrial instance. - static void Register(FieldTrial* trial); - - // Returns all the registered trials. - static RegistrationMap GetRegisteredTrials(); - - static FieldTrialList* global_; // The singleton of this class. - - // This will tell us if there is an attempt to register a field - // trial or check if one-time randomization is enabled without - // creating the FieldTrialList. This is not an error, unless a - // FieldTrialList is created after that. - static bool used_without_global_; - - // Lock for access to registered_ and field_trial_allocator_. - Lock lock_; - RegistrationMap registered_; - - std::map seen_states_; - - // Entropy provider to be used for one-time randomized field trials. If NULL, - // one-time randomization is not supported. - std::unique_ptr entropy_provider_; - - // List of observers to be notified when a group is selected for a FieldTrial. - scoped_refptr > observer_list_; - - // Single synchronous observer to be notified when a trial group is chosen. - Observer* synchronous_observer_ = nullptr; - - // Allocator in shared memory containing field trial data. Used in both - // browser and child processes, but readonly in the child. - // In the future, we may want to move this to a more generic place if we want - // to start passing more data other than field trials. - std::unique_ptr field_trial_allocator_ = nullptr; - - // Readonly copy of the handle to the allocator. Needs to be a member variable - // because it's needed from both CopyFieldTrialStateToFlags() and - // AppendFieldTrialHandleIfNeeded(). - base::SharedMemoryHandle readonly_allocator_handle_; - - // Tracks whether CreateTrialsFromCommandLine() has been called. - bool create_trials_from_command_line_called_ = false; - - DISALLOW_COPY_AND_ASSIGN(FieldTrialList); -}; - -} // namespace base - -#endif // BASE_METRICS_FIELD_TRIAL_H_ diff --git a/metrics/field_trial_param_associator.cc b/metrics/field_trial_param_associator.cc deleted file mode 100644 index af76eafac..000000000 --- a/metrics/field_trial_param_associator.cc +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2016 The Chromium 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/field_trial_param_associator.h" - -#include "base/metrics/field_trial.h" - -namespace base { - -FieldTrialParamAssociator::FieldTrialParamAssociator() = default; -FieldTrialParamAssociator::~FieldTrialParamAssociator() = default; - -// static -FieldTrialParamAssociator* FieldTrialParamAssociator::GetInstance() { - return Singleton>::get(); -} - -bool FieldTrialParamAssociator::AssociateFieldTrialParams( - const std::string& trial_name, - const std::string& group_name, - const FieldTrialParams& params) { - if (FieldTrialList::IsTrialActive(trial_name)) - return false; - - AutoLock scoped_lock(lock_); - const FieldTrialKey key(trial_name, group_name); - if (ContainsKey(field_trial_params_, key)) - return false; - - field_trial_params_[key] = params; - return true; -} - -bool FieldTrialParamAssociator::GetFieldTrialParams( - const std::string& trial_name, - FieldTrialParams* params) { - FieldTrial* field_trial = FieldTrialList::Find(trial_name); - if (!field_trial) - return false; - - // First try the local map, falling back to getting it from shared memory. - if (GetFieldTrialParamsWithoutFallback(trial_name, field_trial->group_name(), - params)) { - return true; - } - - // TODO(lawrencewu): add the params to field_trial_params_ for next time. - return FieldTrialList::GetParamsFromSharedMemory(field_trial, params); -} - -bool FieldTrialParamAssociator::GetFieldTrialParamsWithoutFallback( - const std::string& trial_name, - const std::string& group_name, - FieldTrialParams* params) { - AutoLock scoped_lock(lock_); - - const FieldTrialKey key(trial_name, group_name); - if (!ContainsKey(field_trial_params_, key)) - return false; - - *params = field_trial_params_[key]; - return true; -} - -void FieldTrialParamAssociator::ClearAllParamsForTesting() { - { - AutoLock scoped_lock(lock_); - field_trial_params_.clear(); - } - FieldTrialList::ClearParamsFromSharedMemoryForTesting(); -} - -void FieldTrialParamAssociator::ClearParamsForTesting( - const std::string& trial_name, - const std::string& group_name) { - AutoLock scoped_lock(lock_); - const FieldTrialKey key(trial_name, group_name); - field_trial_params_.erase(key); -} - -void FieldTrialParamAssociator::ClearAllCachedParamsForTesting() { - AutoLock scoped_lock(lock_); - field_trial_params_.clear(); -} - -} // namespace base diff --git a/metrics/field_trial_param_associator.h b/metrics/field_trial_param_associator.h deleted file mode 100644 index b35e2cc5b..000000000 --- a/metrics/field_trial_param_associator.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2016 The Chromium 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_FIELD_TRIAL_PARAM_ASSOCIATOR_H_ -#define BASE_METRICS_FIELD_TRIAL_PARAM_ASSOCIATOR_H_ - -#include -#include -#include - -#include "base/base_export.h" -#include "base/memory/singleton.h" -#include "base/metrics/field_trial.h" -#include "base/synchronization/lock.h" - -namespace base { - -// Keeps track of the parameters of all field trials and ensures access to them -// is thread-safe. -class BASE_EXPORT FieldTrialParamAssociator { - public: - FieldTrialParamAssociator(); - ~FieldTrialParamAssociator(); - - // Key-value mapping type for field trial parameters. - typedef std::map FieldTrialParams; - - // Retrieve the singleton. - static FieldTrialParamAssociator* GetInstance(); - - // Sets parameters for the given field trial name and group. - bool AssociateFieldTrialParams(const std::string& trial_name, - const std::string& group_name, - const FieldTrialParams& params); - - // Gets the parameters for a field trial and its chosen group. If not found in - // field_trial_params_, then tries to looks it up in shared memory. - bool GetFieldTrialParams(const std::string& trial_name, - FieldTrialParams* params); - - // Gets the parameters for a field trial and its chosen group. Does not - // fallback to looking it up in shared memory. This should only be used if you - // know for sure the params are in the mapping, like if you're in the browser - // process, and even then you should probably just use GetFieldTrialParams(). - bool GetFieldTrialParamsWithoutFallback(const std::string& trial_name, - const std::string& group_name, - FieldTrialParams* params); - - // Clears the internal field_trial_params_ mapping, plus removes all params in - // shared memory. - void ClearAllParamsForTesting(); - - // Clears a single field trial param. - // Note: this does NOT remove the param in shared memory. - void ClearParamsForTesting(const std::string& trial_name, - const std::string& group_name); - - // Clears the internal field_trial_params_ mapping. - void ClearAllCachedParamsForTesting(); - - private: - friend struct DefaultSingletonTraits; - - // (field_trial_name, field_trial_group) - typedef std::pair FieldTrialKey; - - Lock lock_; - std::map field_trial_params_; - - DISALLOW_COPY_AND_ASSIGN(FieldTrialParamAssociator); -}; - -} // namespace base - -#endif // BASE_METRICS_FIELD_TRIAL_PARAM_ASSOCIATOR_H_ diff --git a/metrics/field_trial_params.cc b/metrics/field_trial_params.cc deleted file mode 100644 index 7195f4a81..000000000 --- a/metrics/field_trial_params.cc +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2017 The Chromium 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/field_trial_params.h" - -#include "base/feature_list.h" -#include "base/metrics/field_trial.h" -#include "base/metrics/field_trial_param_associator.h" -#include "base/strings/string_number_conversions.h" - -namespace base { - -bool AssociateFieldTrialParams( - const std::string& trial_name, - const std::string& group_name, - const std::map& params) { - return base::FieldTrialParamAssociator::GetInstance() - ->AssociateFieldTrialParams(trial_name, group_name, params); -} - -bool GetFieldTrialParams(const std::string& trial_name, - std::map* params) { - return base::FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams( - trial_name, params); -} - -bool GetFieldTrialParamsByFeature(const base::Feature& feature, - std::map* params) { - if (!base::FeatureList::IsEnabled(feature)) - return false; - - base::FieldTrial* trial = base::FeatureList::GetFieldTrial(feature); - if (!trial) - return false; - - return GetFieldTrialParams(trial->trial_name(), params); -} - -std::string GetFieldTrialParamValue(const std::string& trial_name, - const std::string& param_name) { - std::map params; - if (GetFieldTrialParams(trial_name, ¶ms)) { - std::map::iterator it = params.find(param_name); - if (it != params.end()) - return it->second; - } - return std::string(); -} - -std::string GetFieldTrialParamValueByFeature(const base::Feature& feature, - const std::string& param_name) { - if (!base::FeatureList::IsEnabled(feature)) - return std::string(); - - base::FieldTrial* trial = base::FeatureList::GetFieldTrial(feature); - if (!trial) - return std::string(); - - return GetFieldTrialParamValue(trial->trial_name(), param_name); -} - -int GetFieldTrialParamByFeatureAsInt(const base::Feature& feature, - const std::string& param_name, - int default_value) { - std::string value_as_string = - GetFieldTrialParamValueByFeature(feature, param_name); - int value_as_int = 0; - if (!base::StringToInt(value_as_string, &value_as_int)) { - if (!value_as_string.empty()) { - DLOG(WARNING) << "Failed to parse field trial param " << param_name - << " with string value " << value_as_string - << " under feature " << feature.name - << " into an int. Falling back to default value of " - << default_value; - } - value_as_int = default_value; - } - return value_as_int; -} - -double GetFieldTrialParamByFeatureAsDouble(const base::Feature& feature, - const std::string& param_name, - double default_value) { - std::string value_as_string = - GetFieldTrialParamValueByFeature(feature, param_name); - double value_as_double = 0; - if (!base::StringToDouble(value_as_string, &value_as_double)) { - if (!value_as_string.empty()) { - DLOG(WARNING) << "Failed to parse field trial param " << param_name - << " with string value " << value_as_string - << " under feature " << feature.name - << " into a double. Falling back to default value of " - << default_value; - } - value_as_double = default_value; - } - return value_as_double; -} - -bool GetFieldTrialParamByFeatureAsBool(const base::Feature& feature, - const std::string& param_name, - bool default_value) { - std::string value_as_string = - GetFieldTrialParamValueByFeature(feature, param_name); - if (value_as_string == "true") - return true; - if (value_as_string == "false") - return false; - - if (!value_as_string.empty()) { - DLOG(WARNING) << "Failed to parse field trial param " << param_name - << " with string value " << value_as_string - << " under feature " << feature.name - << " into a bool. Falling back to default value of " - << default_value; - } - return default_value; -} - -std::string FeatureParam::Get() const { - const std::string value = GetFieldTrialParamValueByFeature(*feature, name); - return value.empty() ? default_value : value; -} - -double FeatureParam::Get() const { - return GetFieldTrialParamByFeatureAsDouble(*feature, name, default_value); -} - -int FeatureParam::Get() const { - return GetFieldTrialParamByFeatureAsInt(*feature, name, default_value); -} - -bool FeatureParam::Get() const { - return GetFieldTrialParamByFeatureAsBool(*feature, name, default_value); -} - -void LogInvalidEnumValue(const base::Feature& feature, - const std::string& param_name, - const std::string& value_as_string, - int default_value_as_int) { - DLOG(WARNING) << "Failed to parse field trial param " << param_name - << " with string value " << value_as_string << " under feature " - << feature.name - << " into an enum. Falling back to default value of " - << default_value_as_int; -} - -} // namespace base diff --git a/metrics/field_trial_params.h b/metrics/field_trial_params.h deleted file mode 100644 index 8682226a2..000000000 --- a/metrics/field_trial_params.h +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2017 The Chromium 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_FIELD_TRIAL_PARAMS_H_ -#define BASE_METRICS_FIELD_TRIAL_PARAMS_H_ - -#include -#include - -#include "base/base_export.h" - -namespace base { - -struct Feature; - -// Associates the specified set of key-value |params| with the field trial -// specified by |trial_name| and |group_name|. Fails and returns false if the -// specified field trial already has params associated with it or the trial -// is already active (group() has been called on it). Thread safe. -BASE_EXPORT bool AssociateFieldTrialParams( - const std::string& trial_name, - const std::string& group_name, - const std::map& params); - -// Retrieves the set of key-value |params| for the specified field trial, based -// on its selected group. If the field trial does not exist or its selected -// group does not have any parameters associated with it, returns false and -// does not modify |params|. Calling this function will result in the field -// trial being marked as active if found (i.e. group() will be called on it), -// if it wasn't already. Thread safe. -BASE_EXPORT bool GetFieldTrialParams( - const std::string& trial_name, - std::map* params); - -// Retrieves the set of key-value |params| for the field trial associated with -// the specified |feature|. A feature is associated with at most one field -// trial and selected group. See base/feature_list.h for more information on -// features. If the feature is not enabled, or if there's no associated params, -// returns false and does not modify |params|. Calling this function will -// result in the associated field trial being marked as active if found (i.e. -// group() will be called on it), if it wasn't already. Thread safe. -BASE_EXPORT bool GetFieldTrialParamsByFeature( - const base::Feature& feature, - std::map* params); - -// Retrieves a specific parameter value corresponding to |param_name| for the -// specified field trial, based on its selected group. If the field trial does -// not exist or the specified parameter does not exist, returns an empty -// string. Calling this function will result in the field trial being marked as -// active if found (i.e. group() will be called on it), if it wasn't already. -// Thread safe. -BASE_EXPORT std::string GetFieldTrialParamValue(const std::string& trial_name, - const std::string& param_name); - -// Retrieves a specific parameter value corresponding to |param_name| for the -// field trial associated with the specified |feature|. A feature is associated -// with at most one field trial and selected group. See base/feature_list.h for -// more information on features. If the feature is not enabled, or the -// specified parameter does not exist, returns an empty string. Calling this -// function will result in the associated field trial being marked as active if -// found (i.e. group() will be called on it), if it wasn't already. Thread safe. -BASE_EXPORT std::string GetFieldTrialParamValueByFeature( - const base::Feature& feature, - const std::string& param_name); - -// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the -// string value into an int using base::StringToInt() and returns it, if -// successful. Otherwise, it returns |default_value|. If the string value is not -// empty and the conversion does not succeed, it produces a warning to LOG. -BASE_EXPORT int GetFieldTrialParamByFeatureAsInt(const base::Feature& feature, - const std::string& param_name, - int default_value); - -// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the -// string value into a double using base::StringToDouble() and returns it, if -// successful. Otherwise, it returns |default_value|. If the string value is not -// empty and the conversion does not succeed, it produces a warning to LOG. -BASE_EXPORT double GetFieldTrialParamByFeatureAsDouble( - const base::Feature& feature, - const std::string& param_name, - double default_value); - -// Same as GetFieldTrialParamValueByFeature(). On top of that, it converts the -// string value into a boolean and returns it, if successful. Otherwise, it -// returns |default_value|. The only string representations accepted here are -// "true" and "false". If the string value is not empty and the conversion does -// not succeed, it produces a warning to LOG. -BASE_EXPORT bool GetFieldTrialParamByFeatureAsBool( - const base::Feature& feature, - const std::string& param_name, - bool default_value); - -// Shared declaration for various FeatureParam types. -// -// This template is defined for the following types T: -// bool -// int -// double -// std::string -// enum types -// -// See the individual definitions below for the appropriate interfaces. -// Attempting to use it with any other type is a compile error. -template ::value> -struct FeatureParam { - // Prevent use of FeatureParam<> with unsupported types (e.g. void*). Uses T - // in its definition so that evaluation is deferred until the template is - // instantiated. - static_assert(!std::is_same::value, "unsupported FeatureParam<> type"); -}; - -// Declares a string-valued parameter. Example: -// -// constexpr FeatureParam kAssistantName{ -// &kAssistantFeature, "assistant_name", "HAL"}; -// -// If the feature is not set, or set to the empty string, then Get() will return -// the default value. -template <> -struct FeatureParam { - constexpr FeatureParam(const Feature* feature, - const char* name, - const char* default_value) - : feature(feature), name(name), default_value(default_value) {} - - BASE_EXPORT std::string Get() const; - - const Feature* const feature; - const char* const name; - const char* const default_value; -}; - -// Declares a double-valued parameter. Example: -// -// constexpr FeatureParam kAssistantTriggerThreshold{ -// &kAssistantFeature, "trigger_threshold", 0.10}; -// -// If the feature is not set, or set to an invalid double value, then Get() will -// return the default value. -template <> -struct FeatureParam { - constexpr FeatureParam(const Feature* feature, - const char* name, - double default_value) - : feature(feature), name(name), default_value(default_value) {} - - BASE_EXPORT double Get() const; - - const Feature* const feature; - const char* const name; - const double default_value; -}; - -// Declares an int-valued parameter. Example: -// -// constexpr FeatureParam kAssistantParallelism{ -// &kAssistantFeature, "parallelism", 4}; -// -// If the feature is not set, or set to an invalid int value, then Get() will -// return the default value. -template <> -struct FeatureParam { - constexpr FeatureParam(const Feature* feature, - const char* name, - int default_value) - : feature(feature), name(name), default_value(default_value) {} - - BASE_EXPORT int Get() const; - - const Feature* const feature; - const char* const name; - const int default_value; -}; - -// Declares a bool-valued parameter. Example: -// -// constexpr FeatureParam kAssistantIsHelpful{ -// &kAssistantFeature, "is_helpful", true}; -// -// If the feature is not set, or set to value other than "true" or "false", then -// Get() will return the default value. -template <> -struct FeatureParam { - constexpr FeatureParam(const Feature* feature, - const char* name, - bool default_value) - : feature(feature), name(name), default_value(default_value) {} - - BASE_EXPORT bool Get() const; - - const Feature* const feature; - const char* const name; - const bool default_value; -}; - -BASE_EXPORT void LogInvalidEnumValue(const Feature& feature, - const std::string& param_name, - const std::string& value_as_string, - int default_value_as_int); - -// Feature param declaration for an enum, with associated options. Example: -// -// constexpr FeatureParam::Option[] kShapeParamOptions[] = { -// {SHAPE_CIRCLE, "circle"}, -// {SHAPE_CYLINDER, "cylinder"}, -// {SHAPE_PAPERCLIP, "paperclip"}}; -// constexpr FeatureParam kAssistantShapeParam{ -// &kAssistantFeature, "shape", SHAPE_CIRCLE, &kShapeParamOptions}; -// -// With this declaration, the parameter may be set to "circle", "cylinder", or -// "paperclip", and that will be translated to one of the three enum values. By -// default, or if the param is set to an unknown value, the parameter will be -// assumed to be SHAPE_CIRCLE. -template -struct FeatureParam { - struct Option { - constexpr Option(Enum value, const char* name) : value(value), name(name) {} - - const Enum value; - const char* const name; - }; - - template - constexpr FeatureParam(const Feature* feature, - const char* name, - const Enum default_value, - const Option (*options)[option_count]) - : feature(feature), - name(name), - default_value(default_value), - options(*options), - option_count(option_count) { - static_assert(option_count >= 1, "FeatureParam has no options"); - } - - Enum Get() const { - std::string value = GetFieldTrialParamValueByFeature(*feature, name); - if (value.empty()) - return default_value; - for (size_t i = 0; i < option_count; ++i) { - if (value == options[i].name) - return options[i].value; - } - LogInvalidEnumValue(*feature, name, value, static_cast(default_value)); - return default_value; - } - - const base::Feature* const feature; - const char* const name; - const Enum default_value; - const Option* const options; - const size_t option_count; -}; - -} // namespace base - -#endif // BASE_METRICS_FIELD_TRIAL_PARAMS_H_ diff --git a/metrics/field_trial_params_unittest.cc b/metrics/field_trial_params_unittest.cc deleted file mode 100644 index d310c0d4f..000000000 --- a/metrics/field_trial_params_unittest.cc +++ /dev/null @@ -1,458 +0,0 @@ -// Copyright 2017 The Chromium 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/field_trial_params.h" - -#include "base/feature_list.h" -#include "base/macros.h" -#include "base/metrics/field_trial.h" -#include "base/metrics/field_trial_param_associator.h" -#include "base/test/scoped_feature_list.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -// Call FieldTrialList::FactoryGetFieldTrial() with a future expiry date. -scoped_refptr CreateFieldTrial( - const std::string& trial_name, - int total_probability, - const std::string& default_group_name, - int* default_group_number) { - return FieldTrialList::FactoryGetFieldTrial( - trial_name, total_probability, default_group_name, - FieldTrialList::kNoExpirationYear, 1, 1, FieldTrial::SESSION_RANDOMIZED, - default_group_number); -} - -} // namespace - -class FieldTrialParamsTest : public ::testing::Test { - public: - FieldTrialParamsTest() : field_trial_list_(nullptr) {} - - ~FieldTrialParamsTest() override { - // Ensure that the maps are cleared between tests, since they are stored as - // process singletons. - FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting(); - } - - void CreateFeatureWithTrial(const Feature& feature, - FeatureList::OverrideState override_state, - FieldTrial* trial) { - std::unique_ptr feature_list(new FeatureList); - feature_list->RegisterFieldTrialOverride(feature.name, override_state, - trial); - scoped_feature_list_.InitWithFeatureList(std::move(feature_list)); - } - - private: - FieldTrialList field_trial_list_; - test::ScopedFeatureList scoped_feature_list_; - - DISALLOW_COPY_AND_ASSIGN(FieldTrialParamsTest); -}; - -TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams) { - const std::string kTrialName = "AssociateFieldTrialParams"; - - { - std::map params; - params["a"] = "10"; - params["b"] = "test"; - ASSERT_TRUE(AssociateFieldTrialParams(kTrialName, "A", params)); - } - { - std::map params; - params["a"] = "5"; - ASSERT_TRUE(AssociateFieldTrialParams(kTrialName, "B", params)); - } - - FieldTrialList::CreateFieldTrial(kTrialName, "B"); - EXPECT_EQ("5", GetFieldTrialParamValue(kTrialName, "a")); - EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "b")); - EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x")); - - std::map params; - EXPECT_TRUE(GetFieldTrialParams(kTrialName, ¶ms)); - EXPECT_EQ(1U, params.size()); - EXPECT_EQ("5", params["a"]); -} - -TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams_Fail) { - const std::string kTrialName = "AssociateFieldTrialParams_Fail"; - const std::string kGroupName = "A"; - - std::map params; - params["a"] = "10"; - ASSERT_TRUE(AssociateFieldTrialParams(kTrialName, kGroupName, params)); - params["a"] = "1"; - params["b"] = "2"; - ASSERT_FALSE(AssociateFieldTrialParams(kTrialName, kGroupName, params)); - - FieldTrialList::CreateFieldTrial(kTrialName, kGroupName); - EXPECT_EQ("10", GetFieldTrialParamValue(kTrialName, "a")); - EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "b")); -} - -TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams_TrialActiveFail) { - const std::string kTrialName = "AssociateFieldTrialParams_TrialActiveFail"; - FieldTrialList::CreateFieldTrial(kTrialName, "A"); - ASSERT_EQ("A", FieldTrialList::FindFullName(kTrialName)); - - std::map params; - params["a"] = "10"; - EXPECT_FALSE(AssociateFieldTrialParams(kTrialName, "B", params)); - EXPECT_FALSE(AssociateFieldTrialParams(kTrialName, "A", params)); -} - -TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams_DoesntActivateTrial) { - const std::string kTrialName = - "AssociateFieldTrialParams_DoesntActivateTrial"; - - ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); - - std::map params; - params["a"] = "10"; - EXPECT_TRUE(AssociateFieldTrialParams(kTrialName, "A", params)); - ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); -} - -TEST_F(FieldTrialParamsTest, GetFieldTrialParams_NoTrial) { - const std::string kTrialName = "GetFieldTrialParams_NoParams"; - - std::map params; - EXPECT_FALSE(GetFieldTrialParams(kTrialName, ¶ms)); - EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x")); - EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "y")); -} - -TEST_F(FieldTrialParamsTest, GetFieldTrialParams_NoParams) { - const std::string kTrialName = "GetFieldTrialParams_NoParams"; - - FieldTrialList::CreateFieldTrial(kTrialName, "A"); - - std::map params; - EXPECT_FALSE(GetFieldTrialParams(kTrialName, ¶ms)); - EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x")); - EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "y")); -} - -TEST_F(FieldTrialParamsTest, GetFieldTrialParams_ActivatesTrial) { - const std::string kTrialName = "GetFieldTrialParams_ActivatesTrial"; - - ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); - - std::map params; - EXPECT_FALSE(GetFieldTrialParams(kTrialName, ¶ms)); - ASSERT_TRUE(FieldTrialList::IsTrialActive(kTrialName)); -} - -TEST_F(FieldTrialParamsTest, GetFieldTrialParamValue_ActivatesTrial) { - const std::string kTrialName = "GetFieldTrialParamValue_ActivatesTrial"; - - ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); - - std::map params; - EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x")); - ASSERT_TRUE(FieldTrialList::IsTrialActive(kTrialName)); -} - -TEST_F(FieldTrialParamsTest, GetFieldTrialParamsByFeature) { - const std::string kTrialName = "GetFieldTrialParamsByFeature"; - const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT}; - - std::map params; - params["x"] = "1"; - AssociateFieldTrialParams(kTrialName, "A", params); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - - CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE, - trial.get()); - - std::map actualParams; - EXPECT_TRUE(GetFieldTrialParamsByFeature(kFeature, &actualParams)); - EXPECT_EQ(params, actualParams); -} - -TEST_F(FieldTrialParamsTest, GetFieldTrialParamValueByFeature) { - const std::string kTrialName = "GetFieldTrialParamsByFeature"; - const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT}; - - std::map params; - params["x"] = "1"; - AssociateFieldTrialParams(kTrialName, "A", params); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - - CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE, - trial.get()); - - std::map actualParams; - EXPECT_EQ(params["x"], GetFieldTrialParamValueByFeature(kFeature, "x")); -} - -TEST_F(FieldTrialParamsTest, GetFieldTrialParamsByFeature_Disable) { - const std::string kTrialName = "GetFieldTrialParamsByFeature"; - const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT}; - - std::map params; - params["x"] = "1"; - AssociateFieldTrialParams(kTrialName, "A", params); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - - CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_DISABLE_FEATURE, - trial.get()); - - std::map actualParams; - EXPECT_FALSE(GetFieldTrialParamsByFeature(kFeature, &actualParams)); -} - -TEST_F(FieldTrialParamsTest, GetFieldTrialParamValueByFeature_Disable) { - const std::string kTrialName = "GetFieldTrialParamsByFeature"; - const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT}; - - std::map params; - params["x"] = "1"; - AssociateFieldTrialParams(kTrialName, "A", params); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - - CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_DISABLE_FEATURE, - trial.get()); - - std::map actualParams; - EXPECT_EQ(std::string(), GetFieldTrialParamValueByFeature(kFeature, "x")); -} - -TEST_F(FieldTrialParamsTest, FeatureParamString) { - const std::string kTrialName = "GetFieldTrialParamsByFeature"; - - static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT}; - static const FeatureParam a{&kFeature, "a", "default"}; - static const FeatureParam b{&kFeature, "b", ""}; - static const FeatureParam c{&kFeature, "c", "default"}; - static const FeatureParam d{&kFeature, "d", ""}; - static const FeatureParam e{&kFeature, "e", "default"}; - static const FeatureParam f{&kFeature, "f", ""}; - - std::map params; - params["a"] = ""; - params["b"] = "non-default"; - params["c"] = "non-default"; - params["d"] = ""; - // "e" is not registered - // "f" is not registered - AssociateFieldTrialParams(kTrialName, "A", params); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - - CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE, - trial.get()); - - EXPECT_EQ("default", a.Get()); // empty - EXPECT_EQ("non-default", b.Get()); - EXPECT_EQ("non-default", c.Get()); - EXPECT_EQ("", d.Get()); // empty - EXPECT_EQ("default", e.Get()); // not registered - EXPECT_EQ("", f.Get()); // not registered -} - -TEST_F(FieldTrialParamsTest, FeatureParamInt) { - const std::string kTrialName = "GetFieldTrialParamsByFeature"; - - static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT}; - static const FeatureParam a{&kFeature, "a", 0}; - static const FeatureParam b{&kFeature, "b", 0}; - static const FeatureParam c{&kFeature, "c", 0}; - static const FeatureParam d{&kFeature, "d", 0}; - static const FeatureParam e{&kFeature, "e", 0}; - - std::map params; - params["a"] = "1"; - params["b"] = "1.5"; - params["c"] = "foo"; - params["d"] = ""; - // "e" is not registered - AssociateFieldTrialParams(kTrialName, "A", params); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - - CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE, - trial.get()); - - EXPECT_EQ(1, GetFieldTrialParamByFeatureAsInt(kFeature, "a", 0)); - EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "b", 0)); // invalid - EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "c", 0)); // invalid - EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "d", 0)); // empty - EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "e", 0)); // empty - - EXPECT_EQ(1, a.Get()); - EXPECT_EQ(0, b.Get()); // invalid - EXPECT_EQ(0, c.Get()); // invalid - EXPECT_EQ(0, d.Get()); // empty - EXPECT_EQ(0, e.Get()); // empty -} - -TEST_F(FieldTrialParamsTest, FeatureParamDouble) { - const std::string kTrialName = "GetFieldTrialParamsByFeature"; - - static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT}; - static const FeatureParam a{&kFeature, "a", 0.0}; - static const FeatureParam b{&kFeature, "b", 0.0}; - static const FeatureParam c{&kFeature, "c", 0.0}; - static const FeatureParam d{&kFeature, "d", 0.0}; - static const FeatureParam e{&kFeature, "e", 0.0}; - static const FeatureParam f{&kFeature, "f", 0.0}; - - std::map params; - params["a"] = "1"; - params["b"] = "1.5"; - params["c"] = "1.0e-10"; - params["d"] = "foo"; - params["e"] = ""; - // "f" is not registered - AssociateFieldTrialParams(kTrialName, "A", params); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - - CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE, - trial.get()); - - EXPECT_EQ(1, GetFieldTrialParamByFeatureAsDouble(kFeature, "a", 0)); - EXPECT_EQ(1.5, GetFieldTrialParamByFeatureAsDouble(kFeature, "b", 0)); - EXPECT_EQ(1.0e-10, GetFieldTrialParamByFeatureAsDouble(kFeature, "c", 0)); - EXPECT_EQ(0, - GetFieldTrialParamByFeatureAsDouble(kFeature, "d", 0)); // invalid - EXPECT_EQ(0, GetFieldTrialParamByFeatureAsDouble(kFeature, "e", 0)); // empty - EXPECT_EQ(0, GetFieldTrialParamByFeatureAsDouble(kFeature, "f", 0)); // empty - - EXPECT_EQ(1, a.Get()); - EXPECT_EQ(1.5, b.Get()); - EXPECT_EQ(1.0e-10, c.Get()); - EXPECT_EQ(0, d.Get()); // invalid - EXPECT_EQ(0, e.Get()); // empty - EXPECT_EQ(0, f.Get()); // empty -} - -TEST_F(FieldTrialParamsTest, FeatureParamBool) { - const std::string kTrialName = "GetFieldTrialParamsByFeature"; - - static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT}; - static const FeatureParam a{&kFeature, "a", false}; - static const FeatureParam b{&kFeature, "b", true}; - static const FeatureParam c{&kFeature, "c", false}; - static const FeatureParam d{&kFeature, "d", true}; - static const FeatureParam e{&kFeature, "e", true}; - static const FeatureParam f{&kFeature, "f", true}; - - std::map params; - params["a"] = "true"; - params["b"] = "false"; - params["c"] = "1"; - params["d"] = "False"; - params["e"] = ""; - // "f" is not registered - AssociateFieldTrialParams(kTrialName, "A", params); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - - CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE, - trial.get()); - - EXPECT_TRUE(a.Get()); - EXPECT_FALSE(b.Get()); - EXPECT_FALSE(c.Get()); // invalid - EXPECT_TRUE(d.Get()); // invalid - EXPECT_TRUE(e.Get()); // empty - EXPECT_TRUE(f.Get()); // empty -} - -enum Hand { ROCK, PAPER, SCISSORS }; - -TEST_F(FieldTrialParamsTest, FeatureParamEnum) { - const std::string kTrialName = "GetFieldTrialParamsByFeature"; - - static const FeatureParam::Option hands[] = { - {ROCK, "rock"}, {PAPER, "paper"}, {SCISSORS, "scissors"}}; - static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT}; - static const FeatureParam a{&kFeature, "a", ROCK, &hands}; - static const FeatureParam b{&kFeature, "b", ROCK, &hands}; - static const FeatureParam c{&kFeature, "c", ROCK, &hands}; - static const FeatureParam d{&kFeature, "d", ROCK, &hands}; - static const FeatureParam e{&kFeature, "e", PAPER, &hands}; - static const FeatureParam f{&kFeature, "f", SCISSORS, &hands}; - - std::map params; - params["a"] = "rock"; - params["b"] = "paper"; - params["c"] = "scissors"; - params["d"] = "lizard"; - params["e"] = ""; - // "f" is not registered - AssociateFieldTrialParams(kTrialName, "A", params); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - - CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE, - trial.get()); - - EXPECT_EQ(ROCK, a.Get()); - EXPECT_EQ(PAPER, b.Get()); - EXPECT_EQ(SCISSORS, c.Get()); - EXPECT_EQ(ROCK, d.Get()); // invalid - EXPECT_EQ(PAPER, e.Get()); // invalid/empty - EXPECT_EQ(SCISSORS, f.Get()); // not registered -} - -enum class UI { ONE_D, TWO_D, THREE_D }; - -TEST_F(FieldTrialParamsTest, FeatureParamEnumClass) { - const std::string kTrialName = "GetFieldTrialParamsByFeature"; - - static const FeatureParam::Option uis[] = { - {UI::ONE_D, "1d"}, {UI::TWO_D, "2d"}, {UI::THREE_D, "3d"}}; - static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT}; - static const FeatureParam a{&kFeature, "a", UI::ONE_D, &uis}; - static const FeatureParam b{&kFeature, "b", UI::ONE_D, &uis}; - static const FeatureParam c{&kFeature, "c", UI::ONE_D, &uis}; - static const FeatureParam d{&kFeature, "d", UI::ONE_D, &uis}; - static const FeatureParam e{&kFeature, "e", UI::TWO_D, &uis}; - static const FeatureParam f{&kFeature, "f", UI::THREE_D, &uis}; - - std::map params; - params["a"] = "1d"; - params["b"] = "2d"; - params["c"] = "3d"; - params["d"] = "4d"; - params["e"] = ""; - // "f" is not registered - AssociateFieldTrialParams(kTrialName, "A", params); - scoped_refptr trial( - CreateFieldTrial(kTrialName, 100, "A", nullptr)); - - CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE, - trial.get()); - - EXPECT_EQ(UI::ONE_D, a.Get()); - EXPECT_EQ(UI::TWO_D, b.Get()); - EXPECT_EQ(UI::THREE_D, c.Get()); - EXPECT_EQ(UI::ONE_D, d.Get()); // invalid - EXPECT_EQ(UI::TWO_D, e.Get()); // invalid/empty - EXPECT_EQ(UI::THREE_D, f.Get()); // not registered -} - -} // namespace base diff --git a/metrics/field_trial_params_unittest.nc b/metrics/field_trial_params_unittest.nc deleted file mode 100644 index 4c6005e34..000000000 --- a/metrics/field_trial_params_unittest.nc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a "No Compile Test" suite. -// http://dev.chromium.org/developers/testing/no-compile-tests - -#include "base/feature_list.h" -#include "base/metrics/field_trial_params.h" - -constexpr base::Feature kFeature{"NoCompileFeature"}; - -enum Param { FOO, BAR }; - -#if defined(NCTEST_NO_PARAM_TYPE) // [r"too few template arguments"] - -constexpr base::FeatureParam<> kParam{ - &kFeature, "Param"}; - -#elif defined(NCTEST_VOID_PARAM_TYPE) // [r"unsupported FeatureParam<> type"] - -constexpr base::FeatureParam kParam{ - &kFeature, "Param"}; - -#elif defined(NCTEST_INVALID_PARAM_TYPE) // [r"unsupported FeatureParam<> type"] - -constexpr base::FeatureParam kParam{ - &kFeature, "Param", 1u}; - -#elif defined(NCTEST_ENUM_NULL_OPTIONS) // [r"candidate template ignored: could not match"] - -constexpr base::FeatureParam kParam{ - &kFeature, "Param", FOO, nullptr}; - -#elif defined(NCTEST_ENUM_EMPTY_OPTIONS) // [r"zero-length arrays are not permitted"] - -constexpr base::FeatureParam::Option kParamOptions[] = {}; -constexpr base::FeatureParam kParam{ - &kFeature, "Param", FOO, &kParamOptions}; - -#else - -void suppress_unused_variable_warning() { - (void)kFeature; -} - -#endif diff --git a/metrics/field_trial_unittest.cc b/metrics/field_trial_unittest.cc deleted file mode 100644 index 7dbe737d8..000000000 --- a/metrics/field_trial_unittest.cc +++ /dev/null @@ -1,1504 +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/metrics/field_trial.h" - -#include - -#include "base/base_switches.h" -#include "base/build_time.h" -#include "base/feature_list.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" -#include "base/metrics/field_trial_param_associator.h" -#include "base/rand_util.h" -#include "base/run_loop.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" -#include "base/test/gtest_util.h" -#include "base/test/mock_entropy_provider.h" -#include "base/test/scoped_feature_list.h" -#include "base/test/test_shared_memory_util.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -// Default group name used by several tests. -const char kDefaultGroupName[] = "DefaultGroup"; - -// Call FieldTrialList::FactoryGetFieldTrial() with a future expiry date. -scoped_refptr CreateFieldTrial( - const std::string& trial_name, - int total_probability, - const std::string& default_group_name, - int* default_group_number) { - return FieldTrialList::FactoryGetFieldTrial( - trial_name, total_probability, default_group_name, - FieldTrialList::kNoExpirationYear, 1, 1, FieldTrial::SESSION_RANDOMIZED, - default_group_number); -} - -int OneYearBeforeBuildTime() { - Time one_year_before_build_time = GetBuildTime() - TimeDelta::FromDays(365); - Time::Exploded exploded; - one_year_before_build_time.LocalExplode(&exploded); - return exploded.year; -} - -// FieldTrialList::Observer implementation for testing. -class TestFieldTrialObserver : public FieldTrialList::Observer { - public: - enum Type { - ASYNCHRONOUS, - SYNCHRONOUS, - }; - - TestFieldTrialObserver(Type type) : type_(type) { - if (type == SYNCHRONOUS) - FieldTrialList::SetSynchronousObserver(this); - else - FieldTrialList::AddObserver(this); - } - - ~TestFieldTrialObserver() override { - if (type_ == SYNCHRONOUS) - FieldTrialList::RemoveSynchronousObserver(this); - else - FieldTrialList::RemoveObserver(this); - } - - void OnFieldTrialGroupFinalized(const std::string& trial, - const std::string& group) override { - trial_name_ = trial; - group_name_ = group; - } - - const std::string& trial_name() const { return trial_name_; } - const std::string& group_name() const { return group_name_; } - - private: - const Type type_; - std::string trial_name_; - std::string group_name_; - - DISALLOW_COPY_AND_ASSIGN(TestFieldTrialObserver); -}; - -std::string MockEscapeQueryParamValue(const std::string& input) { - return input; -} - -} // namespace - -class FieldTrialTest : public ::testing::Test { - public: - FieldTrialTest() : trial_list_(nullptr) {} - - private: - MessageLoop message_loop_; - FieldTrialList trial_list_; - - DISALLOW_COPY_AND_ASSIGN(FieldTrialTest); -}; - -// Test registration, and also check that destructors are called for trials. -TEST_F(FieldTrialTest, Registration) { - const char name1[] = "name 1 test"; - const char name2[] = "name 2 test"; - EXPECT_FALSE(FieldTrialList::Find(name1)); - EXPECT_FALSE(FieldTrialList::Find(name2)); - - scoped_refptr trial1 = - CreateFieldTrial(name1, 10, "default name 1 test", nullptr); - EXPECT_EQ(FieldTrial::kNotFinalized, trial1->group_); - EXPECT_EQ(name1, trial1->trial_name()); - EXPECT_EQ("", trial1->group_name_internal()); - - trial1->AppendGroup(std::string(), 7); - - EXPECT_EQ(trial1.get(), FieldTrialList::Find(name1)); - EXPECT_FALSE(FieldTrialList::Find(name2)); - - scoped_refptr trial2 = - CreateFieldTrial(name2, 10, "default name 2 test", nullptr); - EXPECT_EQ(FieldTrial::kNotFinalized, trial2->group_); - EXPECT_EQ(name2, trial2->trial_name()); - EXPECT_EQ("", trial2->group_name_internal()); - - trial2->AppendGroup("a first group", 7); - - EXPECT_EQ(trial1.get(), FieldTrialList::Find(name1)); - EXPECT_EQ(trial2.get(), FieldTrialList::Find(name2)); - // Note: FieldTrialList should delete the objects at shutdown. -} - -TEST_F(FieldTrialTest, AbsoluteProbabilities) { - char always_true[] = " always true"; - char default_always_true[] = " default always true"; - char always_false[] = " always false"; - char default_always_false[] = " default always false"; - for (int i = 1; i < 250; ++i) { - // Try lots of names, by changing the first character of the name. - char c = static_cast(i); - always_true[0] = c; - default_always_true[0] = c; - always_false[0] = c; - default_always_false[0] = c; - - scoped_refptr trial_true = - CreateFieldTrial(always_true, 10, default_always_true, nullptr); - const std::string winner = "TheWinner"; - int winner_group = trial_true->AppendGroup(winner, 10); - - EXPECT_EQ(winner_group, trial_true->group()); - EXPECT_EQ(winner, trial_true->group_name()); - - scoped_refptr trial_false = - CreateFieldTrial(always_false, 10, default_always_false, nullptr); - int loser_group = trial_false->AppendGroup("ALoser", 0); - - EXPECT_NE(loser_group, trial_false->group()); - } -} - -TEST_F(FieldTrialTest, RemainingProbability) { - // First create a test that hasn't had a winner yet. - const std::string winner = "Winner"; - const std::string loser = "Loser"; - scoped_refptr trial; - int counter = 0; - int default_group_number = -1; - do { - std::string name = StringPrintf("trial%d", ++counter); - trial = CreateFieldTrial(name, 10, winner, &default_group_number); - trial->AppendGroup(loser, 5); // 50% chance of not being chosen. - // If a group is not assigned, group_ will be kNotFinalized. - } while (trial->group_ != FieldTrial::kNotFinalized); - - // And that 'default' group (winner) should always win. - EXPECT_EQ(default_group_number, trial->group()); - - // And that winner should ALWAYS win. - EXPECT_EQ(winner, trial->group_name()); -} - -TEST_F(FieldTrialTest, FiftyFiftyProbability) { - // Check that even with small divisors, we have the proper probabilities, and - // all outcomes are possible. Since this is a 50-50 test, it should get both - // outcomes in a few tries, but we'll try no more than 100 times (and be flaky - // with probability around 1 in 2^99). - bool first_winner = false; - bool second_winner = false; - int counter = 0; - do { - std::string name = StringPrintf("FiftyFifty%d", ++counter); - std::string default_group_name = - StringPrintf("Default FiftyFifty%d", ++counter); - scoped_refptr trial = - CreateFieldTrial(name, 2, default_group_name, nullptr); - trial->AppendGroup("first", 1); // 50% chance of being chosen. - // If group_ is kNotFinalized, then a group assignement hasn't been done. - if (trial->group_ != FieldTrial::kNotFinalized) { - first_winner = true; - continue; - } - trial->AppendGroup("second", 1); // Always chosen at this point. - EXPECT_NE(FieldTrial::kNotFinalized, trial->group()); - second_winner = true; - } while ((!second_winner || !first_winner) && counter < 100); - EXPECT_TRUE(second_winner); - EXPECT_TRUE(first_winner); -} - -TEST_F(FieldTrialTest, MiddleProbabilities) { - char name[] = " same name"; - char default_group_name[] = " default same name"; - bool false_event_seen = false; - bool true_event_seen = false; - for (int i = 1; i < 250; ++i) { - char c = static_cast(i); - name[0] = c; - default_group_name[0] = c; - scoped_refptr trial = - CreateFieldTrial(name, 10, default_group_name, nullptr); - int might_win = trial->AppendGroup("MightWin", 5); - - if (trial->group() == might_win) { - true_event_seen = true; - } else { - false_event_seen = true; - } - if (false_event_seen && true_event_seen) - return; // Successful test!!! - } - // Very surprising to get here. Probability should be around 1 in 2 ** 250. - // One of the following will fail. - EXPECT_TRUE(false_event_seen); - EXPECT_TRUE(true_event_seen); -} - -TEST_F(FieldTrialTest, OneWinner) { - char name[] = "Some name"; - char default_group_name[] = "Default some name"; - int group_count(10); - - int default_group_number = -1; - scoped_refptr trial = - CreateFieldTrial(name, group_count, default_group_name, nullptr); - int winner_index(-2); - std::string winner_name; - - for (int i = 1; i <= group_count; ++i) { - int might_win = trial->AppendGroup(std::string(), 1); - - // Because we keep appending groups, we want to see if the last group that - // was added has been assigned or not. - if (trial->group_ == might_win) { - EXPECT_EQ(-2, winner_index); - winner_index = might_win; - StringAppendF(&winner_name, "%d", might_win); - EXPECT_EQ(winner_name, trial->group_name()); - } - } - EXPECT_GE(winner_index, 0); - // Since all groups cover the total probability, we should not have - // chosen the default group. - EXPECT_NE(trial->group(), default_group_number); - EXPECT_EQ(trial->group(), winner_index); - EXPECT_EQ(trial->group_name(), winner_name); -} - -TEST_F(FieldTrialTest, DisableProbability) { - const std::string default_group_name = "Default group"; - const std::string loser = "Loser"; - const std::string name = "Trial"; - - // Create a field trail that has expired. - int default_group_number = -1; - FieldTrial* trial = FieldTrialList::FactoryGetFieldTrial( - name, 1000000000, default_group_name, OneYearBeforeBuildTime(), 1, 1, - FieldTrial::SESSION_RANDOMIZED, - &default_group_number); - trial->AppendGroup(loser, 999999999); // 99.9999999% chance of being chosen. - - // Because trial has expired, we should always be in the default group. - EXPECT_EQ(default_group_number, trial->group()); - - // And that default_group_name should ALWAYS win. - EXPECT_EQ(default_group_name, trial->group_name()); -} - -TEST_F(FieldTrialTest, ActiveGroups) { - std::string no_group("No Group"); - scoped_refptr trial = - CreateFieldTrial(no_group, 10, "Default", nullptr); - - // There is no winner yet, so no NameGroupId should be returned. - FieldTrial::ActiveGroup active_group; - EXPECT_FALSE(trial->GetActiveGroup(&active_group)); - - // Create a single winning group. - std::string one_winner("One Winner"); - trial = CreateFieldTrial(one_winner, 10, "Default", nullptr); - std::string winner("Winner"); - trial->AppendGroup(winner, 10); - EXPECT_FALSE(trial->GetActiveGroup(&active_group)); - // Finalize the group selection by accessing the selected group. - trial->group(); - EXPECT_TRUE(trial->GetActiveGroup(&active_group)); - EXPECT_EQ(one_winner, active_group.trial_name); - EXPECT_EQ(winner, active_group.group_name); - - std::string multi_group("MultiGroup"); - scoped_refptr multi_group_trial = - CreateFieldTrial(multi_group, 9, "Default", nullptr); - - multi_group_trial->AppendGroup("Me", 3); - multi_group_trial->AppendGroup("You", 3); - multi_group_trial->AppendGroup("Them", 3); - EXPECT_FALSE(multi_group_trial->GetActiveGroup(&active_group)); - // Finalize the group selection by accessing the selected group. - multi_group_trial->group(); - EXPECT_TRUE(multi_group_trial->GetActiveGroup(&active_group)); - EXPECT_EQ(multi_group, active_group.trial_name); - EXPECT_EQ(multi_group_trial->group_name(), active_group.group_name); - - // Now check if the list is built properly... - FieldTrial::ActiveGroups active_groups; - FieldTrialList::GetActiveFieldTrialGroups(&active_groups); - EXPECT_EQ(2U, active_groups.size()); - for (size_t i = 0; i < active_groups.size(); ++i) { - // Order is not guaranteed, so check all values. - EXPECT_NE(no_group, active_groups[i].trial_name); - EXPECT_TRUE(one_winner != active_groups[i].trial_name || - winner == active_groups[i].group_name); - EXPECT_TRUE(multi_group != active_groups[i].trial_name || - multi_group_trial->group_name() == active_groups[i].group_name); - } -} - -TEST_F(FieldTrialTest, GetActiveFieldTrialGroupsFromString) { - FieldTrial::ActiveGroups active_groups; - FieldTrialList::GetActiveFieldTrialGroupsFromString("*A/X/B/Y/*C/Z", - &active_groups); - ASSERT_EQ(2U, active_groups.size()); - EXPECT_EQ("A", active_groups[0].trial_name); - EXPECT_EQ("X", active_groups[0].group_name); - EXPECT_EQ("C", active_groups[1].trial_name); - EXPECT_EQ("Z", active_groups[1].group_name); -} - -TEST_F(FieldTrialTest, ActiveGroupsNotFinalized) { - const char kTrialName[] = "TestTrial"; - const char kSecondaryGroupName[] = "SecondaryGroup"; - - int default_group = -1; - scoped_refptr trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); - const int secondary_group = trial->AppendGroup(kSecondaryGroupName, 50); - - // Before |group()| is called, |GetActiveGroup()| should return false. - FieldTrial::ActiveGroup active_group; - EXPECT_FALSE(trial->GetActiveGroup(&active_group)); - - // |GetActiveFieldTrialGroups()| should also not include the trial. - FieldTrial::ActiveGroups active_groups; - FieldTrialList::GetActiveFieldTrialGroups(&active_groups); - EXPECT_TRUE(active_groups.empty()); - - // After |group()| has been called, both APIs should succeed. - const int chosen_group = trial->group(); - EXPECT_TRUE(chosen_group == default_group || chosen_group == secondary_group); - - EXPECT_TRUE(trial->GetActiveGroup(&active_group)); - EXPECT_EQ(kTrialName, active_group.trial_name); - if (chosen_group == default_group) - EXPECT_EQ(kDefaultGroupName, active_group.group_name); - else - EXPECT_EQ(kSecondaryGroupName, active_group.group_name); - - FieldTrialList::GetActiveFieldTrialGroups(&active_groups); - ASSERT_EQ(1U, active_groups.size()); - EXPECT_EQ(kTrialName, active_groups[0].trial_name); - EXPECT_EQ(active_group.group_name, active_groups[0].group_name); -} - -TEST_F(FieldTrialTest, GetGroupNameWithoutActivation) { - const char kTrialName[] = "TestTrial"; - const char kSecondaryGroupName[] = "SecondaryGroup"; - - int default_group = -1; - scoped_refptr trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); - trial->AppendGroup(kSecondaryGroupName, 50); - - // The trial should start inactive. - EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); - - // Calling |GetGroupNameWithoutActivation()| should not activate the trial. - std::string group_name = trial->GetGroupNameWithoutActivation(); - EXPECT_FALSE(group_name.empty()); - EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); - - // Calling |group_name()| should activate it and return the same group name. - EXPECT_EQ(group_name, trial->group_name()); - EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName)); -} - -TEST_F(FieldTrialTest, Save) { - std::string save_string; - - scoped_refptr trial = - CreateFieldTrial("Some name", 10, "Default some name", nullptr); - // There is no winner yet, so no textual group name is associated with trial. - // In this case, the trial should not be included. - EXPECT_EQ("", trial->group_name_internal()); - FieldTrialList::StatesToString(&save_string); - EXPECT_EQ("", save_string); - save_string.clear(); - - // Create a winning group. - trial->AppendGroup("Winner", 10); - // Finalize the group selection by accessing the selected group. - trial->group(); - FieldTrialList::StatesToString(&save_string); - EXPECT_EQ("Some name/Winner/", save_string); - save_string.clear(); - - // Create a second trial and winning group. - scoped_refptr trial2 = - CreateFieldTrial("xxx", 10, "Default xxx", nullptr); - trial2->AppendGroup("yyyy", 10); - // Finalize the group selection by accessing the selected group. - trial2->group(); - - FieldTrialList::StatesToString(&save_string); - // We assume names are alphabetized... though this is not critical. - EXPECT_EQ("Some name/Winner/xxx/yyyy/", save_string); - save_string.clear(); - - // Create a third trial with only the default group. - scoped_refptr trial3 = - CreateFieldTrial("zzz", 10, "default", nullptr); - // Finalize the group selection by accessing the selected group. - trial3->group(); - - FieldTrialList::StatesToString(&save_string); - EXPECT_EQ("Some name/Winner/xxx/yyyy/zzz/default/", save_string); -} - -TEST_F(FieldTrialTest, SaveAll) { - std::string save_string; - - scoped_refptr trial = - CreateFieldTrial("Some name", 10, "Default some name", nullptr); - EXPECT_EQ("", trial->group_name_internal()); - FieldTrialList::AllStatesToString(&save_string, false); - EXPECT_EQ("Some name/Default some name/", save_string); - // Getting all states should have finalized the trial. - EXPECT_EQ("Default some name", trial->group_name_internal()); - save_string.clear(); - - // Create a winning group. - trial = CreateFieldTrial("trial2", 10, "Default some name", nullptr); - trial->AppendGroup("Winner", 10); - // Finalize the group selection by accessing the selected group. - trial->group(); - FieldTrialList::AllStatesToString(&save_string, false); - EXPECT_EQ("Some name/Default some name/*trial2/Winner/", save_string); - save_string.clear(); - - // Create a second trial and winning group. - scoped_refptr trial2 = - CreateFieldTrial("xxx", 10, "Default xxx", nullptr); - trial2->AppendGroup("yyyy", 10); - // Finalize the group selection by accessing the selected group. - trial2->group(); - - FieldTrialList::AllStatesToString(&save_string, false); - // We assume names are alphabetized... though this is not critical. - EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/", - save_string); - save_string.clear(); - - // Create a third trial with only the default group. - scoped_refptr trial3 = - CreateFieldTrial("zzz", 10, "default", nullptr); - - FieldTrialList::AllStatesToString(&save_string, false); - EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default/", - save_string); - - // Create expired study. - int default_group_number = -1; - scoped_refptr expired_trial = - FieldTrialList::FactoryGetFieldTrial( - "Expired trial name", 1000000000, "Default group", - OneYearBeforeBuildTime(), 1, 1, FieldTrial::SESSION_RANDOMIZED, - &default_group_number); - expired_trial->AppendGroup("Expired trial group name", 999999999); - - save_string.clear(); - FieldTrialList::AllStatesToString(&save_string, false); - EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default/", - save_string); - save_string.clear(); - FieldTrialList::AllStatesToString(&save_string, true); - EXPECT_EQ( - "Expired trial name/Default group/" - "Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default/", - save_string); -} - -TEST_F(FieldTrialTest, Restore) { - ASSERT_FALSE(FieldTrialList::TrialExists("Some_name")); - ASSERT_FALSE(FieldTrialList::TrialExists("xxx")); - - FieldTrialList::CreateTrialsFromString("Some_name/Winner/xxx/yyyy/", - std::set()); - - FieldTrial* trial = FieldTrialList::Find("Some_name"); - ASSERT_NE(static_cast(nullptr), trial); - EXPECT_EQ("Winner", trial->group_name()); - EXPECT_EQ("Some_name", trial->trial_name()); - - trial = FieldTrialList::Find("xxx"); - ASSERT_NE(static_cast(nullptr), trial); - EXPECT_EQ("yyyy", trial->group_name()); - EXPECT_EQ("xxx", trial->trial_name()); -} - -TEST_F(FieldTrialTest, RestoreNotEndingWithSlash) { - EXPECT_TRUE(FieldTrialList::CreateTrialsFromString("tname/gname", - std::set())); - - FieldTrial* trial = FieldTrialList::Find("tname"); - ASSERT_NE(static_cast(nullptr), trial); - EXPECT_EQ("gname", trial->group_name()); - EXPECT_EQ("tname", trial->trial_name()); -} - -TEST_F(FieldTrialTest, BogusRestore) { - EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("MissingSlash", - std::set())); - EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("MissingGroupName/", - std::set())); - EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("noname, only group/", - std::set())); - EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("/emptyname", - std::set())); - EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("*/emptyname", - std::set())); -} - -TEST_F(FieldTrialTest, DuplicateRestore) { - scoped_refptr trial = - CreateFieldTrial("Some name", 10, "Default", nullptr); - trial->AppendGroup("Winner", 10); - // Finalize the group selection by accessing the selected group. - trial->group(); - std::string save_string; - FieldTrialList::StatesToString(&save_string); - EXPECT_EQ("Some name/Winner/", save_string); - - // It is OK if we redundantly specify a winner. - EXPECT_TRUE(FieldTrialList::CreateTrialsFromString(save_string, - std::set())); - - // But it is an error to try to change to a different winner. - EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("Some name/Loser/", - std::set())); -} - -TEST_F(FieldTrialTest, CreateTrialsFromStringNotActive) { - ASSERT_FALSE(FieldTrialList::TrialExists("Abc")); - ASSERT_FALSE(FieldTrialList::TrialExists("Xyz")); - ASSERT_TRUE(FieldTrialList::CreateTrialsFromString("Abc/def/Xyz/zyx/", - std::set())); - - FieldTrial::ActiveGroups active_groups; - FieldTrialList::GetActiveFieldTrialGroups(&active_groups); - ASSERT_TRUE(active_groups.empty()); - - // Check that the values still get returned and querying them activates them. - EXPECT_EQ("def", FieldTrialList::FindFullName("Abc")); - EXPECT_EQ("zyx", FieldTrialList::FindFullName("Xyz")); - - FieldTrialList::GetActiveFieldTrialGroups(&active_groups); - ASSERT_EQ(2U, active_groups.size()); - EXPECT_EQ("Abc", active_groups[0].trial_name); - EXPECT_EQ("def", active_groups[0].group_name); - EXPECT_EQ("Xyz", active_groups[1].trial_name); - EXPECT_EQ("zyx", active_groups[1].group_name); -} - -TEST_F(FieldTrialTest, CreateTrialsFromStringForceActivation) { - ASSERT_FALSE(FieldTrialList::TrialExists("Abc")); - ASSERT_FALSE(FieldTrialList::TrialExists("def")); - ASSERT_FALSE(FieldTrialList::TrialExists("Xyz")); - ASSERT_TRUE(FieldTrialList::CreateTrialsFromString( - "*Abc/cba/def/fed/*Xyz/zyx/", std::set())); - - FieldTrial::ActiveGroups active_groups; - FieldTrialList::GetActiveFieldTrialGroups(&active_groups); - ASSERT_EQ(2U, active_groups.size()); - EXPECT_EQ("Abc", active_groups[0].trial_name); - EXPECT_EQ("cba", active_groups[0].group_name); - EXPECT_EQ("Xyz", active_groups[1].trial_name); - EXPECT_EQ("zyx", active_groups[1].group_name); -} - -TEST_F(FieldTrialTest, CreateTrialsFromStringNotActiveObserver) { - ASSERT_FALSE(FieldTrialList::TrialExists("Abc")); - - TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS); - ASSERT_TRUE(FieldTrialList::CreateTrialsFromString("Abc/def/", - std::set())); - RunLoop().RunUntilIdle(); - // Observer shouldn't be notified. - EXPECT_TRUE(observer.trial_name().empty()); - - // Check that the values still get returned and querying them activates them. - EXPECT_EQ("def", FieldTrialList::FindFullName("Abc")); - - RunLoop().RunUntilIdle(); - EXPECT_EQ("Abc", observer.trial_name()); - 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 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/", - 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(nullptr), trial); - EXPECT_EQ("Foo", trial->trial_name()); - EXPECT_EQ("Foo_name", trial->group_name()); - - trial = FieldTrialList::Find("Bar"); - ASSERT_NE(static_cast(nullptr), trial); - EXPECT_EQ("Bar", trial->trial_name()); - EXPECT_EQ("Bar_name", trial->group_name()); -} - -TEST_F(FieldTrialTest, CreateFieldTrial) { - ASSERT_FALSE(FieldTrialList::TrialExists("Some_name")); - - FieldTrialList::CreateFieldTrial("Some_name", "Winner"); - - FieldTrial* trial = FieldTrialList::Find("Some_name"); - ASSERT_NE(static_cast(nullptr), trial); - EXPECT_EQ("Winner", trial->group_name()); - EXPECT_EQ("Some_name", trial->trial_name()); -} - -TEST_F(FieldTrialTest, CreateFieldTrialIsNotActive) { - const char kTrialName[] = "CreateFieldTrialIsActiveTrial"; - const char kWinnerGroup[] = "Winner"; - ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); - FieldTrialList::CreateFieldTrial(kTrialName, kWinnerGroup); - - FieldTrial::ActiveGroups active_groups; - FieldTrialList::GetActiveFieldTrialGroups(&active_groups); - EXPECT_TRUE(active_groups.empty()); -} - -TEST_F(FieldTrialTest, DuplicateFieldTrial) { - scoped_refptr trial = - CreateFieldTrial("Some_name", 10, "Default", nullptr); - trial->AppendGroup("Winner", 10); - - // It is OK if we redundantly specify a winner. - FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("Some_name", "Winner"); - EXPECT_TRUE(trial1 != nullptr); - - // But it is an error to try to change to a different winner. - FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("Some_name", "Loser"); - EXPECT_TRUE(trial2 == nullptr); -} - -TEST_F(FieldTrialTest, DisableImmediately) { - int default_group_number = -1; - scoped_refptr trial = - CreateFieldTrial("trial", 100, "default", &default_group_number); - trial->Disable(); - ASSERT_EQ("default", trial->group_name()); - ASSERT_EQ(default_group_number, trial->group()); -} - -TEST_F(FieldTrialTest, DisableAfterInitialization) { - scoped_refptr trial = - CreateFieldTrial("trial", 100, "default", nullptr); - trial->AppendGroup("non_default", 100); - trial->Disable(); - ASSERT_EQ("default", trial->group_name()); -} - -TEST_F(FieldTrialTest, ForcedFieldTrials) { - // Validate we keep the forced choice. - FieldTrial* forced_trial = FieldTrialList::CreateFieldTrial("Use the", - "Force"); - EXPECT_STREQ("Force", forced_trial->group_name().c_str()); - - int default_group_number = -1; - scoped_refptr factory_trial = - CreateFieldTrial("Use the", 1000, "default", &default_group_number); - EXPECT_EQ(factory_trial.get(), forced_trial); - - int chosen_group = factory_trial->AppendGroup("Force", 100); - EXPECT_EQ(chosen_group, factory_trial->group()); - int not_chosen_group = factory_trial->AppendGroup("Dark Side", 100); - EXPECT_NE(chosen_group, not_chosen_group); - - // Since we didn't force the default group, we should not be returned the - // chosen group as the default group. - EXPECT_NE(default_group_number, chosen_group); - int new_group = factory_trial->AppendGroup("Duck Tape", 800); - EXPECT_NE(chosen_group, new_group); - // The new group should not be the default group either. - EXPECT_NE(default_group_number, new_group); -} - -TEST_F(FieldTrialTest, ForcedFieldTrialsDefaultGroup) { - // Forcing the default should use the proper group ID. - FieldTrial* forced_trial = FieldTrialList::CreateFieldTrial("Trial Name", - "Default"); - int default_group_number = -1; - scoped_refptr factory_trial = - CreateFieldTrial("Trial Name", 1000, "Default", &default_group_number); - EXPECT_EQ(forced_trial, factory_trial.get()); - - int other_group = factory_trial->AppendGroup("Not Default", 100); - EXPECT_STREQ("Default", factory_trial->group_name().c_str()); - EXPECT_EQ(default_group_number, factory_trial->group()); - EXPECT_NE(other_group, factory_trial->group()); - - int new_other_group = factory_trial->AppendGroup("Not Default Either", 800); - EXPECT_NE(new_other_group, factory_trial->group()); -} - -TEST_F(FieldTrialTest, SetForced) { - // Start by setting a trial for which we ensure a winner... - int default_group_number = -1; - scoped_refptr forced_trial = - CreateFieldTrial("Use the", 1, "default", &default_group_number); - EXPECT_EQ(forced_trial, forced_trial); - - int forced_group = forced_trial->AppendGroup("Force", 1); - EXPECT_EQ(forced_group, forced_trial->group()); - - // Now force it. - forced_trial->SetForced(); - - // Now try to set it up differently as a hard coded registration would. - scoped_refptr hard_coded_trial = - CreateFieldTrial("Use the", 1, "default", &default_group_number); - EXPECT_EQ(hard_coded_trial, forced_trial); - - int would_lose_group = hard_coded_trial->AppendGroup("Force", 0); - EXPECT_EQ(forced_group, hard_coded_trial->group()); - EXPECT_EQ(forced_group, would_lose_group); - - // Same thing if we would have done it to win again. - scoped_refptr other_hard_coded_trial = - CreateFieldTrial("Use the", 1, "default", &default_group_number); - EXPECT_EQ(other_hard_coded_trial, forced_trial); - - int would_win_group = other_hard_coded_trial->AppendGroup("Force", 1); - EXPECT_EQ(forced_group, other_hard_coded_trial->group()); - EXPECT_EQ(forced_group, would_win_group); -} - -TEST_F(FieldTrialTest, SetForcedDefaultOnly) { - const char kTrialName[] = "SetForcedDefaultOnly"; - ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); - - int default_group = -1; - scoped_refptr trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); - trial->SetForced(); - - trial = CreateFieldTrial(kTrialName, 100, kDefaultGroupName, nullptr); - EXPECT_EQ(default_group, trial->group()); - EXPECT_EQ(kDefaultGroupName, trial->group_name()); -} - -TEST_F(FieldTrialTest, SetForcedDefaultWithExtraGroup) { - const char kTrialName[] = "SetForcedDefaultWithExtraGroup"; - ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); - - int default_group = -1; - scoped_refptr trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); - trial->SetForced(); - - trial = CreateFieldTrial(kTrialName, 100, kDefaultGroupName, nullptr); - const int extra_group = trial->AppendGroup("Extra", 100); - EXPECT_EQ(default_group, trial->group()); - EXPECT_NE(extra_group, trial->group()); - EXPECT_EQ(kDefaultGroupName, trial->group_name()); -} - -TEST_F(FieldTrialTest, SetForcedTurnFeatureOn) { - const char kTrialName[] = "SetForcedTurnFeatureOn"; - const char kExtraGroupName[] = "Extra"; - ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); - - // Simulate a server-side (forced) config that turns the feature on when the - // original hard-coded config had it disabled. - scoped_refptr forced_trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, nullptr); - forced_trial->AppendGroup(kExtraGroupName, 100); - forced_trial->SetForced(); - - int default_group = -1; - scoped_refptr client_trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); - const int extra_group = client_trial->AppendGroup(kExtraGroupName, 0); - EXPECT_NE(default_group, extra_group); - - EXPECT_FALSE(client_trial->group_reported_); - EXPECT_EQ(extra_group, client_trial->group()); - EXPECT_TRUE(client_trial->group_reported_); - EXPECT_EQ(kExtraGroupName, client_trial->group_name()); -} - -TEST_F(FieldTrialTest, SetForcedTurnFeatureOff) { - const char kTrialName[] = "SetForcedTurnFeatureOff"; - const char kExtraGroupName[] = "Extra"; - ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); - - // Simulate a server-side (forced) config that turns the feature off when the - // original hard-coded config had it enabled. - scoped_refptr forced_trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, nullptr); - forced_trial->AppendGroup(kExtraGroupName, 0); - forced_trial->SetForced(); - - int default_group = -1; - scoped_refptr client_trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); - const int extra_group = client_trial->AppendGroup(kExtraGroupName, 100); - EXPECT_NE(default_group, extra_group); - - EXPECT_FALSE(client_trial->group_reported_); - EXPECT_EQ(default_group, client_trial->group()); - EXPECT_TRUE(client_trial->group_reported_); - EXPECT_EQ(kDefaultGroupName, client_trial->group_name()); -} - -TEST_F(FieldTrialTest, SetForcedChangeDefault_Default) { - const char kTrialName[] = "SetForcedDefaultGroupChange"; - const char kGroupAName[] = "A"; - const char kGroupBName[] = "B"; - ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); - - // Simulate a server-side (forced) config that switches which group is default - // and ensures that the non-forced code receives the correct group numbers. - scoped_refptr forced_trial = - CreateFieldTrial(kTrialName, 100, kGroupAName, nullptr); - forced_trial->AppendGroup(kGroupBName, 100); - forced_trial->SetForced(); - - int default_group = -1; - scoped_refptr client_trial = - CreateFieldTrial(kTrialName, 100, kGroupBName, &default_group); - const int extra_group = client_trial->AppendGroup(kGroupAName, 50); - EXPECT_NE(default_group, extra_group); - - EXPECT_FALSE(client_trial->group_reported_); - EXPECT_EQ(default_group, client_trial->group()); - EXPECT_TRUE(client_trial->group_reported_); - EXPECT_EQ(kGroupBName, client_trial->group_name()); -} - -TEST_F(FieldTrialTest, SetForcedChangeDefault_NonDefault) { - const char kTrialName[] = "SetForcedDefaultGroupChange"; - const char kGroupAName[] = "A"; - const char kGroupBName[] = "B"; - ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); - - // Simulate a server-side (forced) config that switches which group is default - // and ensures that the non-forced code receives the correct group numbers. - scoped_refptr forced_trial = - CreateFieldTrial(kTrialName, 100, kGroupAName, nullptr); - forced_trial->AppendGroup(kGroupBName, 0); - forced_trial->SetForced(); - - int default_group = -1; - scoped_refptr client_trial = - CreateFieldTrial(kTrialName, 100, kGroupBName, &default_group); - const int extra_group = client_trial->AppendGroup(kGroupAName, 50); - EXPECT_NE(default_group, extra_group); - - EXPECT_FALSE(client_trial->group_reported_); - EXPECT_EQ(extra_group, client_trial->group()); - EXPECT_TRUE(client_trial->group_reported_); - EXPECT_EQ(kGroupAName, client_trial->group_name()); -} - -TEST_F(FieldTrialTest, Observe) { - const char kTrialName[] = "TrialToObserve1"; - const char kSecondaryGroupName[] = "SecondaryGroup"; - - TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS); - int default_group = -1; - scoped_refptr trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); - const int secondary_group = trial->AppendGroup(kSecondaryGroupName, 50); - const int chosen_group = trial->group(); - EXPECT_TRUE(chosen_group == default_group || chosen_group == secondary_group); - - // Observers are called asynchronously. - EXPECT_TRUE(observer.trial_name().empty()); - EXPECT_TRUE(observer.group_name().empty()); - RunLoop().RunUntilIdle(); - - EXPECT_EQ(kTrialName, observer.trial_name()); - if (chosen_group == default_group) - EXPECT_EQ(kDefaultGroupName, observer.group_name()); - else - EXPECT_EQ(kSecondaryGroupName, observer.group_name()); -} - -TEST_F(FieldTrialTest, SynchronousObserver) { - const char kTrialName[] = "TrialToObserve1"; - const char kSecondaryGroupName[] = "SecondaryGroup"; - - TestFieldTrialObserver observer(TestFieldTrialObserver::SYNCHRONOUS); - int default_group = -1; - scoped_refptr trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); - const int secondary_group = trial->AppendGroup(kSecondaryGroupName, 50); - const int chosen_group = trial->group(); - EXPECT_TRUE(chosen_group == default_group || chosen_group == secondary_group); - - // The observer should be notified synchronously by the group() call. - EXPECT_EQ(kTrialName, observer.trial_name()); - if (chosen_group == default_group) - EXPECT_EQ(kDefaultGroupName, observer.group_name()); - else - EXPECT_EQ(kSecondaryGroupName, observer.group_name()); -} - -TEST_F(FieldTrialTest, ObserveDisabled) { - const char kTrialName[] = "TrialToObserve2"; - - TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS); - int default_group = -1; - scoped_refptr trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); - trial->AppendGroup("A", 25); - trial->AppendGroup("B", 25); - trial->AppendGroup("C", 25); - trial->Disable(); - - // Observer shouldn't be notified of a disabled trial. - RunLoop().RunUntilIdle(); - EXPECT_TRUE(observer.trial_name().empty()); - EXPECT_TRUE(observer.group_name().empty()); - - // Observer shouldn't be notified even after a |group()| call. - EXPECT_EQ(default_group, trial->group()); - RunLoop().RunUntilIdle(); - EXPECT_TRUE(observer.trial_name().empty()); - EXPECT_TRUE(observer.group_name().empty()); -} - -TEST_F(FieldTrialTest, ObserveForcedDisabled) { - const char kTrialName[] = "TrialToObserve3"; - - TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS); - int default_group = -1; - scoped_refptr trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); - trial->AppendGroup("A", 25); - trial->AppendGroup("B", 25); - trial->AppendGroup("C", 25); - trial->SetForced(); - trial->Disable(); - - // Observer shouldn't be notified of a disabled trial, even when forced. - RunLoop().RunUntilIdle(); - EXPECT_TRUE(observer.trial_name().empty()); - EXPECT_TRUE(observer.group_name().empty()); - - // Observer shouldn't be notified even after a |group()| call. - EXPECT_EQ(default_group, trial->group()); - RunLoop().RunUntilIdle(); - EXPECT_TRUE(observer.trial_name().empty()); - EXPECT_TRUE(observer.group_name().empty()); -} - -TEST_F(FieldTrialTest, DisabledTrialNotActive) { - const char kTrialName[] = "DisabledTrial"; - ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); - - scoped_refptr trial = - CreateFieldTrial(kTrialName, 100, kDefaultGroupName, nullptr); - trial->AppendGroup("X", 50); - trial->Disable(); - - // Ensure the trial is not listed as active. - FieldTrial::ActiveGroups active_groups; - FieldTrialList::GetActiveFieldTrialGroups(&active_groups); - EXPECT_TRUE(active_groups.empty()); - - // Ensure the trial is not listed in the |StatesToString()| result. - std::string states; - FieldTrialList::StatesToString(&states); - EXPECT_TRUE(states.empty()); -} - -TEST_F(FieldTrialTest, ExpirationYearNotExpired) { - const char kTrialName[] = "NotExpired"; - const char kGroupName[] = "Group2"; - const int kProbability = 100; - ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); - - scoped_refptr trial = - CreateFieldTrial(kTrialName, kProbability, kDefaultGroupName, nullptr); - trial->AppendGroup(kGroupName, kProbability); - EXPECT_EQ(kGroupName, trial->group_name()); -} - -TEST_F(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes) { - const int kBucketCount = 100; - - // Try each boundary value |i / 100.0| as the entropy value. - for (int i = 0; i < kBucketCount; ++i) { - const double entropy = i / static_cast(kBucketCount); - - scoped_refptr trial( - new FieldTrial("test", kBucketCount, "default", entropy)); - for (int j = 0; j < kBucketCount; ++j) - trial->AppendGroup(IntToString(j), 1); - - EXPECT_EQ(IntToString(i), trial->group_name()); - } -} - -TEST_F(FieldTrialTest, DoesNotSurpassTotalProbability) { - const double kEntropyValue = 1.0 - 1e-9; - ASSERT_LT(kEntropyValue, 1.0); - - scoped_refptr trial( - new FieldTrial("test", 2, "default", kEntropyValue)); - trial->AppendGroup("1", 1); - trial->AppendGroup("2", 1); - - EXPECT_EQ("2", trial->group_name()); -} - -TEST_F(FieldTrialTest, CreateSimulatedFieldTrial) { - const char kTrialName[] = "CreateSimulatedFieldTrial"; - ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); - - // Different cases to test, e.g. default vs. non default group being chosen. - struct { - double entropy_value; - const char* expected_group; - } test_cases[] = { - { 0.4, "A" }, - { 0.85, "B" }, - { 0.95, kDefaultGroupName }, - }; - - for (size_t i = 0; i < arraysize(test_cases); ++i) { - TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS); - scoped_refptr trial( - FieldTrial::CreateSimulatedFieldTrial(kTrialName, 100, kDefaultGroupName, - test_cases[i].entropy_value)); - trial->AppendGroup("A", 80); - trial->AppendGroup("B", 10); - EXPECT_EQ(test_cases[i].expected_group, trial->group_name()); - - // Field trial shouldn't have been registered with the list. - EXPECT_FALSE(FieldTrialList::TrialExists(kTrialName)); - EXPECT_EQ(0u, FieldTrialList::GetFieldTrialCount()); - - // Observer shouldn't have been notified. - RunLoop().RunUntilIdle(); - EXPECT_TRUE(observer.trial_name().empty()); - - // The trial shouldn't be in the active set of trials. - FieldTrial::ActiveGroups active_groups; - FieldTrialList::GetActiveFieldTrialGroups(&active_groups); - EXPECT_TRUE(active_groups.empty()); - - // The trial shouldn't be listed in the |StatesToString()| result. - std::string states; - FieldTrialList::StatesToString(&states); - EXPECT_TRUE(states.empty()); - } -} - -TEST(FieldTrialTestWithoutList, StatesStringFormat) { - std::string save_string; - - // Scoping the first FieldTrialList, as we need another one to test the - // importing function. - { - FieldTrialList field_trial_list(nullptr); - scoped_refptr trial = - CreateFieldTrial("Abc", 10, "Default some name", nullptr); - trial->AppendGroup("cba", 10); - trial->group(); - scoped_refptr trial2 = - CreateFieldTrial("Xyz", 10, "Default xxx", nullptr); - trial2->AppendGroup("zyx", 10); - trial2->group(); - scoped_refptr trial3 = - CreateFieldTrial("zzz", 10, "default", nullptr); - - FieldTrialList::AllStatesToString(&save_string, false); - } - - // Starting with a new blank FieldTrialList. - FieldTrialList field_trial_list(nullptr); - ASSERT_TRUE(field_trial_list.CreateTrialsFromString(save_string, - std::set())); - - FieldTrial::ActiveGroups active_groups; - field_trial_list.GetActiveFieldTrialGroups(&active_groups); - ASSERT_EQ(2U, active_groups.size()); - EXPECT_EQ("Abc", active_groups[0].trial_name); - EXPECT_EQ("cba", active_groups[0].group_name); - EXPECT_EQ("Xyz", active_groups[1].trial_name); - EXPECT_EQ("zyx", active_groups[1].group_name); - EXPECT_TRUE(field_trial_list.TrialExists("zzz")); -} - -TEST(FieldTrialDeathTest, OneTimeRandomizedTrialWithoutFieldTrialList) { - // Trying to instantiate a one-time randomized field trial before the - // FieldTrialList is created should crash. - EXPECT_DEATH_IF_SUPPORTED( - FieldTrialList::FactoryGetFieldTrial( - "OneTimeRandomizedTrialWithoutFieldTrialList", 100, kDefaultGroupName, - FieldTrialList::kNoExpirationYear, 1, 1, - FieldTrial::ONE_TIME_RANDOMIZED, nullptr), - ""); -} - -#if defined(OS_FUCHSIA) -// TODO(crbug.com/752368): This is flaky on Fuchsia. -#define MAYBE_TestCopyFieldTrialStateToFlags \ - DISABLED_TestCopyFieldTrialStateToFlags -#else -#define MAYBE_TestCopyFieldTrialStateToFlags TestCopyFieldTrialStateToFlags -#endif -TEST(FieldTrialListTest, MAYBE_TestCopyFieldTrialStateToFlags) { - constexpr char kFieldTrialHandleSwitch[] = "test-field-trial-handle"; - constexpr char kEnableFeaturesSwitch[] = "test-enable-features"; - constexpr char kDisableFeaturesSwitch[] = "test-disable-features"; - - FieldTrialList field_trial_list(std::make_unique()); - - std::unique_ptr feature_list(new FeatureList); - feature_list->InitializeFromCommandLine("A,B", "C"); - - FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial1", "Group1"); - feature_list->RegisterFieldTrialOverride( - "MyFeature", FeatureList::OVERRIDE_ENABLE_FEATURE, trial); - - test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitWithFeatureList(std::move(feature_list)); - - FilePath test_file_path = FilePath(FILE_PATH_LITERAL("Program")); - CommandLine command_line = CommandLine(test_file_path); - - FieldTrialList::CopyFieldTrialStateToFlags( - kFieldTrialHandleSwitch, kEnableFeaturesSwitch, kDisableFeaturesSwitch, - &command_line); - EXPECT_TRUE(command_line.HasSwitch(kFieldTrialHandleSwitch)); - - // Explictly specified enabled/disabled features should be specified. - EXPECT_EQ("A,B", command_line.GetSwitchValueASCII(kEnableFeaturesSwitch)); - EXPECT_EQ("C", command_line.GetSwitchValueASCII(kDisableFeaturesSwitch)); -} - -TEST(FieldTrialListTest, InstantiateAllocator) { - test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.Init(); - - FieldTrialList field_trial_list(nullptr); - FieldTrialList::CreateFieldTrial("Trial1", "Group1"); - - FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); - void* memory = field_trial_list.field_trial_allocator_->shared_memory(); - size_t used = field_trial_list.field_trial_allocator_->used(); - - // Ensure that the function is idempotent. - FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); - void* new_memory = field_trial_list.field_trial_allocator_->shared_memory(); - size_t new_used = field_trial_list.field_trial_allocator_->used(); - EXPECT_EQ(memory, new_memory); - EXPECT_EQ(used, new_used); -} - -TEST(FieldTrialListTest, AddTrialsToAllocator) { - std::string save_string; - SharedMemoryHandle handle; - - // Scoping the first FieldTrialList, as we need another one to test that it - // matches. - { - test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.Init(); - - FieldTrialList field_trial_list(nullptr); - FieldTrialList::CreateFieldTrial("Trial1", "Group1"); - FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); - FieldTrialList::AllStatesToString(&save_string, false); - handle = SharedMemory::DuplicateHandle( - field_trial_list.field_trial_allocator_->shared_memory()->handle()); - } - - FieldTrialList field_trial_list2(nullptr); - std::unique_ptr shm(new SharedMemory(handle, true)); - // 4 KiB is enough to hold the trials only created for this test. - shm.get()->Map(4 << 10); - FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); - std::string check_string; - FieldTrialList::AllStatesToString(&check_string, false); - EXPECT_EQ(save_string, check_string); -} - -TEST(FieldTrialListTest, DoNotAddSimulatedFieldTrialsToAllocator) { - constexpr char kTrialName[] = "trial"; - SharedMemoryHandle handle; - { - test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.Init(); - - // Create a simulated trial and a real trial and call group() on them, which - // should only add the real trial to the field trial allocator. - FieldTrialList field_trial_list(nullptr); - FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); - - // This shouldn't add to the allocator. - scoped_refptr simulated_trial = - FieldTrial::CreateSimulatedFieldTrial(kTrialName, 100, "Simulated", - 0.95); - simulated_trial->group(); - - // This should add to the allocator. - FieldTrial* real_trial = - FieldTrialList::CreateFieldTrial(kTrialName, "Real"); - real_trial->group(); - - handle = SharedMemory::DuplicateHandle( - field_trial_list.field_trial_allocator_->shared_memory()->handle()); - } - - // Check that there's only one entry in the allocator. - FieldTrialList field_trial_list2(nullptr); - std::unique_ptr shm(new SharedMemory(handle, true)); - // 4 KiB is enough to hold the trials only created for this test. - shm.get()->Map(4 << 10); - FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); - std::string check_string; - FieldTrialList::AllStatesToString(&check_string, false); - ASSERT_EQ(check_string.find("Simulated"), std::string::npos); -} - -TEST(FieldTrialListTest, AssociateFieldTrialParams) { - test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.Init(); - - std::string trial_name("Trial1"); - std::string group_name("Group1"); - - // Create a field trial with some params. - FieldTrialList field_trial_list(nullptr); - FieldTrialList::CreateFieldTrial(trial_name, group_name); - std::map params; - params["key1"] = "value1"; - params["key2"] = "value2"; - FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( - trial_name, group_name, params); - FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); - - // Clear all cached params from the associator. - FieldTrialParamAssociator::GetInstance()->ClearAllCachedParamsForTesting(); - // Check that the params have been cleared from the cache. - std::map cached_params; - FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback( - trial_name, group_name, &cached_params); - EXPECT_EQ(0U, cached_params.size()); - - // Check that we fetch the param from shared memory properly. - std::map new_params; - FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial_name, - &new_params); - EXPECT_EQ("value1", new_params["key1"]); - EXPECT_EQ("value2", new_params["key2"]); - EXPECT_EQ(2U, new_params.size()); -} - -#if defined(OS_FUCHSIA) -// TODO(crbug.com/752368): This is flaky on Fuchsia. -#define MAYBE_ClearParamsFromSharedMemory DISABLED_ClearParamsFromSharedMemory -#else -#define MAYBE_ClearParamsFromSharedMemory ClearParamsFromSharedMemory -#endif -TEST(FieldTrialListTest, MAYBE_ClearParamsFromSharedMemory) { - std::string trial_name("Trial1"); - std::string group_name("Group1"); - - SharedMemoryHandle handle; - { - test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.Init(); - - // Create a field trial with some params. - FieldTrialList field_trial_list(nullptr); - FieldTrial* trial = - FieldTrialList::CreateFieldTrial(trial_name, group_name); - std::map params; - params["key1"] = "value1"; - params["key2"] = "value2"; - FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( - trial_name, group_name, params); - FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); - - // Clear all params from the associator AND shared memory. The allocated - // segments should be different. - FieldTrial::FieldTrialRef old_ref = trial->ref_; - FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting(); - FieldTrial::FieldTrialRef new_ref = trial->ref_; - EXPECT_NE(old_ref, new_ref); - - // Check that there are no params associated with the field trial anymore. - std::map new_params; - FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial_name, - &new_params); - EXPECT_EQ(0U, new_params.size()); - - // Now duplicate the handle so we can easily check that the trial is still - // in shared memory via AllStatesToString. - handle = SharedMemory::DuplicateHandle( - field_trial_list.field_trial_allocator_->shared_memory()->handle()); - } - - // Check that we have the trial. - FieldTrialList field_trial_list2(nullptr); - std::unique_ptr shm(new SharedMemory(handle, true)); - // 4 KiB is enough to hold the trials only created for this test. - shm.get()->Map(4 << 10); - FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); - std::string check_string; - FieldTrialList::AllStatesToString(&check_string, false); - EXPECT_EQ("*Trial1/Group1/", check_string); -} - -TEST(FieldTrialListTest, DumpAndFetchFromSharedMemory) { - std::string trial_name("Trial1"); - std::string group_name("Group1"); - - // Create a field trial with some params. - FieldTrialList field_trial_list(nullptr); - FieldTrialList::CreateFieldTrial(trial_name, group_name); - std::map params; - params["key1"] = "value1"; - params["key2"] = "value2"; - FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( - trial_name, group_name, params); - - std::unique_ptr shm(new SharedMemory()); - // 4 KiB is enough to hold the trials only created for this test. - shm.get()->CreateAndMapAnonymous(4 << 10); - // We _could_ use PersistentMemoryAllocator, this just has less params. - SharedPersistentMemoryAllocator allocator(std::move(shm), 1, "", false); - - // Dump and subsequently retrieve the field trial to |allocator|. - FieldTrialList::DumpAllFieldTrialsToPersistentAllocator(&allocator); - std::vector entries = - FieldTrialList::GetAllFieldTrialsFromPersistentAllocator(allocator); - - // Check that we have the entry we put in. - EXPECT_EQ(1u, entries.size()); - const FieldTrial::FieldTrialEntry* entry = entries[0]; - - // Check that the trial and group names match. - StringPiece shm_trial_name; - StringPiece shm_group_name; - entry->GetTrialAndGroupName(&shm_trial_name, &shm_group_name); - EXPECT_EQ(trial_name, shm_trial_name); - EXPECT_EQ(group_name, shm_group_name); - - // Check that the params match. - std::map shm_params; - entry->GetParams(&shm_params); - EXPECT_EQ(2u, shm_params.size()); - EXPECT_EQ("value1", shm_params["key1"]); - EXPECT_EQ("value2", shm_params["key2"]); -} - -#if !defined(OS_NACL) -TEST(FieldTrialListTest, SerializeSharedMemoryHandleMetadata) { - std::unique_ptr shm(new SharedMemory()); - shm->CreateAndMapAnonymous(4 << 10); - - std::string serialized = - FieldTrialList::SerializeSharedMemoryHandleMetadata(shm->handle()); -#if defined(OS_WIN) || defined(OS_FUCHSIA) - SharedMemoryHandle deserialized = - FieldTrialList::DeserializeSharedMemoryHandleMetadata(serialized); -#else - // Use a valid-looking arbitrary number for the file descriptor. It's not - // being used in this unittest, but needs to pass sanity checks in the - // handle's constructor. - SharedMemoryHandle deserialized = - FieldTrialList::DeserializeSharedMemoryHandleMetadata(42, serialized); -#endif - EXPECT_EQ(deserialized.GetGUID(), shm->handle().GetGUID()); - EXPECT_FALSE(deserialized.GetGUID().is_empty()); -} -#endif // !defined(OS_NACL) - -// Verify that the field trial shared memory handle is really read-only, and -// does not allow writable mappings. Test disabled on NaCl, Windows and Fuchsia -// which don't support/implement GetFieldTrialHandle(). For Fuchsia, see -// crbug.com/752368 -#if !defined(OS_NACL) && !defined(OS_WIN) && !defined(OS_FUCHSIA) -TEST(FieldTrialListTest, CheckReadOnlySharedMemoryHandle) { - FieldTrialList field_trial_list(nullptr); - FieldTrialList::CreateFieldTrial("Trial1", "Group1"); - - test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.Init(); - - FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); - - SharedMemoryHandle handle = FieldTrialList::GetFieldTrialHandle(); - ASSERT_TRUE(handle.IsValid()); - - ASSERT_TRUE(CheckReadOnlySharedMemoryHandleForTesting(handle)); -} -#endif // !OS_NACL && !OS_WIN && !OS_FUCHSIA - -TEST_F(FieldTrialTest, TestAllParamsToString) { - std::string exptected_output = "t1.g1:p1/v1/p2/v2"; - - // Create study with one group and two params. - std::map params; - params["p1"] = "v1"; - params["p2"] = "v2"; - FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( - "t1", "g1", params); - EXPECT_EQ( - "", FieldTrialList::AllParamsToString(false, &MockEscapeQueryParamValue)); - - scoped_refptr trial1 = - CreateFieldTrial("t1", 100, "Default", nullptr); - trial1->AppendGroup("g1", 100); - trial1->group(); - EXPECT_EQ(exptected_output, FieldTrialList::AllParamsToString( - false, &MockEscapeQueryParamValue)); - - // Create study with two groups and params that don't belog to the assigned - // group. This should be in the output. - FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( - "t2", "g2", params); - scoped_refptr trial2 = - CreateFieldTrial("t2", 100, "Default", nullptr); - trial2->AppendGroup("g1", 100); - trial2->AppendGroup("g2", 0); - trial2->group(); - EXPECT_EQ(exptected_output, FieldTrialList::AllParamsToString( - false, &MockEscapeQueryParamValue)); -} - -} // namespace base diff --git a/metrics/histogram.cc b/metrics/histogram.cc deleted file mode 100644 index 2ea1f884c..000000000 --- a/metrics/histogram.cc +++ /dev/null @@ -1,1350 +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. - -// Histogram is an object that aggregates statistics, and can summarize them in -// various forms, including ASCII graphical, HTML, and numerically (as a -// vector of numbers corresponding to each of the aggregating buckets). -// See header file for details and examples. - -#include "base/metrics/histogram.h" - -#include -#include -#include - -#include -#include -#include - -#include "base/compiler_specific.h" -#include "base/debug/alias.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/dummy_histogram.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/metrics_hashes.h" -#include "base/metrics/persistent_histogram_allocator.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/metrics/sample_vector.h" -#include "base/metrics/statistics_recorder.h" -#include "base/pickle.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/lock.h" -#include "base/values.h" -#include "build/build_config.h" - -namespace base { - -namespace { - -bool ReadHistogramArguments(PickleIterator* iter, - std::string* histogram_name, - int* flags, - int* declared_min, - int* declared_max, - uint32_t* bucket_count, - uint32_t* range_checksum) { - if (!iter->ReadString(histogram_name) || - !iter->ReadInt(flags) || - !iter->ReadInt(declared_min) || - !iter->ReadInt(declared_max) || - !iter->ReadUInt32(bucket_count) || - !iter->ReadUInt32(range_checksum)) { - DLOG(ERROR) << "Pickle error decoding Histogram: " << *histogram_name; - return false; - } - - // Since these fields may have come from an untrusted renderer, do additional - // checks above and beyond those in Histogram::Initialize() - if (*declared_max <= 0 || - *declared_min <= 0 || - *declared_max < *declared_min || - INT_MAX / sizeof(HistogramBase::Count) <= *bucket_count || - *bucket_count < 2) { - DLOG(ERROR) << "Values error decoding Histogram: " << histogram_name; - return false; - } - - // We use the arguments to find or create the local version of the histogram - // in this process, so we need to clear any IPC flag. - *flags &= ~HistogramBase::kIPCSerializationSourceFlag; - - return true; -} - -bool ValidateRangeChecksum(const HistogramBase& histogram, - uint32_t range_checksum) { - // Normally, |histogram| should have type HISTOGRAM or be inherited from it. - // However, if it's expired, it will actually be a DUMMY_HISTOGRAM. - // Skip the checks in that case. - if (histogram.GetHistogramType() == DUMMY_HISTOGRAM) - return true; - const Histogram& casted_histogram = - static_cast(histogram); - - return casted_histogram.bucket_ranges()->checksum() == range_checksum; -} - -} // namespace - -typedef HistogramBase::Count Count; -typedef HistogramBase::Sample Sample; - -// static -const uint32_t Histogram::kBucketCount_MAX = 16384u; - -class Histogram::Factory { - public: - Factory(const std::string& name, - HistogramBase::Sample minimum, - HistogramBase::Sample maximum, - uint32_t bucket_count, - int32_t flags) - : Factory(name, HISTOGRAM, minimum, maximum, bucket_count, flags) {} - - // Create histogram based on construction parameters. Caller takes - // ownership of the returned object. - HistogramBase* Build(); - - protected: - Factory(const std::string& name, - HistogramType histogram_type, - HistogramBase::Sample minimum, - HistogramBase::Sample maximum, - uint32_t bucket_count, - int32_t flags) - : name_(name), - histogram_type_(histogram_type), - minimum_(minimum), - maximum_(maximum), - bucket_count_(bucket_count), - flags_(flags) {} - - // Create a BucketRanges structure appropriate for this histogram. - virtual BucketRanges* CreateRanges() { - BucketRanges* ranges = new BucketRanges(bucket_count_ + 1); - Histogram::InitializeBucketRanges(minimum_, maximum_, ranges); - return ranges; - } - - // Allocate the correct Histogram object off the heap (in case persistent - // memory is not available). - virtual std::unique_ptr HeapAlloc(const BucketRanges* ranges) { - return WrapUnique( - new Histogram(GetPermanentName(name_), minimum_, maximum_, ranges)); - } - - // Perform any required datafill on the just-created histogram. If - // overridden, be sure to call the "super" version -- this method may not - // always remain empty. - virtual void FillHistogram(HistogramBase* histogram) {} - - // These values are protected (instead of private) because they need to - // be accessible to methods of sub-classes in order to avoid passing - // unnecessary parameters everywhere. - const std::string& name_; - const HistogramType histogram_type_; - HistogramBase::Sample minimum_; - HistogramBase::Sample maximum_; - uint32_t bucket_count_; - int32_t flags_; - - private: - DISALLOW_COPY_AND_ASSIGN(Factory); -}; - -HistogramBase* Histogram::Factory::Build() { - HistogramBase* histogram = StatisticsRecorder::FindHistogram(name_); - if (!histogram) { - // TODO(gayane): |HashMetricName()| is called again in Histogram - // constructor. Refactor code to avoid the additional call. - bool should_record = - StatisticsRecorder::ShouldRecordHistogram(HashMetricName(name_)); - if (!should_record) - return DummyHistogram::GetInstance(); - // To avoid racy destruction at shutdown, the following will be leaked. - const BucketRanges* created_ranges = CreateRanges(); - const BucketRanges* registered_ranges = - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(created_ranges); - - // In most cases, the bucket-count, minimum, and maximum values are known - // when the code is written and so are passed in explicitly. In other - // cases (such as with a CustomHistogram), they are calculated dynamically - // at run-time. In the latter case, those ctor parameters are zero and - // the results extracted from the result of CreateRanges(). - if (bucket_count_ == 0) { - bucket_count_ = static_cast(registered_ranges->bucket_count()); - minimum_ = registered_ranges->range(1); - maximum_ = registered_ranges->range(bucket_count_ - 1); - } - DCHECK_EQ(minimum_, registered_ranges->range(1)); - DCHECK_EQ(maximum_, registered_ranges->range(bucket_count_ - 1)); - - // Try to create the histogram using a "persistent" allocator. As of - // 2016-02-25, the availability of such is controlled by a base::Feature - // that is off by default. If the allocator doesn't exist or if - // allocating from it fails, code below will allocate the histogram from - // the process heap. - PersistentHistogramAllocator::Reference histogram_ref = 0; - std::unique_ptr tentative_histogram; - PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get(); - if (allocator) { - tentative_histogram = allocator->AllocateHistogram( - histogram_type_, - name_, - minimum_, - maximum_, - registered_ranges, - flags_, - &histogram_ref); - } - - // Handle the case where no persistent allocator is present or the - // persistent allocation fails (perhaps because it is full). - if (!tentative_histogram) { - DCHECK(!histogram_ref); // Should never have been set. - DCHECK(!allocator); // Shouldn't have failed. - flags_ &= ~HistogramBase::kIsPersistent; - tentative_histogram = HeapAlloc(registered_ranges); - tentative_histogram->SetFlags(flags_); - } - - FillHistogram(tentative_histogram.get()); - - // Register this histogram with the StatisticsRecorder. Keep a copy of - // the pointer value to tell later whether the locally created histogram - // was registered or deleted. The type is "void" because it could point - // to released memory after the following line. - const void* tentative_histogram_ptr = tentative_histogram.get(); - histogram = StatisticsRecorder::RegisterOrDeleteDuplicate( - tentative_histogram.release()); - - // Persistent histograms need some follow-up processing. - if (histogram_ref) { - allocator->FinalizeHistogram(histogram_ref, - histogram == tentative_histogram_ptr); - } - } - - if (histogram_type_ != histogram->GetHistogramType() || - (bucket_count_ != 0 && !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. A NULL - // return would cause Chrome to crash; better to just record it for later - // analysis. - UmaHistogramSparse("Histogram.MismatchedConstructionArguments", - static_cast(HashMetricName(name_))); - DLOG(ERROR) << "Histogram " << name_ - << " has mismatched construction arguments"; - return DummyHistogram::GetInstance(); - } - return histogram; -} - -HistogramBase* Histogram::FactoryGet(const std::string& name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t flags) { - bool valid_arguments = - InspectConstructionArguments(name, &minimum, &maximum, &bucket_count); - DCHECK(valid_arguments); - - return Factory(name, minimum, maximum, bucket_count, flags).Build(); -} - -HistogramBase* Histogram::FactoryTimeGet(const std::string& name, - TimeDelta minimum, - TimeDelta maximum, - uint32_t bucket_count, - int32_t flags) { - return FactoryGet(name, static_cast(minimum.InMilliseconds()), - static_cast(maximum.InMilliseconds()), bucket_count, - flags); -} - -HistogramBase* Histogram::FactoryMicrosecondsTimeGet(const std::string& name, - TimeDelta minimum, - TimeDelta maximum, - uint32_t bucket_count, - int32_t flags) { - return FactoryGet(name, static_cast(minimum.InMicroseconds()), - static_cast(maximum.InMicroseconds()), bucket_count, - flags); -} - -HistogramBase* Histogram::FactoryGet(const char* name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t flags) { - return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags); -} - -HistogramBase* Histogram::FactoryTimeGet(const char* name, - TimeDelta minimum, - TimeDelta maximum, - uint32_t bucket_count, - int32_t flags) { - return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count, - flags); -} - -HistogramBase* Histogram::FactoryMicrosecondsTimeGet(const char* name, - TimeDelta minimum, - TimeDelta maximum, - uint32_t bucket_count, - int32_t flags) { - return FactoryMicrosecondsTimeGet(std::string(name), minimum, maximum, - bucket_count, flags); -} - -std::unique_ptr Histogram::PersistentCreate( - const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta) { - return WrapUnique(new Histogram(name, minimum, maximum, ranges, counts, - logged_counts, meta, logged_meta)); -} - -// Calculate what range of values are held in each bucket. -// We have to be careful that we don't pick a ratio between starting points in -// consecutive buckets that is sooo small, that the integer bounds are the same -// (effectively making one bucket get no values). We need to avoid: -// ranges(i) == ranges(i + 1) -// To avoid that, we just do a fine-grained bucket width as far as we need to -// until we get a ratio that moves us along at least 2 units at a time. From -// that bucket onward we do use the exponential growth of buckets. -// -// static -void Histogram::InitializeBucketRanges(Sample minimum, - Sample maximum, - BucketRanges* ranges) { - double log_max = log(static_cast(maximum)); - double log_ratio; - double log_next; - size_t bucket_index = 1; - Sample current = minimum; - ranges->set_range(bucket_index, current); - size_t bucket_count = ranges->bucket_count(); - while (bucket_count > ++bucket_index) { - double log_current; - log_current = log(static_cast(current)); - // Calculate the count'th root of the range. - log_ratio = (log_max - log_current) / (bucket_count - bucket_index); - // See where the next bucket would start. - log_next = log_current + log_ratio; - Sample next; - next = static_cast(std::round(exp(log_next))); - if (next > current) - current = next; - else - ++current; // Just do a narrow bucket, and keep trying. - ranges->set_range(bucket_index, current); - } - ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX); - ranges->ResetChecksum(); -} - -// static -const int Histogram::kCommonRaceBasedCountMismatch = 5; - -uint32_t Histogram::FindCorruption(const HistogramSamples& samples) const { - int inconsistencies = NO_INCONSISTENCIES; - Sample previous_range = -1; // Bottom range is always 0. - for (uint32_t index = 0; index < bucket_count(); ++index) { - int new_range = ranges(index); - if (previous_range >= new_range) - inconsistencies |= BUCKET_ORDER_ERROR; - previous_range = new_range; - } - - if (!bucket_ranges()->HasValidChecksum()) - inconsistencies |= RANGE_CHECKSUM_ERROR; - - int64_t delta64 = samples.redundant_count() - samples.TotalCount(); - if (delta64 != 0) { - int delta = static_cast(delta64); - if (delta != delta64) - delta = INT_MAX; // Flag all giant errors as INT_MAX. - if (delta > 0) { - if (delta > kCommonRaceBasedCountMismatch) - inconsistencies |= COUNT_HIGH_ERROR; - } else { - DCHECK_GT(0, delta); - if (-delta > kCommonRaceBasedCountMismatch) - inconsistencies |= COUNT_LOW_ERROR; - } - } - return inconsistencies; -} - -const BucketRanges* Histogram::bucket_ranges() const { - return unlogged_samples_->bucket_ranges(); -} - -Sample Histogram::declared_min() const { - const BucketRanges* ranges = bucket_ranges(); - if (ranges->bucket_count() < 2) - return -1; - return ranges->range(1); -} - -Sample Histogram::declared_max() const { - const BucketRanges* ranges = bucket_ranges(); - if (ranges->bucket_count() < 2) - return -1; - return ranges->range(ranges->bucket_count() - 1); -} - -Sample Histogram::ranges(uint32_t i) const { - return bucket_ranges()->range(i); -} - -uint32_t Histogram::bucket_count() const { - return static_cast(bucket_ranges()->bucket_count()); -} - -// static -bool Histogram::InspectConstructionArguments(StringPiece name, - Sample* minimum, - Sample* maximum, - uint32_t* bucket_count) { - // Defensive code for backward compatibility. - if (*minimum < 1) { - DVLOG(1) << "Histogram: " << name << " has bad minimum: " << *minimum; - *minimum = 1; - } - if (*maximum >= kSampleType_MAX) { - DVLOG(1) << "Histogram: " << name << " has bad maximum: " << *maximum; - *maximum = kSampleType_MAX - 1; - } - if (*bucket_count >= kBucketCount_MAX) { - DVLOG(1) << "Histogram: " << name << " has bad bucket_count: " - << *bucket_count; - *bucket_count = kBucketCount_MAX - 1; - } - - bool check_okay = true; - - if (*minimum > *maximum) { - check_okay = false; - std::swap(*minimum, *maximum); - } - if (*maximum == *minimum) { - check_okay = false; - *maximum = *minimum + 1; - } - if (*bucket_count < 3) { - check_okay = false; - *bucket_count = 3; - } - // Very high bucket counts are wasteful. Use a sparse histogram instead. - // Value of 10002 equals a user-supplied value of 10k + 2 overflow buckets. - constexpr uint32_t kMaxBucketCount = 10002; - if (*bucket_count > kMaxBucketCount) { - check_okay = false; - *bucket_count = kMaxBucketCount; - } - if (*bucket_count > static_cast(*maximum - *minimum + 2)) { - check_okay = false; - *bucket_count = static_cast(*maximum - *minimum + 2); - } - - if (!check_okay) { - UmaHistogramSparse("Histogram.BadConstructionArguments", - static_cast(HashMetricName(name))); - } - - return check_okay; -} - -uint64_t Histogram::name_hash() const { - return unlogged_samples_->id(); -} - -HistogramType Histogram::GetHistogramType() const { - return HISTOGRAM; -} - -bool Histogram::HasConstructionArguments(Sample expected_minimum, - Sample expected_maximum, - uint32_t expected_bucket_count) const { - return (expected_bucket_count == bucket_count() && - expected_minimum == declared_min() && - expected_maximum == declared_max()); -} - -void Histogram::Add(int value) { - AddCount(value, 1); -} - -void Histogram::AddCount(int value, int count) { - DCHECK_EQ(0, ranges(0)); - DCHECK_EQ(kSampleType_MAX, ranges(bucket_count())); - - if (value > kSampleType_MAX - 1) - value = kSampleType_MAX - 1; - if (value < 0) - value = 0; - if (count <= 0) { - NOTREACHED(); - return; - } - unlogged_samples_->Accumulate(value, count); - - FindAndRunCallback(value); -} - -std::unique_ptr Histogram::SnapshotSamples() const { - return SnapshotAllSamples(); -} - -std::unique_ptr Histogram::SnapshotDelta() { -#if DCHECK_IS_ON() - DCHECK(!final_delta_created_); -#endif - - // The code below has subtle thread-safety guarantees! All changes to - // the underlying SampleVectors use atomic integer operations, which guarantee - // eventual consistency, but do not guarantee full synchronization between - // different entries in the SampleVector. In particular, this means that - // concurrent updates to the histogram might result in the reported sum not - // matching the individual bucket counts; or there being some buckets that are - // logically updated "together", but end up being only partially updated when - // a snapshot is captured. Note that this is why it's important to subtract - // exactly the snapshotted unlogged samples, rather than simply resetting the - // vector: this way, the next snapshot will include any concurrent updates - // missed by the current snapshot. - - std::unique_ptr snapshot = SnapshotUnloggedSamples(); - unlogged_samples_->Subtract(*snapshot); - logged_samples_->Add(*snapshot); - - return snapshot; -} - -std::unique_ptr Histogram::SnapshotFinalDelta() const { -#if DCHECK_IS_ON() - DCHECK(!final_delta_created_); - final_delta_created_ = true; -#endif - - return SnapshotUnloggedSamples(); -} - -void Histogram::AddSamples(const HistogramSamples& samples) { - unlogged_samples_->Add(samples); -} - -bool Histogram::AddSamplesFromPickle(PickleIterator* iter) { - return unlogged_samples_->AddFromPickle(iter); -} - -// The following methods provide a graphical histogram display. -void Histogram::WriteHTMLGraph(std::string* output) const { - // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc. - output->append("
");
-  WriteAsciiImpl(true, "
", output); - output->append("
"); -} - -void Histogram::WriteAscii(std::string* output) const { - WriteAsciiImpl(true, "\n", output); -} - -void Histogram::ValidateHistogramContents() const { - CHECK(unlogged_samples_); - CHECK(unlogged_samples_->bucket_ranges()); - CHECK(logged_samples_); - CHECK(logged_samples_->bucket_ranges()); - CHECK_NE(0U, logged_samples_->id()); -} - -void Histogram::SerializeInfoImpl(Pickle* pickle) const { - DCHECK(bucket_ranges()->HasValidChecksum()); - pickle->WriteString(histogram_name()); - pickle->WriteInt(flags()); - pickle->WriteInt(declared_min()); - pickle->WriteInt(declared_max()); - pickle->WriteUInt32(bucket_count()); - pickle->WriteUInt32(bucket_ranges()->checksum()); -} - -// TODO(bcwhite): Remove minimum/maximum parameters from here and call chain. -Histogram::Histogram(const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges) - : HistogramBase(name) { - DCHECK(ranges) << name << ": " << minimum << "-" << maximum; - unlogged_samples_.reset(new SampleVector(HashMetricName(name), ranges)); - logged_samples_.reset(new SampleVector(unlogged_samples_->id(), ranges)); -} - -Histogram::Histogram(const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta) - : HistogramBase(name) { - DCHECK(ranges) << name << ": " << minimum << "-" << maximum; - unlogged_samples_.reset( - new PersistentSampleVector(HashMetricName(name), ranges, meta, counts)); - logged_samples_.reset(new PersistentSampleVector( - unlogged_samples_->id(), ranges, logged_meta, logged_counts)); -} - -Histogram::~Histogram() = default; - -bool Histogram::PrintEmptyBucket(uint32_t index) const { - return true; -} - -// Use the actual bucket widths (like a linear histogram) until the widths get -// over some transition value, and then use that transition width. Exponentials -// get so big so fast (and we don't expect to see a lot of entries in the large -// buckets), so we need this to make it possible to see what is going on and -// not have 0-graphical-height buckets. -double Histogram::GetBucketSize(Count current, uint32_t i) const { - DCHECK_GT(ranges(i + 1), ranges(i)); - static const double kTransitionWidth = 5; - double denominator = ranges(i + 1) - ranges(i); - if (denominator > kTransitionWidth) - denominator = kTransitionWidth; // Stop trying to normalize. - return current/denominator; -} - -const std::string Histogram::GetAsciiBucketRange(uint32_t i) const { - return GetSimpleAsciiBucketRange(ranges(i)); -} - -//------------------------------------------------------------------------------ -// Private methods - -// static -HistogramBase* Histogram::DeserializeInfoImpl(PickleIterator* iter) { - std::string histogram_name; - int flags; - int declared_min; - int declared_max; - uint32_t bucket_count; - uint32_t range_checksum; - - if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, - &declared_max, &bucket_count, &range_checksum)) { - return nullptr; - } - - // Find or create the local version of the histogram in this process. - HistogramBase* histogram = Histogram::FactoryGet( - histogram_name, declared_min, declared_max, bucket_count, flags); - if (!histogram) - return nullptr; - - // The serialized histogram might be corrupted. - if (!ValidateRangeChecksum(*histogram, range_checksum)) - return nullptr; - - return histogram; -} - -std::unique_ptr Histogram::SnapshotAllSamples() const { - std::unique_ptr samples = SnapshotUnloggedSamples(); - samples->Add(*logged_samples_); - return samples; -} - -std::unique_ptr Histogram::SnapshotUnloggedSamples() const { - std::unique_ptr samples( - new SampleVector(unlogged_samples_->id(), bucket_ranges())); - samples->Add(*unlogged_samples_); - return samples; -} - -void Histogram::WriteAsciiImpl(bool graph_it, - const std::string& newline, - std::string* output) const { - // Get local (stack) copies of all effectively volatile class data so that we - // are consistent across our output activities. - std::unique_ptr snapshot = SnapshotAllSamples(); - Count sample_count = snapshot->TotalCount(); - - WriteAsciiHeader(*snapshot, sample_count, output); - output->append(newline); - - // Prepare to normalize graphical rendering of bucket contents. - double max_size = 0; - if (graph_it) - max_size = GetPeakBucketSize(*snapshot); - - // Calculate space needed to print bucket range numbers. Leave room to print - // nearly the largest bucket range without sliding over the histogram. - uint32_t largest_non_empty_bucket = bucket_count() - 1; - while (0 == snapshot->GetCountAtIndex(largest_non_empty_bucket)) { - if (0 == largest_non_empty_bucket) - break; // All buckets are empty. - --largest_non_empty_bucket; - } - - // Calculate largest print width needed for any of our bucket range displays. - size_t print_width = 1; - for (uint32_t i = 0; i < bucket_count(); ++i) { - if (snapshot->GetCountAtIndex(i)) { - size_t width = GetAsciiBucketRange(i).size() + 1; - if (width > print_width) - print_width = width; - } - } - - int64_t remaining = sample_count; - int64_t past = 0; - // Output the actual histogram graph. - for (uint32_t i = 0; i < bucket_count(); ++i) { - Count current = snapshot->GetCountAtIndex(i); - if (!current && !PrintEmptyBucket(i)) - continue; - remaining -= current; - std::string range = GetAsciiBucketRange(i); - output->append(range); - for (size_t j = 0; range.size() + j < print_width + 1; ++j) - output->push_back(' '); - if (0 == current && i < bucket_count() - 1 && - 0 == snapshot->GetCountAtIndex(i + 1)) { - while (i < bucket_count() - 1 && - 0 == snapshot->GetCountAtIndex(i + 1)) { - ++i; - } - output->append("... "); - output->append(newline); - continue; // No reason to plot emptiness. - } - double current_size = GetBucketSize(current, i); - if (graph_it) - WriteAsciiBucketGraph(current_size, max_size, output); - WriteAsciiBucketContext(past, current, remaining, i, output); - output->append(newline); - past += current; - } - DCHECK_EQ(sample_count, past); -} - -double Histogram::GetPeakBucketSize(const SampleVectorBase& samples) const { - double max = 0; - for (uint32_t i = 0; i < bucket_count() ; ++i) { - double current_size = GetBucketSize(samples.GetCountAtIndex(i), i); - if (current_size > max) - max = current_size; - } - return max; -} - -void Histogram::WriteAsciiHeader(const SampleVectorBase& samples, - Count sample_count, - std::string* output) const { - StringAppendF(output, "Histogram: %s recorded %d samples", histogram_name(), - sample_count); - if (sample_count == 0) { - DCHECK_EQ(samples.sum(), 0); - } else { - double mean = static_cast(samples.sum()) / sample_count; - StringAppendF(output, ", mean = %.1f", mean); - } - if (flags()) - StringAppendF(output, " (flags = 0x%x)", flags()); -} - -void Histogram::WriteAsciiBucketContext(const int64_t past, - const Count current, - const int64_t remaining, - const uint32_t i, - std::string* output) const { - double scaled_sum = (past + current + remaining) / 100.0; - WriteAsciiBucketValue(current, scaled_sum, output); - if (0 < i) { - double percentage = past / scaled_sum; - StringAppendF(output, " {%3.1f%%}", percentage); - } -} - -void Histogram::GetParameters(DictionaryValue* params) const { - params->SetString("type", HistogramTypeToString(GetHistogramType())); - params->SetInteger("min", declared_min()); - params->SetInteger("max", declared_max()); - params->SetInteger("bucket_count", static_cast(bucket_count())); -} - -void Histogram::GetCountAndBucketData(Count* count, - int64_t* sum, - ListValue* buckets) const { - std::unique_ptr snapshot = SnapshotAllSamples(); - *count = snapshot->TotalCount(); - *sum = snapshot->sum(); - uint32_t index = 0; - for (uint32_t i = 0; i < bucket_count(); ++i) { - Sample count_at_index = snapshot->GetCountAtIndex(i); - if (count_at_index > 0) { - std::unique_ptr bucket_value(new DictionaryValue()); - bucket_value->SetInteger("low", ranges(i)); - if (i != bucket_count() - 1) - bucket_value->SetInteger("high", ranges(i + 1)); - bucket_value->SetInteger("count", count_at_index); - buckets->Set(index, std::move(bucket_value)); - ++index; - } - } -} - -//------------------------------------------------------------------------------ -// LinearHistogram: This histogram uses a traditional set of evenly spaced -// buckets. -//------------------------------------------------------------------------------ - -class LinearHistogram::Factory : public Histogram::Factory { - public: - Factory(const std::string& name, - HistogramBase::Sample minimum, - HistogramBase::Sample maximum, - uint32_t bucket_count, - int32_t flags, - const DescriptionPair* descriptions) - : Histogram::Factory(name, LINEAR_HISTOGRAM, minimum, maximum, - bucket_count, flags) { - descriptions_ = descriptions; - } - - protected: - BucketRanges* CreateRanges() override { - BucketRanges* ranges = new BucketRanges(bucket_count_ + 1); - LinearHistogram::InitializeBucketRanges(minimum_, maximum_, ranges); - return ranges; - } - - std::unique_ptr HeapAlloc( - const BucketRanges* ranges) override { - return WrapUnique(new LinearHistogram(GetPermanentName(name_), minimum_, - maximum_, ranges)); - } - - void FillHistogram(HistogramBase* base_histogram) override { - Histogram::Factory::FillHistogram(base_histogram); - // Normally, |base_histogram| should have type LINEAR_HISTOGRAM or be - // inherited from it. However, if it's expired, it will actually be a - // DUMMY_HISTOGRAM. Skip filling in that case. - if (base_histogram->GetHistogramType() == DUMMY_HISTOGRAM) - return; - LinearHistogram* histogram = static_cast(base_histogram); - // Set range descriptions. - if (descriptions_) { - for (int i = 0; descriptions_[i].description; ++i) { - histogram->bucket_description_[descriptions_[i].sample] = - descriptions_[i].description; - } - } - } - - private: - const DescriptionPair* descriptions_; - - DISALLOW_COPY_AND_ASSIGN(Factory); -}; - -LinearHistogram::~LinearHistogram() = default; - -HistogramBase* LinearHistogram::FactoryGet(const std::string& name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t flags) { - return FactoryGetWithRangeDescription(name, minimum, maximum, bucket_count, - flags, NULL); -} - -HistogramBase* LinearHistogram::FactoryTimeGet(const std::string& name, - TimeDelta minimum, - TimeDelta maximum, - uint32_t bucket_count, - int32_t flags) { - return FactoryGet(name, static_cast(minimum.InMilliseconds()), - static_cast(maximum.InMilliseconds()), bucket_count, - flags); -} - -HistogramBase* LinearHistogram::FactoryGet(const char* name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t flags) { - return FactoryGet(std::string(name), minimum, maximum, bucket_count, flags); -} - -HistogramBase* LinearHistogram::FactoryTimeGet(const char* name, - TimeDelta minimum, - TimeDelta maximum, - uint32_t bucket_count, - int32_t flags) { - return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count, - flags); -} - -std::unique_ptr LinearHistogram::PersistentCreate( - const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta) { - return WrapUnique(new LinearHistogram(name, minimum, maximum, ranges, counts, - logged_counts, meta, logged_meta)); -} - -HistogramBase* LinearHistogram::FactoryGetWithRangeDescription( - const std::string& name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t flags, - const DescriptionPair descriptions[]) { - bool valid_arguments = Histogram::InspectConstructionArguments( - name, &minimum, &maximum, &bucket_count); - DCHECK(valid_arguments); - - return Factory(name, minimum, maximum, bucket_count, flags, descriptions) - .Build(); -} - -HistogramType LinearHistogram::GetHistogramType() const { - return LINEAR_HISTOGRAM; -} - -LinearHistogram::LinearHistogram(const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges) - : Histogram(name, minimum, maximum, ranges) {} - -LinearHistogram::LinearHistogram( - const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta) - : Histogram(name, - minimum, - maximum, - ranges, - counts, - logged_counts, - meta, - logged_meta) {} - -double LinearHistogram::GetBucketSize(Count current, uint32_t i) const { - DCHECK_GT(ranges(i + 1), ranges(i)); - // Adjacent buckets with different widths would have "surprisingly" many (few) - // samples in a histogram if we didn't normalize this way. - double denominator = ranges(i + 1) - ranges(i); - return current/denominator; -} - -const std::string LinearHistogram::GetAsciiBucketRange(uint32_t i) const { - int range = ranges(i); - BucketDescriptionMap::const_iterator it = bucket_description_.find(range); - if (it == bucket_description_.end()) - return Histogram::GetAsciiBucketRange(i); - return it->second; -} - -bool LinearHistogram::PrintEmptyBucket(uint32_t index) const { - return bucket_description_.find(ranges(index)) == bucket_description_.end(); -} - -// static -void LinearHistogram::InitializeBucketRanges(Sample minimum, - Sample maximum, - BucketRanges* ranges) { - double min = minimum; - double max = maximum; - size_t bucket_count = ranges->bucket_count(); - for (size_t i = 1; i < bucket_count; ++i) { - double linear_range = - (min * (bucket_count - 1 - i) + max * (i - 1)) / (bucket_count - 2); - ranges->set_range(i, static_cast(linear_range + 0.5)); - } - ranges->set_range(ranges->bucket_count(), HistogramBase::kSampleType_MAX); - ranges->ResetChecksum(); -} - -// static -HistogramBase* LinearHistogram::DeserializeInfoImpl(PickleIterator* iter) { - std::string histogram_name; - int flags; - int declared_min; - int declared_max; - uint32_t bucket_count; - uint32_t range_checksum; - - if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, - &declared_max, &bucket_count, &range_checksum)) { - return nullptr; - } - - HistogramBase* histogram = LinearHistogram::FactoryGet( - histogram_name, declared_min, declared_max, bucket_count, flags); - if (!histogram) - return nullptr; - - if (!ValidateRangeChecksum(*histogram, range_checksum)) { - // The serialized histogram might be corrupted. - return nullptr; - } - return histogram; -} - -//------------------------------------------------------------------------------ -// ScaledLinearHistogram: This is a wrapper around a LinearHistogram that -// scales input counts. -//------------------------------------------------------------------------------ - -ScaledLinearHistogram::ScaledLinearHistogram(const char* name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t scale, - int32_t flags) - : histogram_(static_cast( - LinearHistogram::FactoryGet(name, - minimum, - maximum, - bucket_count, - flags))), - scale_(scale) { - DCHECK(histogram_); - DCHECK_LT(1, scale); - DCHECK_EQ(1, minimum); - CHECK_EQ(static_cast(bucket_count), maximum - minimum + 2) - << " ScaledLinearHistogram requires buckets of size 1"; - - remainders_.resize(histogram_->bucket_count(), 0); -} - -ScaledLinearHistogram::~ScaledLinearHistogram() = default; - -void ScaledLinearHistogram::AddScaledCount(Sample value, int count) { - if (count == 0) - return; - if (count < 0) { - NOTREACHED(); - return; - } - const int32_t max_value = - static_cast(histogram_->bucket_count() - 1); - if (value > max_value) - value = max_value; - if (value < 0) - value = 0; - - int scaled_count = count / scale_; - subtle::Atomic32 remainder = count - scaled_count * scale_; - - // ScaledLinearHistogram currently requires 1-to-1 mappings between value - // and bucket which alleviates the need to do a bucket lookup here (something - // that is internal to the HistogramSamples object). - if (remainder > 0) { - remainder = - subtle::NoBarrier_AtomicIncrement(&remainders_[value], remainder); - // If remainder passes 1/2 scale, increment main count (thus rounding up). - // The remainder is decremented by the full scale, though, which will - // cause it to go negative and thus requrire another increase by the full - // scale amount before another bump of the scaled count. - if (remainder >= scale_ / 2) { - scaled_count += 1; - subtle::NoBarrier_AtomicIncrement(&remainders_[value], -scale_); - } - } - - if (scaled_count > 0) - histogram_->AddCount(value, scaled_count); -} - -//------------------------------------------------------------------------------ -// This section provides implementation for BooleanHistogram. -//------------------------------------------------------------------------------ - -class BooleanHistogram::Factory : public Histogram::Factory { - public: - Factory(const std::string& name, int32_t flags) - : Histogram::Factory(name, BOOLEAN_HISTOGRAM, 1, 2, 3, flags) {} - - protected: - BucketRanges* CreateRanges() override { - BucketRanges* ranges = new BucketRanges(3 + 1); - LinearHistogram::InitializeBucketRanges(1, 2, ranges); - return ranges; - } - - std::unique_ptr HeapAlloc( - const BucketRanges* ranges) override { - return WrapUnique(new BooleanHistogram(GetPermanentName(name_), ranges)); - } - - private: - DISALLOW_COPY_AND_ASSIGN(Factory); -}; - -HistogramBase* BooleanHistogram::FactoryGet(const std::string& name, - int32_t flags) { - return Factory(name, flags).Build(); -} - -HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32_t flags) { - return FactoryGet(std::string(name), flags); -} - -std::unique_ptr BooleanHistogram::PersistentCreate( - const char* name, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta) { - return WrapUnique(new BooleanHistogram(name, ranges, counts, logged_counts, - meta, logged_meta)); -} - -HistogramType BooleanHistogram::GetHistogramType() const { - return BOOLEAN_HISTOGRAM; -} - -BooleanHistogram::BooleanHistogram(const char* name, const BucketRanges* ranges) - : LinearHistogram(name, 1, 2, ranges) {} - -BooleanHistogram::BooleanHistogram( - const char* name, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta) - : LinearHistogram(name, - 1, - 2, - ranges, - counts, - logged_counts, - meta, - logged_meta) {} - -HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) { - std::string histogram_name; - int flags; - int declared_min; - int declared_max; - uint32_t bucket_count; - uint32_t range_checksum; - - if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, - &declared_max, &bucket_count, &range_checksum)) { - return nullptr; - } - - HistogramBase* histogram = BooleanHistogram::FactoryGet( - histogram_name, flags); - if (!histogram) - return nullptr; - - if (!ValidateRangeChecksum(*histogram, range_checksum)) { - // The serialized histogram might be corrupted. - return nullptr; - } - return histogram; -} - -//------------------------------------------------------------------------------ -// CustomHistogram: -//------------------------------------------------------------------------------ - -class CustomHistogram::Factory : public Histogram::Factory { - public: - Factory(const std::string& name, - const std::vector* custom_ranges, - int32_t flags) - : Histogram::Factory(name, CUSTOM_HISTOGRAM, 0, 0, 0, flags) { - custom_ranges_ = custom_ranges; - } - - protected: - BucketRanges* CreateRanges() override { - // Remove the duplicates in the custom ranges array. - std::vector ranges = *custom_ranges_; - ranges.push_back(0); // Ensure we have a zero value. - ranges.push_back(HistogramBase::kSampleType_MAX); - std::sort(ranges.begin(), ranges.end()); - ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end()); - - BucketRanges* bucket_ranges = new BucketRanges(ranges.size()); - for (uint32_t i = 0; i < ranges.size(); i++) { - bucket_ranges->set_range(i, ranges[i]); - } - bucket_ranges->ResetChecksum(); - return bucket_ranges; - } - - std::unique_ptr HeapAlloc( - const BucketRanges* ranges) override { - return WrapUnique(new CustomHistogram(GetPermanentName(name_), ranges)); - } - - private: - const std::vector* custom_ranges_; - - DISALLOW_COPY_AND_ASSIGN(Factory); -}; - -HistogramBase* CustomHistogram::FactoryGet( - const std::string& name, - const std::vector& custom_ranges, - int32_t flags) { - CHECK(ValidateCustomRanges(custom_ranges)); - - return Factory(name, &custom_ranges, flags).Build(); -} - -HistogramBase* CustomHistogram::FactoryGet( - const char* name, - const std::vector& custom_ranges, - int32_t flags) { - return FactoryGet(std::string(name), custom_ranges, flags); -} - -std::unique_ptr CustomHistogram::PersistentCreate( - const char* name, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta) { - return WrapUnique(new CustomHistogram(name, ranges, counts, logged_counts, - meta, logged_meta)); -} - -HistogramType CustomHistogram::GetHistogramType() const { - return CUSTOM_HISTOGRAM; -} - -// static -std::vector CustomHistogram::ArrayToCustomEnumRanges( - base::span values) { - std::vector all_values; - for (Sample value : values) { - all_values.push_back(value); - - // Ensure that a guard bucket is added. If we end up with duplicate - // values, FactoryGet will take care of removing them. - all_values.push_back(value + 1); - } - return all_values; -} - -CustomHistogram::CustomHistogram(const char* name, const BucketRanges* ranges) - : Histogram(name, - ranges->range(1), - ranges->range(ranges->bucket_count() - 1), - ranges) {} - -CustomHistogram::CustomHistogram( - const char* name, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta) - : Histogram(name, - ranges->range(1), - ranges->range(ranges->bucket_count() - 1), - ranges, - counts, - logged_counts, - meta, - logged_meta) {} - -void CustomHistogram::SerializeInfoImpl(Pickle* pickle) const { - Histogram::SerializeInfoImpl(pickle); - - // Serialize ranges. First and last ranges are alwasy 0 and INT_MAX, so don't - // write them. - for (uint32_t i = 1; i < bucket_ranges()->bucket_count(); ++i) - pickle->WriteInt(bucket_ranges()->range(i)); -} - -double CustomHistogram::GetBucketSize(Count current, uint32_t i) const { - // If this is a histogram of enum values, normalizing the bucket count - // by the bucket range is not helpful, so just return the bucket count. - return current; -} - -// static -HistogramBase* CustomHistogram::DeserializeInfoImpl(PickleIterator* iter) { - std::string histogram_name; - int flags; - int declared_min; - int declared_max; - uint32_t bucket_count; - uint32_t range_checksum; - - if (!ReadHistogramArguments(iter, &histogram_name, &flags, &declared_min, - &declared_max, &bucket_count, &range_checksum)) { - return nullptr; - } - - // First and last ranges are not serialized. - std::vector sample_ranges(bucket_count - 1); - - for (uint32_t i = 0; i < sample_ranges.size(); ++i) { - if (!iter->ReadInt(&sample_ranges[i])) - return nullptr; - } - - HistogramBase* histogram = CustomHistogram::FactoryGet( - histogram_name, sample_ranges, flags); - if (!histogram) - return nullptr; - - if (!ValidateRangeChecksum(*histogram, range_checksum)) { - // The serialized histogram might be corrupted. - return nullptr; - } - return histogram; -} - -// static -bool CustomHistogram::ValidateCustomRanges( - const std::vector& custom_ranges) { - bool has_valid_range = false; - for (uint32_t i = 0; i < custom_ranges.size(); i++) { - Sample sample = custom_ranges[i]; - if (sample < 0 || sample > HistogramBase::kSampleType_MAX - 1) - return false; - if (sample != 0) - has_valid_range = true; - } - return has_valid_range; -} - -} // namespace base diff --git a/metrics/histogram.h b/metrics/histogram.h deleted file mode 100644 index bd5a9ebe3..000000000 --- a/metrics/histogram.h +++ /dev/null @@ -1,610 +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. - -// Histogram is an object that aggregates statistics, and can summarize them in -// various forms, including ASCII graphical, HTML, and numerically (as a -// vector of numbers corresponding to each of the aggregating buckets). - -// It supports calls to accumulate either time intervals (which are processed -// as integral number of milliseconds), or arbitrary integral units. - -// For Histogram (exponential histogram), LinearHistogram and CustomHistogram, -// the minimum for a declared range is 1 (instead of 0), while the maximum is -// (HistogramBase::kSampleType_MAX - 1). However, there will always be underflow -// and overflow buckets added automatically, so a 0 bucket will always exist -// even when a minimum value of 1 is specified. - -// Each use of a histogram with the same name will reference the same underlying -// data, so it is safe to record to the same histogram from multiple locations -// in the code. It is a runtime error if all uses of the same histogram do not -// agree exactly in type, bucket size and range. - -// For Histogram and LinearHistogram, the maximum for a declared range should -// always be larger (not equal) than minimal range. Zero and -// HistogramBase::kSampleType_MAX are implicitly added as first and last ranges, -// so the smallest legal bucket_count is 3. However CustomHistogram can have -// bucket count as 2 (when you give a custom ranges vector containing only 1 -// range). -// For these 3 kinds of histograms, the max bucket count is always -// (Histogram::kBucketCount_MAX - 1). - -// The buckets layout of class Histogram is exponential. For example, buckets -// might contain (sequentially) the count of values in the following intervals: -// [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity) -// That bucket allocation would actually result from construction of a histogram -// for values between 1 and 64, with 8 buckets, such as: -// Histogram count("some name", 1, 64, 8); -// Note that the underflow bucket [0,1) and the overflow bucket [64,infinity) -// are also counted by the constructor in the user supplied "bucket_count" -// argument. -// The above example has an exponential ratio of 2 (doubling the bucket width -// in each consecutive bucket). The Histogram class automatically calculates -// the smallest ratio that it can use to construct the number of buckets -// selected in the constructor. An another example, if you had 50 buckets, -// and millisecond time values from 1 to 10000, then the ratio between -// consecutive bucket widths will be approximately somewhere around the 50th -// root of 10000. This approach provides very fine grain (narrow) buckets -// at the low end of the histogram scale, but allows the histogram to cover a -// gigantic range with the addition of very few buckets. - -// Usually we use macros to define and use a histogram, which are defined in -// base/metrics/histogram_macros.h. Note: Callers should include that header -// directly if they only access the histogram APIs through macros. -// -// Macros use a pattern involving a function static variable, that is a pointer -// to a histogram. This static is explicitly initialized on any thread -// that detects a uninitialized (NULL) pointer. The potentially racy -// initialization is not a problem as it is always set to point to the same -// value (i.e., the FactoryGet always returns the same value). FactoryGet -// is also completely thread safe, which results in a completely thread safe, -// and relatively fast, set of counters. To avoid races at shutdown, the static -// pointer is NOT deleted, and we leak the histograms at process termination. - -#ifndef BASE_METRICS_HISTOGRAM_H_ -#define BASE_METRICS_HISTOGRAM_H_ - -#include -#include - -#include -#include -#include -#include - -#include "base/base_export.h" -#include "base/compiler_specific.h" -#include "base/containers/span.h" -#include "base/gtest_prod_util.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/metrics/bucket_ranges.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_samples.h" -#include "base/strings/string_piece.h" -#include "base/time/time.h" - -namespace base { - -class BooleanHistogram; -class CustomHistogram; -class DelayedPersistentAllocation; -class Histogram; -class HistogramTest; -class LinearHistogram; -class Pickle; -class PickleIterator; -class SampleVector; -class SampleVectorBase; - -class BASE_EXPORT Histogram : public HistogramBase { - public: - // Initialize maximum number of buckets in histograms as 16,384. - static const uint32_t kBucketCount_MAX; - - typedef std::vector Counts; - - ~Histogram() override; - - //---------------------------------------------------------------------------- - // For a valid histogram, input should follow these restrictions: - // minimum > 0 (if a minimum below 1 is specified, it will implicitly be - // normalized up to 1) - // maximum > minimum - // buckets > 2 [minimum buckets needed: underflow, overflow and the range] - // Additionally, - // buckets <= (maximum - minimum + 2) - this is to ensure that we don't have - // more buckets than the range of numbers; having more buckets than 1 per - // value in the range would be nonsensical. - static HistogramBase* FactoryGet(const std::string& name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t flags); - static HistogramBase* FactoryTimeGet(const std::string& name, - base::TimeDelta minimum, - base::TimeDelta maximum, - uint32_t bucket_count, - int32_t flags); - static HistogramBase* FactoryMicrosecondsTimeGet(const std::string& name, - base::TimeDelta minimum, - base::TimeDelta maximum, - uint32_t bucket_count, - int32_t flags); - - // Overloads of the above functions that take a const char* |name| param, to - // avoid code bloat from the std::string constructor being inlined into call - // sites. - static HistogramBase* FactoryGet(const char* name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t flags); - static HistogramBase* FactoryTimeGet(const char* name, - base::TimeDelta minimum, - base::TimeDelta maximum, - uint32_t bucket_count, - int32_t flags); - static HistogramBase* FactoryMicrosecondsTimeGet(const char* name, - base::TimeDelta minimum, - base::TimeDelta maximum, - uint32_t bucket_count, - int32_t flags); - - // Create a histogram using data in persistent storage. - static std::unique_ptr PersistentCreate( - const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta); - - static void InitializeBucketRanges(Sample minimum, - Sample maximum, - BucketRanges* ranges); - - // This constant if for FindCorruption. Since snapshots of histograms are - // taken asynchronously relative to sampling, and our counting code currently - // does not prevent race conditions, it is pretty likely that we'll catch a - // redundant count that doesn't match the sample count. We allow for a - // certain amount of slop before flagging this as an inconsistency. Even with - // an inconsistency, we'll snapshot it again (for UMA in about a half hour), - // so we'll eventually get the data, if it was not the result of a corruption. - static const int kCommonRaceBasedCountMismatch; - - // Check to see if bucket ranges, counts and tallies in the snapshot are - // consistent with the bucket ranges and checksums in our histogram. This can - // produce a false-alarm if a race occurred in the reading of the data during - // a SnapShot process, but should otherwise be false at all times (unless we - // have memory over-writes, or DRAM failures). Flag definitions are located - // under "enum Inconsistency" in base/metrics/histogram_base.h. - uint32_t FindCorruption(const HistogramSamples& samples) const override; - - //---------------------------------------------------------------------------- - // Accessors for factory construction, serialization and testing. - //---------------------------------------------------------------------------- - const BucketRanges* bucket_ranges() const; - Sample declared_min() const; - Sample declared_max() const; - virtual Sample ranges(uint32_t i) const; - virtual uint32_t bucket_count() const; - - // This function validates histogram construction arguments. It returns false - // if some of the arguments are bad but also corrects them so they should - // function on non-dcheck builds without crashing. - // Note. Currently it allow some bad input, e.g. 0 as minimum, but silently - // converts it to good input: 1. - // TODO(bcwhite): Use false returns to create "sink" histograms so that bad - // data doesn't create confusion on the servers. - static bool InspectConstructionArguments(StringPiece name, - Sample* minimum, - Sample* maximum, - uint32_t* bucket_count); - - // HistogramBase implementation: - uint64_t name_hash() const override; - HistogramType GetHistogramType() const override; - bool HasConstructionArguments(Sample expected_minimum, - Sample expected_maximum, - uint32_t expected_bucket_count) const override; - void Add(Sample value) override; - void AddCount(Sample value, int count) override; - std::unique_ptr SnapshotSamples() const override; - std::unique_ptr SnapshotDelta() override; - std::unique_ptr SnapshotFinalDelta() const override; - void AddSamples(const HistogramSamples& samples) override; - bool AddSamplesFromPickle(base::PickleIterator* iter) override; - void WriteHTMLGraph(std::string* output) const override; - void WriteAscii(std::string* output) const override; - - // Validates the histogram contents and CHECKs on errors. - // TODO(bcwhite): Remove this after https://crbug/836875. - void ValidateHistogramContents() const override; - - protected: - // This class, defined entirely within the .cc file, contains all the - // common logic for building a Histogram and can be overridden by more - // specific types to alter details of how the creation is done. It is - // defined as an embedded class (rather than an anonymous one) so it - // can access the protected constructors. - class Factory; - - // |ranges| should contain the underflow and overflow buckets. See top - // comments for example. - Histogram(const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges); - - // Traditionally, histograms allocate their own memory for the bucket - // vector but "shared" histograms use memory regions allocated from a - // special memory segment that is passed in here. It is assumed that - // the life of this memory is managed externally and exceeds the lifetime - // of this object. Practically, this memory is never released until the - // process exits and the OS cleans it up. - Histogram(const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta); - - // HistogramBase implementation: - void SerializeInfoImpl(base::Pickle* pickle) const override; - - // Method to override to skip the display of the i'th bucket if it's empty. - virtual bool PrintEmptyBucket(uint32_t index) const; - - // Get normalized size, relative to the ranges(i). - virtual double GetBucketSize(Count current, uint32_t i) const; - - // Return a string description of what goes in a given bucket. - // Most commonly this is the numeric value, but in derived classes it may - // be a name (or string description) given to the bucket. - virtual const std::string GetAsciiBucketRange(uint32_t it) const; - - private: - // Allow tests to corrupt our innards for testing purposes. - friend class HistogramTest; - FRIEND_TEST_ALL_PREFIXES(HistogramTest, BoundsTest); - FRIEND_TEST_ALL_PREFIXES(HistogramTest, BucketPlacementTest); - FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts); - - friend class StatisticsRecorder; // To allow it to delete duplicates. - friend class StatisticsRecorderTest; - - friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( - base::PickleIterator* iter); - static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); - - // Create a snapshot containing all samples (both logged and unlogged). - // Implementation of SnapshotSamples method with a more specific type for - // internal use. - std::unique_ptr SnapshotAllSamples() const; - - // Create a copy of unlogged samples. - std::unique_ptr SnapshotUnloggedSamples() const; - - //---------------------------------------------------------------------------- - // Helpers for emitting Ascii graphic. Each method appends data to output. - - void WriteAsciiImpl(bool graph_it, - const std::string& newline, - std::string* output) const; - - // Find out how large (graphically) the largest bucket will appear to be. - double GetPeakBucketSize(const SampleVectorBase& samples) const; - - // Write a common header message describing this histogram. - void WriteAsciiHeader(const SampleVectorBase& samples, - Count sample_count, - std::string* output) const; - - // Write information about previous, current, and next buckets. - // Information such as cumulative percentage, etc. - void WriteAsciiBucketContext(const int64_t past, - const Count current, - const int64_t remaining, - const uint32_t i, - std::string* output) const; - - // WriteJSON calls these. - void GetParameters(DictionaryValue* params) const override; - - void GetCountAndBucketData(Count* count, - int64_t* sum, - ListValue* buckets) const override; - - // Samples that have not yet been logged with SnapshotDelta(). - std::unique_ptr unlogged_samples_; - - // Accumulation of all samples that have been logged with SnapshotDelta(). - std::unique_ptr logged_samples_; - -#if DCHECK_IS_ON() // Don't waste memory if it won't be used. - // Flag to indicate if PrepareFinalDelta has been previously called. It is - // used to DCHECK that a final delta is not created multiple times. - mutable bool final_delta_created_ = false; -#endif - - DISALLOW_COPY_AND_ASSIGN(Histogram); -}; - -//------------------------------------------------------------------------------ - -// LinearHistogram is a more traditional histogram, with evenly spaced -// buckets. -class BASE_EXPORT LinearHistogram : public Histogram { - public: - ~LinearHistogram() override; - - /* minimum should start from 1. 0 is as minimum is invalid. 0 is an implicit - default underflow bucket. */ - static HistogramBase* FactoryGet(const std::string& name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t flags); - static HistogramBase* FactoryTimeGet(const std::string& name, - TimeDelta minimum, - TimeDelta maximum, - uint32_t bucket_count, - int32_t flags); - - // Overloads of the above two functions that take a const char* |name| param, - // to avoid code bloat from the std::string constructor being inlined into - // call sites. - static HistogramBase* FactoryGet(const char* name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t flags); - static HistogramBase* FactoryTimeGet(const char* name, - TimeDelta minimum, - TimeDelta maximum, - uint32_t bucket_count, - int32_t flags); - - // Create a histogram using data in persistent storage. - static std::unique_ptr PersistentCreate( - const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta); - - struct DescriptionPair { - Sample sample; - const char* description; // Null means end of a list of pairs. - }; - - // Create a LinearHistogram and store a list of number/text values for use in - // writing the histogram graph. - // |descriptions| can be NULL, which means no special descriptions to set. If - // it's not NULL, the last element in the array must has a NULL in its - // "description" field. - static HistogramBase* FactoryGetWithRangeDescription( - const std::string& name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t flags, - const DescriptionPair descriptions[]); - - static void InitializeBucketRanges(Sample minimum, - Sample maximum, - BucketRanges* ranges); - - // Overridden from Histogram: - HistogramType GetHistogramType() const override; - - protected: - class Factory; - - LinearHistogram(const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges); - - LinearHistogram(const char* name, - Sample minimum, - Sample maximum, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta); - - double GetBucketSize(Count current, uint32_t i) const override; - - // If we have a description for a bucket, then return that. Otherwise - // let parent class provide a (numeric) description. - const std::string GetAsciiBucketRange(uint32_t i) const override; - - // Skip printing of name for numeric range if we have a name (and if this is - // an empty bucket). - bool PrintEmptyBucket(uint32_t index) const override; - - private: - friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( - base::PickleIterator* iter); - static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); - - // For some ranges, we store a printable description of a bucket range. - // If there is no description, then GetAsciiBucketRange() uses parent class - // to provide a description. - typedef std::map BucketDescriptionMap; - BucketDescriptionMap bucket_description_; - - DISALLOW_COPY_AND_ASSIGN(LinearHistogram); -}; - -//------------------------------------------------------------------------------ - -// ScaledLinearHistogram is a wrapper around a linear histogram that scales the -// counts down by some factor. Remainder values are kept locally but lost when -// uploaded or serialized. The integral counts are rounded up/down so should -// average to the correct value when many reports are added. -// -// This is most useful when adding many counts at once via AddCount() that can -// cause overflows of the 31-bit counters, usually with an enum as the value. -class BASE_EXPORT ScaledLinearHistogram { - using AtomicCount = Histogram::AtomicCount; - using Sample = Histogram::Sample; - - public: - // Currently only works with "exact" linear histograms: minimum=1, maximum=N, - // and bucket_count=N+1. - ScaledLinearHistogram(const char* name, - Sample minimum, - Sample maximum, - uint32_t bucket_count, - int32_t scale, - int32_t flags); - - ~ScaledLinearHistogram(); - - // Like AddCount() but actually accumulates |count|/|scale| and increments - // the accumulated remainder by |count|%|scale|. An additional increment - // is done when the remainder has grown sufficiently large. - void AddScaledCount(Sample value, int count); - - int32_t scale() const { return scale_; } - LinearHistogram* histogram() { return histogram_; } - - private: - // Pointer to the underlying histogram. Ownership of it remains with - // the statistics-recorder. - LinearHistogram* const histogram_; - - // The scale factor of the sample counts. - const int32_t scale_; - - // A vector of "remainder" counts indexed by bucket number. These values - // may be negative as the scaled count is actually bumped once the - // remainder is 1/2 way to the scale value (thus "rounding"). - std::vector remainders_; - - DISALLOW_COPY_AND_ASSIGN(ScaledLinearHistogram); -}; - -//------------------------------------------------------------------------------ - -// BooleanHistogram is a histogram for booleans. -class BASE_EXPORT BooleanHistogram : public LinearHistogram { - public: - static HistogramBase* FactoryGet(const std::string& name, int32_t flags); - - // Overload of the above function that takes a const char* |name| param, - // to avoid code bloat from the std::string constructor being inlined into - // call sites. - static HistogramBase* FactoryGet(const char* name, int32_t flags); - - // Create a histogram using data in persistent storage. - static std::unique_ptr PersistentCreate( - const char* name, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta); - - HistogramType GetHistogramType() const override; - - protected: - class Factory; - - private: - BooleanHistogram(const char* name, const BucketRanges* ranges); - BooleanHistogram(const char* name, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta); - - friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( - base::PickleIterator* iter); - static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); - - DISALLOW_COPY_AND_ASSIGN(BooleanHistogram); -}; - -//------------------------------------------------------------------------------ - -// CustomHistogram is a histogram for a set of custom integers. -class BASE_EXPORT CustomHistogram : public Histogram { - public: - // |custom_ranges| contains a vector of limits on ranges. Each limit should be - // > 0 and < kSampleType_MAX. (Currently 0 is still accepted for backward - // compatibility). The limits can be unordered or contain duplication, but - // client should not depend on this. - static HistogramBase* FactoryGet(const std::string& name, - const std::vector& custom_ranges, - int32_t flags); - - // Overload of the above function that takes a const char* |name| param, - // to avoid code bloat from the std::string constructor being inlined into - // call sites. - static HistogramBase* FactoryGet(const char* name, - const std::vector& custom_ranges, - int32_t flags); - - // Create a histogram using data in persistent storage. - static std::unique_ptr PersistentCreate( - const char* name, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta); - - // Overridden from Histogram: - HistogramType GetHistogramType() const override; - - // Helper method for transforming an array of valid enumeration values - // to the std::vector expected by UMA_HISTOGRAM_CUSTOM_ENUMERATION. - // This function ensures that a guard bucket exists right after any - // valid sample value (unless the next higher sample is also a valid value), - // so that invalid samples never fall into the same bucket as valid samples. - static std::vector ArrayToCustomEnumRanges( - base::span values); - - protected: - class Factory; - - CustomHistogram(const char* name, const BucketRanges* ranges); - - CustomHistogram(const char* name, - const BucketRanges* ranges, - const DelayedPersistentAllocation& counts, - const DelayedPersistentAllocation& logged_counts, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta); - - // HistogramBase implementation: - void SerializeInfoImpl(base::Pickle* pickle) const override; - - double GetBucketSize(Count current, uint32_t i) const override; - - private: - friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( - base::PickleIterator* iter); - static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); - - static bool ValidateCustomRanges(const std::vector& custom_ranges); - - DISALLOW_COPY_AND_ASSIGN(CustomHistogram); -}; - -} // namespace base - -#endif // BASE_METRICS_HISTOGRAM_H_ diff --git a/metrics/histogram_base.cc b/metrics/histogram_base.cc deleted file mode 100644 index 990d9f5e0..000000000 --- a/metrics/histogram_base.cc +++ /dev/null @@ -1,215 +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/metrics/histogram_base.h" - -#include - -#include -#include -#include - -#include "base/json/json_string_value_serializer.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_macros.h" -#include "base/metrics/histogram_samples.h" -#include "base/metrics/sparse_histogram.h" -#include "base/metrics/statistics_recorder.h" -#include "base/numerics/safe_conversions.h" -#include "base/pickle.h" -#include "base/process/process_handle.h" -#include "base/rand_util.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/lock.h" -#include "base/values.h" - -namespace base { - -std::string HistogramTypeToString(HistogramType type) { - switch (type) { - case HISTOGRAM: - return "HISTOGRAM"; - case LINEAR_HISTOGRAM: - return "LINEAR_HISTOGRAM"; - case BOOLEAN_HISTOGRAM: - return "BOOLEAN_HISTOGRAM"; - case CUSTOM_HISTOGRAM: - return "CUSTOM_HISTOGRAM"; - case SPARSE_HISTOGRAM: - return "SPARSE_HISTOGRAM"; - case DUMMY_HISTOGRAM: - return "DUMMY_HISTOGRAM"; - } - NOTREACHED(); - return "UNKNOWN"; -} - -HistogramBase* DeserializeHistogramInfo(PickleIterator* iter) { - int type; - if (!iter->ReadInt(&type)) - return nullptr; - - switch (type) { - case HISTOGRAM: - return Histogram::DeserializeInfoImpl(iter); - case LINEAR_HISTOGRAM: - return LinearHistogram::DeserializeInfoImpl(iter); - case BOOLEAN_HISTOGRAM: - return BooleanHistogram::DeserializeInfoImpl(iter); - case CUSTOM_HISTOGRAM: - return CustomHistogram::DeserializeInfoImpl(iter); - case SPARSE_HISTOGRAM: - return SparseHistogram::DeserializeInfoImpl(iter); - default: - return nullptr; - } -} - -const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX; - -HistogramBase::HistogramBase(const char* name) - : histogram_name_(name), flags_(kNoFlags) {} - -HistogramBase::~HistogramBase() = default; - -void HistogramBase::CheckName(const StringPiece& name) const { - DCHECK_EQ(StringPiece(histogram_name()), name); -} - -void HistogramBase::SetFlags(int32_t flags) { - HistogramBase::Count old_flags = subtle::NoBarrier_Load(&flags_); - subtle::NoBarrier_Store(&flags_, old_flags | flags); -} - -void HistogramBase::ClearFlags(int32_t flags) { - HistogramBase::Count old_flags = subtle::NoBarrier_Load(&flags_); - subtle::NoBarrier_Store(&flags_, old_flags & ~flags); -} - -void HistogramBase::AddScaled(Sample value, int count, int scale) { - DCHECK_LT(0, scale); - - // Convert raw count and probabilistically round up/down if the remainder - // is more than a random number [0, scale). This gives a more accurate - // count when there are a large number of records. RandInt is "inclusive", - // hence the -1 for the max value. - int64_t count_scaled = count / scale; - if (count - (count_scaled * scale) > base::RandInt(0, scale - 1)) - count_scaled += 1; - if (count_scaled == 0) - return; - - AddCount(value, count_scaled); -} - -void HistogramBase::AddKilo(Sample value, int count) { - AddScaled(value, count, 1000); -} - -void HistogramBase::AddKiB(Sample value, int count) { - AddScaled(value, count, 1024); -} - -void HistogramBase::AddTimeMillisecondsGranularity(const TimeDelta& time) { - Add(saturated_cast(time.InMilliseconds())); -} - -void HistogramBase::AddTimeMicrosecondsGranularity(const TimeDelta& time) { - // Intentionally drop high-resolution reports on clients with low-resolution - // clocks. High-resolution metrics cannot make use of low-resolution data and - // reporting it merely adds noise to the metric. https://crbug.com/807615#c16 - if (TimeTicks::IsHighResolution()) - Add(saturated_cast(time.InMicroseconds())); -} - -void HistogramBase::AddBoolean(bool value) { - Add(value ? 1 : 0); -} - -void HistogramBase::SerializeInfo(Pickle* pickle) const { - pickle->WriteInt(GetHistogramType()); - SerializeInfoImpl(pickle); -} - -uint32_t HistogramBase::FindCorruption(const HistogramSamples& samples) const { - // Not supported by default. - return NO_INCONSISTENCIES; -} - -void HistogramBase::ValidateHistogramContents() const {} - -void HistogramBase::WriteJSON(std::string* output, - JSONVerbosityLevel verbosity_level) const { - Count count; - int64_t sum; - std::unique_ptr buckets(new ListValue()); - GetCountAndBucketData(&count, &sum, buckets.get()); - std::unique_ptr parameters(new DictionaryValue()); - GetParameters(parameters.get()); - - JSONStringValueSerializer serializer(output); - DictionaryValue root; - root.SetString("name", histogram_name()); - root.SetInteger("count", count); - root.SetDouble("sum", static_cast(sum)); - root.SetInteger("flags", flags()); - root.Set("params", std::move(parameters)); - if (verbosity_level != JSON_VERBOSITY_LEVEL_OMIT_BUCKETS) - root.Set("buckets", std::move(buckets)); - root.SetInteger("pid", GetUniqueIdForProcess()); - serializer.Serialize(root); -} - -void HistogramBase::FindAndRunCallback(HistogramBase::Sample sample) const { - if ((flags() & kCallbackExists) == 0) - return; - - StatisticsRecorder::OnSampleCallback cb = - StatisticsRecorder::FindCallback(histogram_name()); - if (!cb.is_null()) - cb.Run(sample); -} - -void HistogramBase::WriteAsciiBucketGraph(double current_size, - double max_size, - std::string* output) const { - const int k_line_length = 72; // Maximal horizontal width of graph. - int x_count = static_cast(k_line_length * (current_size / max_size) - + 0.5); - int x_remainder = k_line_length - x_count; - - while (0 < x_count--) - output->append("-"); - output->append("O"); - while (0 < x_remainder--) - output->append(" "); -} - -const std::string HistogramBase::GetSimpleAsciiBucketRange( - Sample sample) const { - return StringPrintf("%d", sample); -} - -void HistogramBase::WriteAsciiBucketValue(Count current, - double scaled_sum, - std::string* output) const { - StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); -} - -// static -char const* HistogramBase::GetPermanentName(const std::string& name) { - // A set of histogram names that provides the "permanent" lifetime required - // by histogram objects for those strings that are not already code constants - // or held in persistent memory. - static LazyInstance>::Leaky permanent_names; - static LazyInstance::Leaky permanent_names_lock; - - AutoLock lock(permanent_names_lock.Get()); - auto result = permanent_names.Get().insert(name); - return result.first->c_str(); -} - -} // namespace base diff --git a/metrics/histogram_base.h b/metrics/histogram_base.h deleted file mode 100644 index f128ff2d9..000000000 --- a/metrics/histogram_base.h +++ /dev/null @@ -1,307 +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_METRICS_HISTOGRAM_BASE_H_ -#define BASE_METRICS_HISTOGRAM_BASE_H_ - -#include -#include -#include - -#include -#include -#include - -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" -#include "base/time/time.h" - -namespace base { - -class DictionaryValue; -class HistogramBase; -class HistogramSamples; -class ListValue; -class Pickle; -class PickleIterator; - -//////////////////////////////////////////////////////////////////////////////// -// This enum is used to facilitate deserialization of histograms from other -// processes into the browser. If you create another class that inherits from -// HistogramBase, add new histogram types and names below. - -enum HistogramType { - HISTOGRAM, - LINEAR_HISTOGRAM, - BOOLEAN_HISTOGRAM, - CUSTOM_HISTOGRAM, - SPARSE_HISTOGRAM, - DUMMY_HISTOGRAM, -}; - -// Controls the verbosity of the information when the histogram is serialized to -// a JSON. -// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.metrics -enum JSONVerbosityLevel { - // The histogram is completely serialized. - JSON_VERBOSITY_LEVEL_FULL, - // The bucket information is not serialized. - JSON_VERBOSITY_LEVEL_OMIT_BUCKETS, -}; - -std::string HistogramTypeToString(HistogramType type); - -// This enum is used for reporting how many histograms and of what types and -// variations are being created. It has to be in the main .h file so it is -// visible to files that define the various histogram types. -enum HistogramReport { - // Count the number of reports created. The other counts divided by this - // number will give the average per run of the program. - HISTOGRAM_REPORT_CREATED = 0, - - // Count the total number of histograms created. It is the limit against - // which all others are compared. - HISTOGRAM_REPORT_HISTOGRAM_CREATED = 1, - - // Count the total number of histograms looked-up. It's better to cache - // the result of a single lookup rather than do it repeatedly. - HISTOGRAM_REPORT_HISTOGRAM_LOOKUP = 2, - - // These count the individual histogram types. This must follow the order - // of HistogramType above. - HISTOGRAM_REPORT_TYPE_LOGARITHMIC = 3, - HISTOGRAM_REPORT_TYPE_LINEAR = 4, - HISTOGRAM_REPORT_TYPE_BOOLEAN = 5, - HISTOGRAM_REPORT_TYPE_CUSTOM = 6, - HISTOGRAM_REPORT_TYPE_SPARSE = 7, - - // These indicate the individual flags that were set. - HISTOGRAM_REPORT_FLAG_UMA_TARGETED = 8, - HISTOGRAM_REPORT_FLAG_UMA_STABILITY = 9, - HISTOGRAM_REPORT_FLAG_PERSISTENT = 10, - - // This must be last. - HISTOGRAM_REPORT_MAX = 11 -}; - -// Create or find existing histogram that matches the pickled info. -// Returns NULL if the pickled data has problems. -BASE_EXPORT HistogramBase* DeserializeHistogramInfo(base::PickleIterator* iter); - -//////////////////////////////////////////////////////////////////////////////// - -class BASE_EXPORT HistogramBase { - public: - typedef int32_t Sample; // Used for samples. - typedef subtle::Atomic32 AtomicCount; // Used to count samples. - typedef int32_t Count; // Used to manipulate counts in temporaries. - - static const Sample kSampleType_MAX; // INT_MAX - - enum Flags { - kNoFlags = 0x0, - - // 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, - - // Indicates that a callback exists for when a new sample is recorded on - // this histogram. We store this as a flag with the histogram since - // histograms can be in performance critical code, and this allows us - // to shortcut looking up the callback if it doesn't exist. - kCallbackExists = 0x20, - - // Indicates that the histogram is held in "persistent" memory and may - // be accessible between processes. This is only possible if such a - // memory segment has been created/attached, used to create a Persistent- - // MemoryAllocator, and that loaded into the Histogram module before this - // histogram is created. - kIsPersistent = 0x40, - }; - - // Histogram data inconsistency types. - enum Inconsistency : uint32_t { - NO_INCONSISTENCIES = 0x0, - RANGE_CHECKSUM_ERROR = 0x1, - BUCKET_ORDER_ERROR = 0x2, - COUNT_HIGH_ERROR = 0x4, - COUNT_LOW_ERROR = 0x8, - - NEVER_EXCEEDED_VALUE = 0x10, - }; - - // Construct the base histogram. The name is not copied; it's up to the - // caller to ensure that it lives at least as long as this object. - explicit HistogramBase(const char* name); - virtual ~HistogramBase(); - - const char* histogram_name() const { return histogram_name_; } - - // Compares |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. - virtual void CheckName(const StringPiece& name) const; - - // Get a unique ID for this histogram's samples. - virtual uint64_t name_hash() const = 0; - - // Operations with Flags enum. - int32_t flags() const { return subtle::NoBarrier_Load(&flags_); } - void SetFlags(int32_t flags); - void ClearFlags(int32_t flags); - - virtual HistogramType GetHistogramType() const = 0; - - // Whether the histogram has construction arguments as parameters specified. - // For histograms that don't have the concept of minimum, maximum or - // bucket_count, this function always returns false. - virtual bool HasConstructionArguments( - Sample expected_minimum, - Sample expected_maximum, - uint32_t expected_bucket_count) const = 0; - - virtual void Add(Sample value) = 0; - - // In Add function the |value| bucket is increased by one, but in some use - // cases we need to increase this value by an arbitrary integer. AddCount - // function increases the |value| bucket by |count|. |count| should be greater - // than or equal to 1. - virtual void AddCount(Sample value, int count) = 0; - - // Similar to above but divides |count| by the |scale| amount. Probabilistic - // rounding is used to yield a reasonably accurate total when many samples - // are added. Methods for common cases of scales 1000 and 1024 are included. - // The ScaledLinearHistogram (which can also used be for enumerations) may be - // a better (and faster) solution. - void AddScaled(Sample value, int count, int scale); - void AddKilo(Sample value, int count); // scale=1000 - void AddKiB(Sample value, int count); // scale=1024 - - // Convenient functions that call Add(Sample). - void AddTime(const TimeDelta& time) { AddTimeMillisecondsGranularity(time); } - void AddTimeMillisecondsGranularity(const TimeDelta& time); - // Note: AddTimeMicrosecondsGranularity() drops the report if this client - // doesn't have a high-resolution clock. - void AddTimeMicrosecondsGranularity(const TimeDelta& time); - void AddBoolean(bool value); - - virtual void AddSamples(const HistogramSamples& samples) = 0; - virtual bool AddSamplesFromPickle(base::PickleIterator* iter) = 0; - - // Serialize the histogram info into |pickle|. - // Note: This only serializes the construction arguments of the histogram, but - // does not serialize the samples. - void SerializeInfo(base::Pickle* pickle) const; - - // Try to find out data corruption from histogram and the samples. - // The returned value is a combination of Inconsistency enum. - virtual uint32_t FindCorruption(const HistogramSamples& samples) const; - - // Snapshot the current complete set of sample data. - // Override with atomic/locked snapshot if needed. - // NOTE: this data can overflow for long-running sessions. It should be - // handled with care and this method is recommended to be used only - // in about:histograms and test code. - virtual std::unique_ptr SnapshotSamples() const = 0; - - // Calculate the change (delta) in histogram counts since the previous call - // to this method. Each successive call will return only those counts - // changed since the last call. - virtual std::unique_ptr SnapshotDelta() = 0; - - // Calculate the change (delta) in histogram counts since the previous call - // to SnapshotDelta() but do so without modifying any internal data as to - // what was previous logged. After such a call, no further calls to this - // method or to SnapshotDelta() should be done as the result would include - // data previously returned. Because no internal data is changed, this call - // can be made on "const" histograms such as those with data held in - // read-only memory. - virtual std::unique_ptr SnapshotFinalDelta() const = 0; - - // The following methods provide graphical histogram displays. - virtual void WriteHTMLGraph(std::string* output) const = 0; - virtual void WriteAscii(std::string* output) const = 0; - - // TODO(bcwhite): Remove this after https://crbug/836875. - virtual void ValidateHistogramContents() const; - - // Produce a JSON representation of the histogram with |verbosity_level| as - // the serialization verbosity. This is implemented with the help of - // GetParameters and GetCountAndBucketData; overwrite them to customize the - // output. - void WriteJSON(std::string* output, JSONVerbosityLevel verbosity_level) const; - - protected: - enum ReportActivity { HISTOGRAM_CREATED, HISTOGRAM_LOOKUP }; - - // Subclasses should implement this function to make SerializeInfo work. - virtual void SerializeInfoImpl(base::Pickle* pickle) const = 0; - - // Writes information about the construction parameters in |params|. - virtual void GetParameters(DictionaryValue* params) const = 0; - - // Writes information about the current (non-empty) buckets and their sample - // counts to |buckets|, the total sample count to |count| and the total sum - // to |sum|. - virtual void GetCountAndBucketData(Count* count, - int64_t* sum, - ListValue* buckets) const = 0; - - //// Produce actual graph (set of blank vs non blank char's) for a bucket. - void WriteAsciiBucketGraph(double current_size, - double max_size, - std::string* output) const; - - // Return a string description of what goes in a given bucket. - const std::string GetSimpleAsciiBucketRange(Sample sample) const; - - // Write textual description of the bucket contents (relative to histogram). - // Output is the count in the buckets, as well as the percentage. - void WriteAsciiBucketValue(Count current, - double scaled_sum, - std::string* output) const; - - // Retrieves the callback for this histogram, if one exists, and runs it - // passing |sample| as the parameter. - void FindAndRunCallback(Sample sample) const; - - // Gets a permanent string that can be used for histogram objects when the - // original is not a code constant or held in persistent memory. - static const char* GetPermanentName(const std::string& name); - - private: - friend class HistogramBaseTest; - - // A pointer to permanent storage where the histogram name is held. This can - // be code space or the output of GetPermanentName() or any other storage - // that is known to never change. This is not StringPiece because (a) char* - // is 1/2 the size and (b) StringPiece transparently casts from std::string - // which can easily lead to a pointer to non-permanent space. - // For persistent histograms, this will simply point into the persistent - // memory segment, thus avoiding duplication. For heap histograms, the - // GetPermanentName method will create the necessary copy. - const char* const histogram_name_; - - // Additional information about the histogram. - AtomicCount flags_; - - DISALLOW_COPY_AND_ASSIGN(HistogramBase); -}; - -} // namespace base - -#endif // BASE_METRICS_HISTOGRAM_BASE_H_ diff --git a/metrics/histogram_base_unittest.cc b/metrics/histogram_base_unittest.cc deleted file mode 100644 index 0314ef4d6..000000000 --- a/metrics/histogram_base_unittest.cc +++ /dev/null @@ -1,275 +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 - -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/sample_vector.h" -#include "base/metrics/sparse_histogram.h" -#include "base/metrics/statistics_recorder.h" -#include "base/pickle.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -class HistogramBaseTest : public testing::Test { - protected: - HistogramBaseTest() { - // Each test will have a clean state (no Histogram / BucketRanges - // registered). - ResetStatisticsRecorder(); - } - - ~HistogramBaseTest() override = default; - - void ResetStatisticsRecorder() { - // It is necessary to fully destruct any existing StatisticsRecorder - // before creating a new one. - statistics_recorder_.reset(); - statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting(); - } - - private: - std::unique_ptr statistics_recorder_; - - DISALLOW_COPY_AND_ASSIGN(HistogramBaseTest); -}; - -TEST_F(HistogramBaseTest, DeserializeHistogram) { - HistogramBase* histogram = Histogram::FactoryGet( - "TestHistogram", 1, 1000, 10, - (HistogramBase::kUmaTargetedHistogramFlag | - HistogramBase::kIPCSerializationSourceFlag)); - - Pickle pickle; - histogram->SerializeInfo(&pickle); - - PickleIterator iter(pickle); - HistogramBase* deserialized = DeserializeHistogramInfo(&iter); - EXPECT_EQ(histogram, deserialized); - - ResetStatisticsRecorder(); - - PickleIterator iter2(pickle); - deserialized = DeserializeHistogramInfo(&iter2); - EXPECT_TRUE(deserialized); - EXPECT_NE(histogram, deserialized); - EXPECT_EQ("TestHistogram", StringPiece(deserialized->histogram_name())); - EXPECT_TRUE(deserialized->HasConstructionArguments(1, 1000, 10)); - - // kIPCSerializationSourceFlag will be cleared. - EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, deserialized->flags()); -} - -TEST_F(HistogramBaseTest, DeserializeLinearHistogram) { - HistogramBase* histogram = LinearHistogram::FactoryGet( - "TestHistogram", 1, 1000, 10, - HistogramBase::kIPCSerializationSourceFlag); - - Pickle pickle; - histogram->SerializeInfo(&pickle); - - PickleIterator iter(pickle); - HistogramBase* deserialized = DeserializeHistogramInfo(&iter); - EXPECT_EQ(histogram, deserialized); - - ResetStatisticsRecorder(); - - PickleIterator iter2(pickle); - deserialized = DeserializeHistogramInfo(&iter2); - EXPECT_TRUE(deserialized); - EXPECT_NE(histogram, deserialized); - EXPECT_EQ("TestHistogram", StringPiece(deserialized->histogram_name())); - EXPECT_TRUE(deserialized->HasConstructionArguments(1, 1000, 10)); - EXPECT_EQ(0, deserialized->flags()); -} - -TEST_F(HistogramBaseTest, DeserializeBooleanHistogram) { - HistogramBase* histogram = BooleanHistogram::FactoryGet( - "TestHistogram", HistogramBase::kIPCSerializationSourceFlag); - - Pickle pickle; - histogram->SerializeInfo(&pickle); - - PickleIterator iter(pickle); - HistogramBase* deserialized = DeserializeHistogramInfo(&iter); - EXPECT_EQ(histogram, deserialized); - - ResetStatisticsRecorder(); - - PickleIterator iter2(pickle); - deserialized = DeserializeHistogramInfo(&iter2); - EXPECT_TRUE(deserialized); - EXPECT_NE(histogram, deserialized); - EXPECT_EQ("TestHistogram", StringPiece(deserialized->histogram_name())); - EXPECT_TRUE(deserialized->HasConstructionArguments(1, 2, 3)); - EXPECT_EQ(0, deserialized->flags()); -} - -TEST_F(HistogramBaseTest, DeserializeCustomHistogram) { - std::vector ranges; - ranges.push_back(13); - ranges.push_back(5); - ranges.push_back(9); - - HistogramBase* histogram = CustomHistogram::FactoryGet( - "TestHistogram", ranges, HistogramBase::kIPCSerializationSourceFlag); - - Pickle pickle; - histogram->SerializeInfo(&pickle); - - PickleIterator iter(pickle); - HistogramBase* deserialized = DeserializeHistogramInfo(&iter); - EXPECT_EQ(histogram, deserialized); - - ResetStatisticsRecorder(); - - PickleIterator iter2(pickle); - deserialized = DeserializeHistogramInfo(&iter2); - EXPECT_TRUE(deserialized); - EXPECT_NE(histogram, deserialized); - EXPECT_EQ("TestHistogram", StringPiece(deserialized->histogram_name())); - EXPECT_TRUE(deserialized->HasConstructionArguments(5, 13, 4)); - EXPECT_EQ(0, deserialized->flags()); -} - -TEST_F(HistogramBaseTest, DeserializeSparseHistogram) { - HistogramBase* histogram = SparseHistogram::FactoryGet( - "TestHistogram", HistogramBase::kIPCSerializationSourceFlag); - - Pickle pickle; - histogram->SerializeInfo(&pickle); - - PickleIterator iter(pickle); - HistogramBase* deserialized = DeserializeHistogramInfo(&iter); - EXPECT_EQ(histogram, deserialized); - - ResetStatisticsRecorder(); - - PickleIterator iter2(pickle); - deserialized = DeserializeHistogramInfo(&iter2); - EXPECT_TRUE(deserialized); - EXPECT_NE(histogram, deserialized); - EXPECT_EQ("TestHistogram", StringPiece(deserialized->histogram_name())); - EXPECT_EQ(0, deserialized->flags()); -} - -TEST_F(HistogramBaseTest, AddKilo) { - HistogramBase* histogram = - LinearHistogram::FactoryGet("TestAddKiloHistogram", 1, 1000, 100, 0); - - histogram->AddKilo(100, 1000); - histogram->AddKilo(200, 2000); - histogram->AddKilo(300, 1500); - - std::unique_ptr samples = histogram->SnapshotSamples(); - EXPECT_EQ(1, samples->GetCount(100)); - EXPECT_EQ(2, samples->GetCount(200)); - EXPECT_LE(1, samples->GetCount(300)); - EXPECT_GE(2, samples->GetCount(300)); -} - -TEST_F(HistogramBaseTest, AddKiB) { - HistogramBase* histogram = - LinearHistogram::FactoryGet("TestAddKiBHistogram", 1, 1000, 100, 0); - - histogram->AddKiB(100, 1024); - histogram->AddKiB(200, 2048); - histogram->AddKiB(300, 1536); - - std::unique_ptr samples = histogram->SnapshotSamples(); - EXPECT_EQ(1, samples->GetCount(100)); - EXPECT_EQ(2, samples->GetCount(200)); - EXPECT_LE(1, samples->GetCount(300)); - EXPECT_GE(2, samples->GetCount(300)); -} - -TEST_F(HistogramBaseTest, AddTimeMillisecondsGranularityOverflow) { - const HistogramBase::Sample sample_max = - std::numeric_limits::max() / 2; - HistogramBase* histogram = LinearHistogram::FactoryGet( - "TestAddTimeMillisecondsGranularity1", 1, sample_max, 100, 0); - int64_t large_positive = std::numeric_limits::max(); - // |add_count| is the number of large values that have been added to the - // histogram. We consider a number to be 'large' if it cannot be represented - // in a HistogramBase::Sample. - int add_count = 0; - while (large_positive > std::numeric_limits::max()) { - // Add the TimeDelta corresponding to |large_positive| milliseconds to the - // histogram. - histogram->AddTimeMillisecondsGranularity( - TimeDelta::FromMilliseconds(large_positive)); - ++add_count; - // Reduce the value of |large_positive|. The choice of 7 here is - // arbitrary. - large_positive /= 7; - } - std::unique_ptr samples = histogram->SnapshotSamples(); - // All of the reported values must have gone into the max overflow bucket. - EXPECT_EQ(add_count, samples->GetCount(sample_max)); - - // We now perform the analoguous operations, now with negative values with a - // large absolute value. - histogram = LinearHistogram::FactoryGet("TestAddTimeMillisecondsGranularity2", - 1, sample_max, 100, 0); - int64_t large_negative = std::numeric_limits::min(); - add_count = 0; - while (large_negative < std::numeric_limits::min()) { - histogram->AddTimeMillisecondsGranularity( - TimeDelta::FromMilliseconds(large_negative)); - ++add_count; - large_negative /= 7; - } - samples = histogram->SnapshotSamples(); - // All of the reported values must have gone into the min overflow bucket. - EXPECT_EQ(add_count, samples->GetCount(0)); -} - -TEST_F(HistogramBaseTest, AddTimeMicrosecondsGranularityOverflow) { - // Nothing to test if we don't have a high resolution clock. - if (!TimeTicks::IsHighResolution()) - return; - - const HistogramBase::Sample sample_max = - std::numeric_limits::max() / 2; - HistogramBase* histogram = LinearHistogram::FactoryGet( - "TestAddTimeMicrosecondsGranularity1", 1, sample_max, 100, 0); - int64_t large_positive = std::numeric_limits::max(); - // |add_count| is the number of large values that have been added to the - // histogram. We consider a number to be 'large' if it cannot be represented - // in a HistogramBase::Sample. - int add_count = 0; - while (large_positive > std::numeric_limits::max()) { - // Add the TimeDelta corresponding to |large_positive| microseconds to the - // histogram. - histogram->AddTimeMicrosecondsGranularity( - TimeDelta::FromMicroseconds(large_positive)); - ++add_count; - // Reduce the value of |large_positive|. The choice of 7 here is - // arbitrary. - large_positive /= 7; - } - std::unique_ptr samples = histogram->SnapshotSamples(); - // All of the reported values must have gone into the max overflow bucket. - EXPECT_EQ(add_count, samples->GetCount(sample_max)); - - // We now perform the analoguous operations, now with negative values with a - // large absolute value. - histogram = LinearHistogram::FactoryGet("TestAddTimeMicrosecondsGranularity2", - 1, sample_max, 100, 0); - int64_t large_negative = std::numeric_limits::min(); - add_count = 0; - while (large_negative < std::numeric_limits::min()) { - histogram->AddTimeMicrosecondsGranularity( - TimeDelta::FromMicroseconds(large_negative)); - ++add_count; - large_negative /= 7; - } - samples = histogram->SnapshotSamples(); - // All of the reported values must have gone into the min overflow bucket. - EXPECT_EQ(add_count, samples->GetCount(0)); -} - -} // namespace base diff --git a/metrics/histogram_delta_serialization.cc b/metrics/histogram_delta_serialization.cc deleted file mode 100644 index a74b87f0d..000000000 --- a/metrics/histogram_delta_serialization.cc +++ /dev/null @@ -1,81 +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/metrics/histogram_delta_serialization.h" - -#include "base/logging.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_snapshot_manager.h" -#include "base/metrics/statistics_recorder.h" -#include "base/numerics/safe_conversions.h" -#include "base/pickle.h" -#include "base/values.h" - -namespace base { - -namespace { - -// Create or find existing histogram and add the samples from pickle. -// Silently returns when seeing any data problem in the pickle. -void DeserializeHistogramAndAddSamples(PickleIterator* iter) { - HistogramBase* histogram = DeserializeHistogramInfo(iter); - if (!histogram) - return; - - if (histogram->flags() & HistogramBase::kIPCSerializationSourceFlag) { - DVLOG(1) << "Single process mode, histogram observed and not copied: " - << histogram->histogram_name(); - return; - } - histogram->AddSamplesFromPickle(iter); -} - -} // namespace - -HistogramDeltaSerialization::HistogramDeltaSerialization( - const std::string& caller_name) - : histogram_snapshot_manager_(this), serialized_deltas_(nullptr) {} - -HistogramDeltaSerialization::~HistogramDeltaSerialization() = default; - -void HistogramDeltaSerialization::PrepareAndSerializeDeltas( - std::vector* serialized_deltas, - bool include_persistent) { - DCHECK(thread_checker_.CalledOnValidThread()); - - serialized_deltas_ = serialized_deltas; - // Note: Before serializing, we set the kIPCSerializationSourceFlag for all - // the histograms, so that the receiving process can distinguish them from the - // local histograms. - StatisticsRecorder::PrepareDeltas( - include_persistent, Histogram::kIPCSerializationSourceFlag, - Histogram::kNoFlags, &histogram_snapshot_manager_); - serialized_deltas_ = nullptr; -} - -// static -void HistogramDeltaSerialization::DeserializeAndAddSamples( - const std::vector& serialized_deltas) { - for (std::vector::const_iterator it = serialized_deltas.begin(); - it != serialized_deltas.end(); ++it) { - Pickle pickle(it->data(), checked_cast(it->size())); - PickleIterator iter(pickle); - DeserializeHistogramAndAddSamples(&iter); - } -} - -void HistogramDeltaSerialization::RecordDelta( - const HistogramBase& histogram, - const HistogramSamples& snapshot) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK_NE(0, snapshot.TotalCount()); - - Pickle pickle; - histogram.SerializeInfo(&pickle); - snapshot.Serialize(&pickle); - serialized_deltas_->push_back( - std::string(static_cast(pickle.data()), pickle.size())); -} - -} // namespace base diff --git a/metrics/histogram_delta_serialization.h b/metrics/histogram_delta_serialization.h deleted file mode 100644 index 57ebd2c42..000000000 --- a/metrics/histogram_delta_serialization.h +++ /dev/null @@ -1,61 +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_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_ -#define BASE_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_ - -#include -#include -#include - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/metrics/histogram_flattener.h" -#include "base/metrics/histogram_snapshot_manager.h" -#include "base/threading/thread_checker.h" - -namespace base { - -class HistogramBase; - -// Serializes and restores histograms deltas. -class BASE_EXPORT HistogramDeltaSerialization : public HistogramFlattener { - public: - // |caller_name| is string used in histograms for counting inconsistencies. - explicit HistogramDeltaSerialization(const std::string& caller_name); - ~HistogramDeltaSerialization() override; - - // Computes deltas in histogram bucket counts relative to the previous call to - // this method. Stores the deltas in serialized form into |serialized_deltas|. - // If |serialized_deltas| is null, no data is serialized, though the next call - // will compute the deltas relative to this one. Setting |include_persistent| - // will include histograms held in persistent memory (and thus may be reported - // elsewhere); otherwise only histograms local to this process are serialized. - void PrepareAndSerializeDeltas(std::vector* serialized_deltas, - bool include_persistent); - - // Deserialize deltas and add samples to corresponding histograms, creating - // them if necessary. Silently ignores errors in |serialized_deltas|. - static void DeserializeAndAddSamples( - const std::vector& serialized_deltas); - - private: - // HistogramFlattener implementation. - void RecordDelta(const HistogramBase& histogram, - const HistogramSamples& snapshot) override; - - ThreadChecker thread_checker_; - - // Calculates deltas in histogram counters. - HistogramSnapshotManager histogram_snapshot_manager_; - - // Output buffer for serialized deltas. - std::vector* serialized_deltas_; - - DISALLOW_COPY_AND_ASSIGN(HistogramDeltaSerialization); -}; - -} // namespace base - -#endif // BASE_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_ diff --git a/metrics/histogram_delta_serialization_unittest.cc b/metrics/histogram_delta_serialization_unittest.cc deleted file mode 100644 index 719bc7097..000000000 --- a/metrics/histogram_delta_serialization_unittest.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/metrics/histogram_delta_serialization.h" - -#include - -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/statistics_recorder.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(HistogramDeltaSerializationTest, DeserializeHistogramAndAddSamples) { - std::unique_ptr statistic_recorder( - StatisticsRecorder::CreateTemporaryForTesting()); - HistogramDeltaSerialization serializer("HistogramDeltaSerializationTest"); - std::vector deltas; - // Nothing was changed yet. - serializer.PrepareAndSerializeDeltas(&deltas, true); - EXPECT_TRUE(deltas.empty()); - - HistogramBase* histogram = Histogram::FactoryGet( - "TestHistogram", 1, 1000, 10, HistogramBase::kIPCSerializationSourceFlag); - histogram->Add(1); - histogram->Add(10); - histogram->Add(100); - histogram->Add(1000); - - serializer.PrepareAndSerializeDeltas(&deltas, true); - EXPECT_FALSE(deltas.empty()); - - HistogramDeltaSerialization::DeserializeAndAddSamples(deltas); - - // The histogram has kIPCSerializationSourceFlag. So samples will be ignored. - std::unique_ptr snapshot(histogram->SnapshotSamples()); - EXPECT_EQ(1, snapshot->GetCount(1)); - EXPECT_EQ(1, snapshot->GetCount(10)); - EXPECT_EQ(1, snapshot->GetCount(100)); - EXPECT_EQ(1, snapshot->GetCount(1000)); - - // Clear kIPCSerializationSourceFlag to emulate multi-process usage. - histogram->ClearFlags(HistogramBase::kIPCSerializationSourceFlag); - HistogramDeltaSerialization::DeserializeAndAddSamples(deltas); - - std::unique_ptr snapshot2(histogram->SnapshotSamples()); - EXPECT_EQ(2, snapshot2->GetCount(1)); - EXPECT_EQ(2, snapshot2->GetCount(10)); - EXPECT_EQ(2, snapshot2->GetCount(100)); - EXPECT_EQ(2, snapshot2->GetCount(1000)); -} - -} // namespace base diff --git a/metrics/histogram_flattener.h b/metrics/histogram_flattener.h deleted file mode 100644 index 6a5e3f429..000000000 --- a/metrics/histogram_flattener.h +++ /dev/null @@ -1,35 +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_METRICS_HISTOGRAM_FLATTENER_H_ -#define BASE_METRICS_HISTOGRAM_FLATTENER_H_ - -#include -#include - -#include "base/macros.h" -#include "base/metrics/histogram.h" - -namespace base { - -class HistogramSamples; - -// HistogramFlattener is an interface used by HistogramSnapshotManager, which -// handles the logistics of gathering up available histograms for recording. -class BASE_EXPORT HistogramFlattener { - public: - virtual void RecordDelta(const HistogramBase& histogram, - const HistogramSamples& snapshot) = 0; - - protected: - HistogramFlattener() = default; - virtual ~HistogramFlattener() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(HistogramFlattener); -}; - -} // namespace base - -#endif // BASE_METRICS_HISTOGRAM_FLATTENER_H_ diff --git a/metrics/histogram_functions.cc b/metrics/histogram_functions.cc deleted file mode 100644 index 31bf219e5..000000000 --- a/metrics/histogram_functions.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2016 The Chromium 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_functions.h" - -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/sparse_histogram.h" -#include "base/time/time.h" - -namespace base { - -void UmaHistogramBoolean(const std::string& name, bool sample) { - HistogramBase* histogram = BooleanHistogram::FactoryGet( - name, HistogramBase::kUmaTargetedHistogramFlag); - histogram->Add(sample); -} - -void UmaHistogramExactLinear(const std::string& name, - int sample, - int value_max) { - HistogramBase* histogram = - LinearHistogram::FactoryGet(name, 1, value_max, value_max + 1, - HistogramBase::kUmaTargetedHistogramFlag); - histogram->Add(sample); -} - -void UmaHistogramPercentage(const std::string& name, int percent) { - UmaHistogramExactLinear(name, percent, 100); -} - -void UmaHistogramCustomCounts(const std::string& name, - int sample, - int min, - int max, - int buckets) { - HistogramBase* histogram = Histogram::FactoryGet( - name, min, max, buckets, HistogramBase::kUmaTargetedHistogramFlag); - histogram->Add(sample); -} - -void UmaHistogramCounts100(const std::string& name, int sample) { - UmaHistogramCustomCounts(name, sample, 1, 100, 50); -} - -void UmaHistogramCounts1000(const std::string& name, int sample) { - UmaHistogramCustomCounts(name, sample, 1, 1000, 50); -} - -void UmaHistogramCounts10000(const std::string& name, int sample) { - UmaHistogramCustomCounts(name, sample, 1, 10000, 50); -} - -void UmaHistogramCounts100000(const std::string& name, int sample) { - UmaHistogramCustomCounts(name, sample, 1, 100000, 50); -} - -void UmaHistogramCounts1M(const std::string& name, int sample) { - UmaHistogramCustomCounts(name, sample, 1, 1000000, 50); -} - -void UmaHistogramCounts10M(const std::string& name, int sample) { - UmaHistogramCustomCounts(name, sample, 1, 10000000, 50); -} - -void UmaHistogramCustomTimes(const std::string& name, - TimeDelta sample, - TimeDelta min, - TimeDelta max, - int buckets) { - HistogramBase* histogram = Histogram::FactoryTimeGet( - name, min, max, buckets, HistogramBase::kUmaTargetedHistogramFlag); - histogram->AddTimeMillisecondsGranularity(sample); -} - -void UmaHistogramTimes(const std::string& name, TimeDelta sample) { - UmaHistogramCustomTimes(name, sample, TimeDelta::FromMilliseconds(1), - TimeDelta::FromSeconds(10), 50); -} - -void UmaHistogramMediumTimes(const std::string& name, TimeDelta sample) { - UmaHistogramCustomTimes(name, sample, TimeDelta::FromMilliseconds(1), - TimeDelta::FromMinutes(3), 50); -} - -void UmaHistogramLongTimes(const std::string& name, TimeDelta sample) { - UmaHistogramCustomTimes(name, sample, TimeDelta::FromMilliseconds(1), - TimeDelta::FromHours(1), 50); -} - -void UmaHistogramMemoryKB(const std::string& name, int sample) { - UmaHistogramCustomCounts(name, sample, 1000, 500000, 50); -} - -void UmaHistogramMemoryMB(const std::string& name, int sample) { - UmaHistogramCustomCounts(name, sample, 1, 1000, 50); -} - -void UmaHistogramMemoryLargeMB(const std::string& name, int sample) { - UmaHistogramCustomCounts(name, sample, 1, 64000, 100); -} - -void UmaHistogramSparse(const std::string& name, int sample) { - HistogramBase* histogram = SparseHistogram::FactoryGet( - name, HistogramBase::kUmaTargetedHistogramFlag); - histogram->Add(sample); -} - -} // namespace base diff --git a/metrics/histogram_functions.h b/metrics/histogram_functions.h deleted file mode 100644 index 60c005726..000000000 --- a/metrics/histogram_functions.h +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_METRICS_HISTOGRAM_FUNCTIONS_H_ -#define BASE_METRICS_HISTOGRAM_FUNCTIONS_H_ - -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_base.h" -#include "base/time/time.h" - -// Functions for recording metrics. -// -// For best practices on deciding when to emit to a histogram and what form -// the histogram should take, see -// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md - -// Functions for recording UMA histograms. These can be used for cases -// when the histogram name is generated at runtime. The functionality is -// equivalent to macros defined in histogram_macros.h but allowing non-constant -// histogram names. These functions are slower compared to their macro -// equivalent because the histogram objects are not cached between calls. -// So, these shouldn't be used in performance critical code. -namespace base { - -// For histograms with linear buckets. -// Used for capturing integer data with a linear bucketing scheme. This can be -// used when you want the exact value of some small numeric count, with a max of -// 100 or less. If you need to capture a range of greater than 100, we recommend -// the use of the COUNT histograms below. -// Sample usage: -// base::UmaHistogramExactLinear("Histogram.Linear", some_value, 10); -BASE_EXPORT void UmaHistogramExactLinear(const std::string& name, - int sample, - int value_max); - -// For adding a sample to an enumerated histogram. -// Sample usage: -// // These values are persisted to logs. Entries should not be renumbered and -// // numeric values should never be reused. -// enum class MyEnum { -// FIRST_VALUE = 0, -// SECOND_VALUE = 1, -// ... -// FINAL_VALUE = N, -// COUNT -// }; -// base::UmaHistogramEnumeration("My.Enumeration", -// MyEnum::SOME_VALUE, MyEnum::COUNT); -// -// Note: The value in |sample| must be strictly less than |enum_size|. -template -void UmaHistogramEnumeration(const std::string& name, T sample, T enum_size) { - static_assert(std::is_enum::value, - "Non enum passed to UmaHistogramEnumeration"); - DCHECK_LE(static_cast(enum_size), static_cast(INT_MAX)); - DCHECK_LT(static_cast(sample), static_cast(enum_size)); - return UmaHistogramExactLinear(name, static_cast(sample), - static_cast(enum_size)); -} - -// Same as above, but uses T::kMaxValue as the inclusive maximum value of the -// enum. -template -void UmaHistogramEnumeration(const std::string& name, T sample) { - static_assert(std::is_enum::value, - "Non enum passed to UmaHistogramEnumeration"); - DCHECK_LE(static_cast(T::kMaxValue), - static_cast(INT_MAX) - 1); - DCHECK_LE(static_cast(sample), - static_cast(T::kMaxValue)); - return UmaHistogramExactLinear(name, static_cast(sample), - static_cast(T::kMaxValue) + 1); -} - -// For adding boolean sample to histogram. -// Sample usage: -// base::UmaHistogramBoolean("My.Boolean", true) -BASE_EXPORT void UmaHistogramBoolean(const std::string& name, bool sample); - -// For adding histogram with percent. -// Percents are integer between 1 and 100. -// Sample usage: -// base::UmaHistogramPercentage("My.Percent", 69) -BASE_EXPORT void UmaHistogramPercentage(const std::string& name, int percent); - -// For adding counts histogram. -// Sample usage: -// base::UmaHistogramCustomCounts("My.Counts", some_value, 1, 600, 30) -BASE_EXPORT void UmaHistogramCustomCounts(const std::string& name, - int sample, - int min, - int max, - int buckets); - -// Counts specialization for maximum counts 100, 1000, 10k, 100k, 1M and 10M. -BASE_EXPORT void UmaHistogramCounts100(const std::string& name, int sample); -BASE_EXPORT void UmaHistogramCounts1000(const std::string& name, int sample); -BASE_EXPORT void UmaHistogramCounts10000(const std::string& name, int sample); -BASE_EXPORT void UmaHistogramCounts100000(const std::string& name, int sample); -BASE_EXPORT void UmaHistogramCounts1M(const std::string& name, int sample); -BASE_EXPORT void UmaHistogramCounts10M(const std::string& name, int sample); - -// For histograms storing times. -BASE_EXPORT void UmaHistogramCustomTimes(const std::string& name, - TimeDelta sample, - TimeDelta min, - TimeDelta max, - int buckets); -// For short timings from 1 ms up to 10 seconds (50 buckets). -BASE_EXPORT void UmaHistogramTimes(const std::string& name, TimeDelta sample); -// For medium timings up to 3 minutes (50 buckets). -BASE_EXPORT void UmaHistogramMediumTimes(const std::string& name, - TimeDelta sample); -// For time intervals up to 1 hr (50 buckets). -BASE_EXPORT void UmaHistogramLongTimes(const std::string& name, - TimeDelta sample); - -// For recording memory related histograms. -// Used to measure common KB-granularity memory stats. Range is up to 500M. -BASE_EXPORT void UmaHistogramMemoryKB(const std::string& name, int sample); -// Used to measure common MB-granularity memory stats. Range is up to ~1G. -BASE_EXPORT void UmaHistogramMemoryMB(const std::string& name, int sample); -// Used to measure common MB-granularity memory stats. Range is up to ~64G. -BASE_EXPORT void UmaHistogramMemoryLargeMB(const std::string& name, int sample); - -// For recording sparse histograms. -// The |sample| can be a negative or non-negative number. -// -// Sparse histograms are well suited for recording counts of exact sample values -// that are sparsely distributed over a relatively large range, in cases where -// ultra-fast performance is not critical. For instance, Sqlite.Version.* are -// sparse because for any given database, there's going to be exactly one -// version logged. -// -// Performance: -// ------------ -// Sparse histograms are typically more memory-efficient but less time-efficient -// than other histograms. Essentially, they sparse histograms use a map rather -// than a vector for their backing storage; they also require lock acquisition -// to increment a sample, whereas other histogram do not. Hence, each increment -// operation is a bit slower than for other histograms. But, if the data is -// sparse, then they use less memory client-side, because they allocate buckets -// on demand rather than preallocating. -// -// Data size: -// ---------- -// Note that server-side, we still need to load all buckets, across all users, -// at once. Thus, please avoid exploding such histograms, i.e. uploading many -// many distinct values to the server (across all users). Concretely, keep the -// number of distinct values <= 100 ideally, definitely <= 1000. If you have no -// guarantees on the range of your data, use clamping, e.g.: -// UmaHistogramSparse("MyHistogram", ClampToRange(value, 0, 200)); -BASE_EXPORT void UmaHistogramSparse(const std::string& name, int sample); - -} // namespace base - -#endif // BASE_METRICS_HISTOGRAM_FUNCTIONS_H_ diff --git a/metrics/histogram_functions_unittest.cc b/metrics/histogram_functions_unittest.cc deleted file mode 100644 index 32f439469..000000000 --- a/metrics/histogram_functions_unittest.cc +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2016 The Chromium 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_functions.h" - -#include "base/metrics/histogram_macros.h" -#include "base/test/metrics/histogram_tester.h" -#include "base/time/time.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -enum UmaHistogramTestingEnum { - UMA_HISTOGRAM_TESTING_ENUM_FIRST, - UMA_HISTOGRAM_TESTING_ENUM_SECOND, - UMA_HISTOGRAM_TESTING_ENUM_THIRD -}; - -TEST(HistogramFunctionsTest, ExactLinear) { - std::string histogram("Testing.UMA.HistogramExactLinear"); - HistogramTester tester; - UmaHistogramExactLinear(histogram, 10, 100); - tester.ExpectUniqueSample(histogram, 10, 1); - UmaHistogramExactLinear(histogram, 20, 100); - UmaHistogramExactLinear(histogram, 10, 100); - tester.ExpectBucketCount(histogram, 10, 2); - tester.ExpectBucketCount(histogram, 20, 1); - tester.ExpectTotalCount(histogram, 3); - // Test linear buckets overflow. - UmaHistogramExactLinear(histogram, 200, 100); - tester.ExpectBucketCount(histogram, 101, 1); - tester.ExpectTotalCount(histogram, 4); - // Test linear buckets underflow. - UmaHistogramExactLinear(histogram, 0, 100); - tester.ExpectBucketCount(histogram, 0, 1); - tester.ExpectTotalCount(histogram, 5); -} - -TEST(HistogramFunctionsTest, Enumeration) { - std::string histogram("Testing.UMA.HistogramEnumeration"); - HistogramTester tester; - UmaHistogramEnumeration(histogram, UMA_HISTOGRAM_TESTING_ENUM_FIRST, - UMA_HISTOGRAM_TESTING_ENUM_THIRD); - tester.ExpectUniqueSample(histogram, UMA_HISTOGRAM_TESTING_ENUM_FIRST, 1); - - // Verify the overflow & underflow bucket exists. - UMA_HISTOGRAM_ENUMERATION( - histogram, static_cast(UMA_HISTOGRAM_TESTING_ENUM_THIRD) + 10, - static_cast(UMA_HISTOGRAM_TESTING_ENUM_THIRD)); - tester.ExpectBucketCount( - histogram, static_cast(UMA_HISTOGRAM_TESTING_ENUM_THIRD) + 1, 1); - tester.ExpectTotalCount(histogram, 2); -} - -TEST(HistogramFunctionsTest, Boolean) { - std::string histogram("Testing.UMA.HistogramBoolean"); - HistogramTester tester; - UmaHistogramBoolean(histogram, true); - tester.ExpectUniqueSample(histogram, 1, 1); - UmaHistogramBoolean(histogram, false); - tester.ExpectBucketCount(histogram, 0, 1); - tester.ExpectTotalCount(histogram, 2); -} - -TEST(HistogramFunctionsTest, Percentage) { - std::string histogram("Testing.UMA.HistogramPercentage"); - HistogramTester tester; - UmaHistogramPercentage(histogram, 50); - tester.ExpectUniqueSample(histogram, 50, 1); - // Test overflows. - UmaHistogramPercentage(histogram, 110); - tester.ExpectBucketCount(histogram, 101, 1); - tester.ExpectTotalCount(histogram, 2); -} - -TEST(HistogramFunctionsTest, Counts) { - std::string histogram("Testing.UMA.HistogramCount.Custom"); - HistogramTester tester; - UmaHistogramCustomCounts(histogram, 10, 1, 100, 10); - tester.ExpectUniqueSample(histogram, 10, 1); - UmaHistogramCustomCounts(histogram, 20, 1, 100, 10); - UmaHistogramCustomCounts(histogram, 20, 1, 100, 10); - UmaHistogramCustomCounts(histogram, 20, 1, 100, 10); - tester.ExpectBucketCount(histogram, 20, 3); - tester.ExpectTotalCount(histogram, 4); - UmaHistogramCustomCounts(histogram, 110, 1, 100, 10); - tester.ExpectBucketCount(histogram, 101, 1); - tester.ExpectTotalCount(histogram, 5); -} - -TEST(HistogramFunctionsTest, Times) { - std::string histogram("Testing.UMA.HistogramTimes"); - HistogramTester tester; - UmaHistogramTimes(histogram, TimeDelta::FromSeconds(1)); - tester.ExpectTimeBucketCount(histogram, TimeDelta::FromSeconds(1), 1); - tester.ExpectTotalCount(histogram, 1); - UmaHistogramTimes(histogram, TimeDelta::FromSeconds(9)); - tester.ExpectTimeBucketCount(histogram, TimeDelta::FromSeconds(9), 1); - tester.ExpectTotalCount(histogram, 2); - UmaHistogramTimes(histogram, TimeDelta::FromSeconds(10)); // Overflows - tester.ExpectTimeBucketCount(histogram, TimeDelta::FromSeconds(10), 1); - UmaHistogramTimes(histogram, TimeDelta::FromSeconds(20)); // Overflows. - // Check the value by picking any overflow time. - tester.ExpectTimeBucketCount(histogram, TimeDelta::FromSeconds(11), 2); - tester.ExpectTotalCount(histogram, 4); -} - -TEST(HistogramFunctionsTest, Sparse_SupportsLargeRange) { - std::string histogram("Testing.UMA.HistogramSparse"); - HistogramTester tester; - UmaHistogramSparse(histogram, 0); - UmaHistogramSparse(histogram, 123456789); - UmaHistogramSparse(histogram, 123456789); - EXPECT_THAT(tester.GetAllSamples(histogram), - testing::ElementsAre(Bucket(0, 1), Bucket(123456789, 2))); -} - -TEST(HistogramFunctionsTest, Sparse_SupportsNegativeValues) { - std::string histogram("Testing.UMA.HistogramSparse"); - HistogramTester tester; - UmaHistogramSparse(histogram, -1); - tester.ExpectUniqueSample(histogram, -1, 1); -} - -} // namespace base. diff --git a/metrics/histogram_macros.h b/metrics/histogram_macros.h deleted file mode 100644 index 93bd4bdff..000000000 --- a/metrics/histogram_macros.h +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_METRICS_HISTOGRAM_MACROS_H_ -#define BASE_METRICS_HISTOGRAM_MACROS_H_ - -#include "base/macros.h" -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_macros_internal.h" -#include "base/metrics/histogram_macros_local.h" -#include "base/time/time.h" - - -// Macros for efficient use of histograms. -// -// For best practices on deciding when to emit to a histogram and what form -// the histogram should take, see -// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md - -// TODO(rkaplow): Link to proper documentation on metric creation once we have -// it in a good state. - -// All of these macros must be called with |name| as a runtime constant - it -// doesn't have to literally be a constant, but it must be the same string on -// all calls from a particular call site. If this rule is violated, it is -// possible the data will be written to the wrong histogram. - -//------------------------------------------------------------------------------ -// Enumeration histograms. - -// These macros create histograms for enumerated data. Ideally, the data should -// be of the form of "event occurs, log the result". We recommended not putting -// related but not directly connected data as enums within the same histogram. -// You should be defining an associated Enum, and the input sample should be -// an element of the Enum. -// All of these macros must be called with |name| as a runtime constant. - -// The first variant of UMA_HISTOGRAM_ENUMERATION accepts two arguments: the -// histogram name and the enum sample. It deduces the correct boundary value to -// use by looking for an enumerator with the name kMaxValue. kMaxValue should -// share the value of the highest enumerator: this avoids switch statements -// having to handle a sentinel no-op value. -// -// Sample usage: -// // These values are persisted to logs. Entries should not be renumbered and -// // numeric values should never be reused. -// enum class MyEnum { -// kFirstValue = 0, -// kSecondValue = 1, -// ... -// kFinalValue = N, -// kMaxValue = kFinalValue, -// }; -// UMA_HISTOGRAM_ENUMERATION("My.Enumeration", MyEnum::kSomeValue); -// -// The second variant requires three arguments: the first two are the same as -// before, and the third argument is the enum boundary: this must be strictly -// greater than any other enumerator that will be sampled. -// -// Sample usage: -// // These values are persisted to logs. Entries should not be renumbered and -// // numeric values should never be reused. -// enum class MyEnum { -// FIRST_VALUE = 0, -// SECOND_VALUE = 1, -// ... -// FINAL_VALUE = N, -// COUNT -// }; -// UMA_HISTOGRAM_ENUMERATION("My.Enumeration", -// MyEnum::SOME_VALUE, MyEnum::COUNT); -// -// Note: If the enum is used in a switch, it is often desirable to avoid writing -// a case statement to handle an unused sentinel value (i.e. COUNT in the above -// example). For scoped enums, this is awkward since it requires casting the -// enum to an arithmetic type and adding one. Instead, prefer the two argument -// version of the macro which automatically deduces the boundary from kMaxValue. -#define UMA_HISTOGRAM_ENUMERATION(name, ...) \ - CR_EXPAND_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ - __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \ - INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY)( \ - name, __VA_ARGS__, base::HistogramBase::kUmaTargetedHistogramFlag)) - -// As above but "scaled" count to avoid overflows caused by increments of -// large amounts. See UMA_HISTOGRAM_SCALED_EXACT_LINEAR for more information. -// Only the new format utilizing an internal kMaxValue is supported. -// It'll be necessary to #include "base/lazy_instance.h" to use this macro. -#define UMA_HISTOGRAM_SCALED_ENUMERATION(name, sample, count, scale) \ - INTERNAL_HISTOGRAM_SCALED_ENUMERATION_WITH_FLAG( \ - name, sample, count, scale, \ - base::HistogramBase::kUmaTargetedHistogramFlag) - -// Histogram for boolean values. - -// Sample usage: -// UMA_HISTOGRAM_BOOLEAN("Histogram.Boolean", bool); -#define UMA_HISTOGRAM_BOOLEAN(name, sample) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ - base::BooleanHistogram::FactoryGet(name, \ - base::HistogramBase::kUmaTargetedHistogramFlag)) - -//------------------------------------------------------------------------------ -// Linear histograms. - -// All of these macros must be called with |name| as a runtime constant. - -// Used for capturing integer data with a linear bucketing scheme. This can be -// used when you want the exact value of some small numeric count, with a max of -// 100 or less. If you need to capture a range of greater than 100, we recommend -// the use of the COUNT histograms below. - -// Sample usage: -// UMA_HISTOGRAM_EXACT_LINEAR("Histogram.Linear", count, 10); -#define UMA_HISTOGRAM_EXACT_LINEAR(name, sample, value_max) \ - INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG( \ - name, sample, value_max, base::HistogramBase::kUmaTargetedHistogramFlag) - -// Used for capturing basic percentages. This will be 100 buckets of size 1. - -// Sample usage: -// UMA_HISTOGRAM_PERCENTAGE("Histogram.Percent", percent_as_int); -#define UMA_HISTOGRAM_PERCENTAGE(name, percent_as_int) \ - UMA_HISTOGRAM_EXACT_LINEAR(name, percent_as_int, 101) - -//------------------------------------------------------------------------------ -// Scaled Linear histograms. - -// These take |count| and |scale| parameters to allow cumulative reporting of -// large numbers. Only the scaled count is reported but the reminder is kept so -// multiple calls will accumulate correctly. Only "exact linear" is supported. -// It'll be necessary to #include "base/lazy_instance.h" to use this macro. - -#define UMA_HISTOGRAM_SCALED_EXACT_LINEAR(name, sample, count, value_max, \ - scale) \ - INTERNAL_HISTOGRAM_SCALED_EXACT_LINEAR_WITH_FLAG( \ - name, sample, count, value_max, scale, \ - base::HistogramBase::kUmaTargetedHistogramFlag) - -//------------------------------------------------------------------------------ -// Count histograms. These are used for collecting numeric data. Note that we -// have macros for more specialized use cases below (memory, time, percentages). - -// The number suffixes here refer to the max size of the sample, i.e. COUNT_1000 -// will be able to collect samples of counts up to 1000. The default number of -// buckets in all default macros is 50. We recommend erring on the side of too -// large a range versus too short a range. -// These macros default to exponential histograms - i.e. the lengths of the -// bucket ranges exponentially increase as the sample range increases. -// These should *not* be used if you are interested in exact counts, i.e. a -// bucket range of 1. In these cases, you should use the ENUMERATION macros -// defined later. These should also not be used to capture the number of some -// event, i.e. "button X was clicked N times". In this cases, an enum should be -// used, ideally with an appropriate baseline enum entry included. -// All of these macros must be called with |name| as a runtime constant. - -// Sample usage: -// UMA_HISTOGRAM_COUNTS_1M("My.Histogram", sample); - -#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 100, 50) - -#define UMA_HISTOGRAM_COUNTS_1000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 1000, 50) - -#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 10000, 50) - -#define UMA_HISTOGRAM_COUNTS_100000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 100000, 50) - -#define UMA_HISTOGRAM_COUNTS_1M(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 1000000, 50) - -#define UMA_HISTOGRAM_COUNTS_10M(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 10000000, 50) - -// This can be used when the default ranges are not sufficient. This macro lets -// the metric developer customize the min and max of the sampled range, as well -// as the number of buckets recorded. -// Any data outside the range here will be put in underflow and overflow -// buckets. Min values should be >=1 as emitted 0s will still go into the -// underflow bucket. - -// Sample usage: -// UMA_HISTOGRAM_CUSTOM_COUNTS("My.Histogram", 1, 100000000, 100); -#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ - INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \ - name, sample, min, max, bucket_count, \ - base::HistogramBase::kUmaTargetedHistogramFlag) - -//------------------------------------------------------------------------------ -// Timing histograms. These are used for collecting timing data (generally -// latencies). - -// These macros create exponentially sized histograms (lengths of the bucket -// ranges exponentially increase as the sample range increases). The input -// sample is a base::TimeDelta. The output data is measured in ms granularity. -// All of these macros must be called with |name| as a runtime constant. - -// Sample usage: -// UMA_HISTOGRAM_TIMES("My.Timing.Histogram", time_delta); - -// Short timings - up to 10 seconds. For high-resolution (microseconds) timings, -// see UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES. -#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ - name, sample, base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromSeconds(10), 50) - -// Medium timings - up to 3 minutes. Note this starts at 10ms (no good reason, -// but not worth changing). -#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ - name, sample, base::TimeDelta::FromMilliseconds(10), \ - base::TimeDelta::FromMinutes(3), 50) - -// Long timings - up to an hour. -#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ - name, sample, base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromHours(1), 50) - -// Long timings with higher granularity - up to an hour with 100 buckets. -#define UMA_HISTOGRAM_LONG_TIMES_100(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ - name, sample, base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromHours(1), 100) - -// This can be used when the default ranges are not sufficient. This macro lets -// the metric developer customize the min and max of the sampled range, as well -// as the number of buckets recorded. - -// Sample usage: -// UMA_HISTOGRAM_CUSTOM_TIMES("Very.Long.Timing.Histogram", time_delta, -// base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(1), 100); -#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ - STATIC_HISTOGRAM_POINTER_BLOCK( \ - name, AddTimeMillisecondsGranularity(sample), \ - base::Histogram::FactoryTimeGet( \ - name, min, max, bucket_count, \ - base::HistogramBase::kUmaTargetedHistogramFlag)) - -// Same as UMA_HISTOGRAM_CUSTOM_TIMES but reports |sample| in microseconds, -// dropping the report if this client doesn't have a high-resolution clock. -// -// Note: dropping reports on clients with low-resolution clocks means these -// reports will be biased to a portion of the population on Windows. See -// Windows.HasHighResolutionTimeTicks for the affected sample. -// -// Sample usage: -// UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES( -// "High.Resolution.TimingMicroseconds.Histogram", time_delta, -// base::TimeDelta::FromMicroseconds(1), -// base::TimeDelta::FromMilliseconds(10), 100); -#define UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(name, sample, min, max, \ - bucket_count) \ - STATIC_HISTOGRAM_POINTER_BLOCK( \ - name, AddTimeMicrosecondsGranularity(sample), \ - base::Histogram::FactoryMicrosecondsTimeGet( \ - name, min, max, bucket_count, \ - base::HistogramBase::kUmaTargetedHistogramFlag)) - -// Scoped class which logs its time on this earth as a UMA statistic. This is -// recommended for when you want a histogram which measures the time it takes -// for a method to execute. This measures up to 10 seconds. It uses -// UMA_HISTOGRAM_TIMES under the hood. - -// Sample usage: -// void Function() { -// SCOPED_UMA_HISTOGRAM_TIMER("Component.FunctionTime"); -// ... -// } -#define SCOPED_UMA_HISTOGRAM_TIMER(name) \ - INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, false, __COUNTER__) - -// Similar scoped histogram timer, but this uses UMA_HISTOGRAM_LONG_TIMES_100, -// which measures up to an hour, and uses 100 buckets. This is more expensive -// to store, so only use if this often takes >10 seconds. -#define SCOPED_UMA_HISTOGRAM_LONG_TIMER(name) \ - INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, true, __COUNTER__) - - -//------------------------------------------------------------------------------ -// Memory histograms. - -// These macros create exponentially sized histograms (lengths of the bucket -// ranges exponentially increase as the sample range increases). The input -// sample must be a number measured in kilobytes. -// All of these macros must be called with |name| as a runtime constant. - -// Sample usage: -// UMA_HISTOGRAM_MEMORY_KB("My.Memory.Histogram", memory_in_kb); - -// Used to measure common KB-granularity memory stats. Range is up to 500000KB - -// approximately 500M. -#define UMA_HISTOGRAM_MEMORY_KB(name, sample) \ - UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1000, 500000, 50) - -// Used to measure common MB-granularity memory stats. Range is up to ~64G. -#define UMA_HISTOGRAM_MEMORY_LARGE_MB(name, sample) \ - UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 64000, 100) - - -//------------------------------------------------------------------------------ -// Stability-specific histograms. - -// Histograms logged in as stability histograms will be included in the initial -// stability log. See comments by declaration of -// MetricsService::PrepareInitialStabilityLog(). -// All of these macros must be called with |name| as a runtime constant. - -// For details on usage, see the documentation on the non-stability equivalents. - -#define UMA_STABILITY_HISTOGRAM_COUNTS_100(name, sample) \ - UMA_STABILITY_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50) - -#define UMA_STABILITY_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, \ - bucket_count) \ - INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \ - name, sample, min, max, bucket_count, \ - base::HistogramBase::kUmaStabilityHistogramFlag) - -#define UMA_STABILITY_HISTOGRAM_ENUMERATION(name, sample, enum_max) \ - INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG( \ - name, sample, enum_max, \ - base::HistogramBase::kUmaStabilityHistogramFlag) - -//------------------------------------------------------------------------------ -// Histogram instantiation helpers. - -// Support a collection of histograms, perhaps one for each entry in an -// enumeration. This macro manages a block of pointers, adding to a specific -// one by its index. -// -// A typical instantiation looks something like this: -// STATIC_HISTOGRAM_POINTER_GROUP( -// GetHistogramNameForIndex(histogram_index), -// histogram_index, MAXIMUM_HISTOGRAM_INDEX, Add(some_delta), -// base::Histogram::FactoryGet( -// GetHistogramNameForIndex(histogram_index), -// MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT, -// base::HistogramBase::kUmaTargetedHistogramFlag)); -// -// Though it seems inefficient to generate the name twice, the first -// instance will be used only for DCHECK builds and the second will -// execute only during the first access to the given index, after which -// the pointer is cached and the name never needed again. -#define STATIC_HISTOGRAM_POINTER_GROUP(constant_histogram_name, index, \ - constant_maximum, \ - histogram_add_method_invocation, \ - histogram_factory_get_invocation) \ - do { \ - static base::subtle::AtomicWord atomic_histograms[constant_maximum]; \ - DCHECK_LE(0, index); \ - DCHECK_LT(index, constant_maximum); \ - HISTOGRAM_POINTER_USE(&atomic_histograms[index], constant_histogram_name, \ - histogram_add_method_invocation, \ - histogram_factory_get_invocation); \ - } while (0) - -//------------------------------------------------------------------------------ -// Deprecated histogram macros. Not recommended for current use. - -// Legacy name for UMA_HISTOGRAM_COUNTS_1M. Suggest using explicit naming -// and not using this macro going forward. -#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1, 1000000, 50) - -// MB-granularity memory metric. This has a short max (1G). -#define UMA_HISTOGRAM_MEMORY_MB(name, sample) \ - UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000, 50) - -// For an enum with customized range. In general, sparse histograms should be -// used instead. -// Samples should be one of the std::vector list provided via -// |custom_ranges|. See comments above CustomRanges::FactoryGet about the -// requirement of |custom_ranges|. You can use the helper function -// CustomHistogram::ArrayToCustomEnumRanges to transform a C-style array of -// valid sample values to a std::vector. -#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \ - base::CustomHistogram::FactoryGet(name, custom_ranges, \ - base::HistogramBase::kUmaTargetedHistogramFlag)) - -#endif // BASE_METRICS_HISTOGRAM_MACROS_H_ diff --git a/metrics/histogram_macros_internal.h b/metrics/histogram_macros_internal.h deleted file mode 100644 index cc7c76aef..000000000 --- a/metrics/histogram_macros_internal.h +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_ -#define BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_ - -#include - -#include -#include - -#include "base/atomicops.h" -#include "base/logging.h" -#include "base/metrics/histogram.h" -#include "base/metrics/sparse_histogram.h" -#include "base/time/time.h" - -// This is for macros and helpers internal to base/metrics. They should not be -// used outside of this directory. For writing to UMA histograms, see -// histogram_macros.h. - -namespace base { -namespace internal { - -// Helper traits for deducing the boundary value for enums. -template -struct EnumSizeTraits { - static constexpr Enum Count() { - static_assert( - sizeof(Enum) == 0, - "enumerator must define kMaxValue enumerator to use this macro!"); - return Enum(); - } -}; - -// Since the UMA histogram macros expect a value one larger than the max defined -// enumerator value, add one. -template -struct EnumSizeTraits< - Enum, - std::enable_if_t::value>> { - static constexpr Enum Count() { - return static_cast( - static_cast>(Enum::kMaxValue) + 1); - } -}; - -} // namespace internal -} // namespace base - -// TODO(rkaplow): Improve commenting of these methods. -//------------------------------------------------------------------------------ -// Histograms are often put in areas where they are called many many times, and -// performance is critical. As a result, they are designed to have a very low -// recurring cost of executing (adding additional samples). Toward that end, -// the macros declare a static pointer to the histogram in question, and only -// take a "slow path" to construct (or find) the histogram on the first run -// through the macro. We leak the histograms at shutdown time so that we don't -// have to validate using the pointers at any time during the running of the -// process. - -// In some cases (integration into 3rd party code), it's useful to separate the -// definition of |atomic_histogram_pointer| from its use. To achieve this we -// define HISTOGRAM_POINTER_USE, which uses an |atomic_histogram_pointer|, and -// STATIC_HISTOGRAM_POINTER_BLOCK, which defines an |atomic_histogram_pointer| -// and forwards to HISTOGRAM_POINTER_USE. -#define HISTOGRAM_POINTER_USE(atomic_histogram_pointer, \ - constant_histogram_name, \ - histogram_add_method_invocation, \ - histogram_factory_get_invocation) \ - do { \ - /* \ - * Acquire_Load() ensures that we acquire visibility to the \ - * pointed-to data in the histogram. \ - */ \ - base::HistogramBase* histogram_pointer( \ - reinterpret_cast( \ - base::subtle::Acquire_Load(atomic_histogram_pointer))); \ - if (!histogram_pointer) { \ - /* \ - * This is the slow path, which will construct OR find the \ - * matching histogram. histogram_factory_get_invocation includes \ - * locks on a global histogram name map and is completely thread \ - * safe. \ - */ \ - histogram_pointer = histogram_factory_get_invocation; \ - \ - /* \ - * Use Release_Store to ensure that the histogram data is made \ - * available globally before we make the pointer visible. Several \ - * threads may perform this store, but the same value will be \ - * stored in all cases (for a given named/spec'ed histogram). \ - * We could do this without any barrier, since FactoryGet entered \ - * and exited a lock after construction, but this barrier makes \ - * things clear. \ - */ \ - base::subtle::Release_Store( \ - atomic_histogram_pointer, \ - reinterpret_cast(histogram_pointer)); \ - } \ - if (DCHECK_IS_ON()) \ - histogram_pointer->CheckName(constant_histogram_name); \ - histogram_pointer->histogram_add_method_invocation; \ - } while (0) - -// This is a helper macro used by other macros and shouldn't be used directly. -// Defines the static |atomic_histogram_pointer| and forwards to -// HISTOGRAM_POINTER_USE. -#define STATIC_HISTOGRAM_POINTER_BLOCK(constant_histogram_name, \ - histogram_add_method_invocation, \ - histogram_factory_get_invocation) \ - do { \ - /* \ - * The pointer's presence indicates that the initialization is complete. \ - * Initialization is idempotent, so it can safely be atomically repeated. \ - */ \ - static base::subtle::AtomicWord atomic_histogram_pointer = 0; \ - HISTOGRAM_POINTER_USE(&atomic_histogram_pointer, constant_histogram_name, \ - histogram_add_method_invocation, \ - histogram_factory_get_invocation); \ - } while (0) - -// This is a helper macro used by other macros and shouldn't be used directly. -#define INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG(name, sample, min, max, \ - bucket_count, flag) \ - STATIC_HISTOGRAM_POINTER_BLOCK( \ - name, Add(sample), \ - base::Histogram::FactoryGet(name, min, max, bucket_count, flag)) - -// This is a helper macro used by other macros and shouldn't be used directly. -// The bucketing scheme is linear with a bucket size of 1. For N items, -// recording values in the range [0, N - 1] creates a linear histogram with N + -// 1 buckets: -// [0, 1), [1, 2), ..., [N - 1, N) -// and an overflow bucket [N, infinity). -// -// Code should never emit to the overflow bucket; only to the other N buckets. -// This allows future versions of Chrome to safely increase the boundary size. -// Otherwise, the histogram would have [N - 1, infinity) as its overflow bucket, -// and so the maximal value (N - 1) would be emitted to this overflow bucket. -// But, if an additional value were later added, the bucket label for -// the value (N - 1) would change to [N - 1, N), which would result in different -// versions of Chrome using different bucket labels for identical data. -#define INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG(name, sample, boundary, \ - flag) \ - do { \ - static_assert(!std::is_enum::value, \ - "|sample| should not be an enum type!"); \ - static_assert(!std::is_enum::value, \ - "|boundary| should not be an enum type!"); \ - STATIC_HISTOGRAM_POINTER_BLOCK( \ - name, Add(sample), \ - base::LinearHistogram::FactoryGet(name, 1, boundary, boundary + 1, \ - flag)); \ - } while (0) - -// While this behaves the same as the above macro, the wrapping of a linear -// histogram with another object to do the scaling means the POINTER_BLOCK -// macro can't be used as it is tied to HistogramBase -#define INTERNAL_HISTOGRAM_SCALED_EXACT_LINEAR_WITH_FLAG( \ - name, sample, count, boundary, scale, flag) \ - do { \ - static_assert(!std::is_enum::value, \ - "|sample| should not be an enum type!"); \ - static_assert(!std::is_enum::value, \ - "|boundary| should not be an enum type!"); \ - class ScaledLinearHistogramInstance : public base::ScaledLinearHistogram { \ - public: \ - ScaledLinearHistogramInstance() \ - : ScaledLinearHistogram(name, \ - 1, \ - boundary, \ - boundary + 1, \ - scale, \ - flag) {} \ - }; \ - static base::LazyInstance::Leaky scaled; \ - scaled.Get().AddScaledCount(sample, count); \ - } while (0) - -// Helper for 'overloading' UMA_HISTOGRAM_ENUMERATION with a variable number of -// arguments. -#define INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO(_1, _2, NAME, ...) NAME - -#define INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY(name, sample, \ - flags) \ - INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG( \ - name, sample, base::internal::EnumSizeTraits::Count(), \ - flags) - -// Note: The value in |sample| must be strictly less than |enum_size|. -#define INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY(name, sample, \ - enum_size, flags) \ - INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, enum_size, flags) - -// Similar to the previous macro but intended for enumerations. This delegates -// the work to the previous macro, but supports scoped enumerations as well by -// forcing an explicit cast to the HistogramBase::Sample integral type. -// -// Note the range checks verify two separate issues: -// - that the declared enum size isn't out of range of HistogramBase::Sample -// - that the declared enum size is > 0 -// -// TODO(dcheng): This should assert that the passed in types are actually enum -// types. -#define INTERNAL_HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary, flag) \ - do { \ - using decayed_sample = std::decay::type; \ - using decayed_boundary = std::decay::type; \ - static_assert(!std::is_enum::value || \ - std::is_enum::value, \ - "Unexpected: |boundary| is enum, but |sample| is not."); \ - static_assert(!std::is_enum::value || \ - !std::is_enum::value || \ - std::is_same::value, \ - "|sample| and |boundary| shouldn't be of different enums"); \ - static_assert( \ - static_cast(boundary) < \ - static_cast( \ - std::numeric_limits::max()), \ - "|boundary| is out of range of HistogramBase::Sample"); \ - INTERNAL_HISTOGRAM_EXACT_LINEAR_WITH_FLAG( \ - name, static_cast(sample), \ - static_cast(boundary), flag); \ - } while (0) - -#define INTERNAL_HISTOGRAM_SCALED_ENUMERATION_WITH_FLAG(name, sample, count, \ - scale, flag) \ - do { \ - using decayed_sample = std::decay::type; \ - static_assert(std::is_enum::value, \ - "Unexpected: |sample| is not at enum."); \ - constexpr auto boundary = \ - base::internal::EnumSizeTraits::Count(); \ - static_assert( \ - static_cast(boundary) < \ - static_cast( \ - std::numeric_limits::max()), \ - "|boundary| is out of range of HistogramBase::Sample"); \ - INTERNAL_HISTOGRAM_SCALED_EXACT_LINEAR_WITH_FLAG( \ - name, static_cast(sample), count, \ - static_cast(boundary), scale, flag); \ - } while (0) - -// This is a helper macro used by other macros and shouldn't be used directly. -// This is necessary to expand __COUNTER__ to an actual value. -#define INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(name, is_long, key) \ - INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) - -// This is a helper macro used by other macros and shouldn't be used directly. -#define INTERNAL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(name, is_long, key) \ - class ScopedHistogramTimer##key { \ - public: \ - ScopedHistogramTimer##key() : constructed_(base::TimeTicks::Now()) {} \ - ~ScopedHistogramTimer##key() { \ - base::TimeDelta elapsed = base::TimeTicks::Now() - constructed_; \ - if (is_long) { \ - UMA_HISTOGRAM_LONG_TIMES_100(name, elapsed); \ - } else { \ - UMA_HISTOGRAM_TIMES(name, elapsed); \ - } \ - } \ - private: \ - base::TimeTicks constructed_; \ - } scoped_histogram_timer_##key - -#endif // BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_ diff --git a/metrics/histogram_macros_local.h b/metrics/histogram_macros_local.h deleted file mode 100644 index c4d333b50..000000000 --- a/metrics/histogram_macros_local.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_ -#define BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_ - -#include "base/logging.h" -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_macros_internal.h" -#include "base/time/time.h" - -// TODO(rkaplow): Migrate all LOCAL_* usage within Chromium to include this -// file instead of the histogram_macros.h file. - -//------------------------------------------------------------------------------ -// Enumeration histograms. -// -// For usage details, see the equivalents in histogram_macros.h. - -#define LOCAL_HISTOGRAM_ENUMERATION(name, ...) \ - CR_EXPAND_ARG(INTERNAL_UMA_HISTOGRAM_ENUMERATION_GET_MACRO( \ - __VA_ARGS__, INTERNAL_UMA_HISTOGRAM_ENUMERATION_SPECIFY_BOUNDARY, \ - INTERNAL_UMA_HISTOGRAM_ENUMERATION_DEDUCE_BOUNDARY)( \ - name, __VA_ARGS__, base::HistogramBase::kNoFlags)) - -#define LOCAL_HISTOGRAM_BOOLEAN(name, sample) \ - STATIC_HISTOGRAM_POINTER_BLOCK(name, AddBoolean(sample), \ - base::BooleanHistogram::FactoryGet(name, base::Histogram::kNoFlags)) - -//------------------------------------------------------------------------------ -// Percentage histograms. -// -// For usage details, see the equivalents in histogram_macros.h - -#define LOCAL_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ - LOCAL_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) - -//------------------------------------------------------------------------------ -// Count histograms. These are used for collecting numeric data. Note that we -// have macros for more specialized use cases below (memory, time, percentages). -// For usage details, see the equivalents in histogram_macros.h. - -#define LOCAL_HISTOGRAM_COUNTS_100(name, sample) \ - LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 100, 50) - -#define LOCAL_HISTOGRAM_COUNTS_10000(name, sample) \ - LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 10000, 50) - -#define LOCAL_HISTOGRAM_COUNTS_1000000(name, sample) \ - LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000000, 50) - -#define LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ - INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \ - name, sample, min, max, bucket_count, base::HistogramBase::kNoFlags) - -//------------------------------------------------------------------------------ -// Timing histograms. These are used for collecting timing data (generally -// latencies). -// -// For usage details, see the equivalents in histogram_macros.h. - -#define LOCAL_HISTOGRAM_TIMES(name, sample) LOCAL_HISTOGRAM_CUSTOM_TIMES( \ - name, sample, base::TimeDelta::FromMilliseconds(1), \ - base::TimeDelta::FromSeconds(10), 50) - -#define LOCAL_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ - STATIC_HISTOGRAM_POINTER_BLOCK( \ - name, AddTimeMillisecondsGranularity(sample), \ - base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ - base::HistogramBase::kNoFlags)) - -//------------------------------------------------------------------------------ -// Memory histograms. -// -// For usage details, see the equivalents in histogram_macros.h. - -#define LOCAL_HISTOGRAM_MEMORY_KB(name, sample) LOCAL_HISTOGRAM_CUSTOM_COUNTS( \ - name, sample, 1000, 500000, 50) - -//------------------------------------------------------------------------------ -// Deprecated histograms. Not recommended for current use. - -// TODO(rkaplow): See if we can clean up this macro and usage. -// Legacy non-explicit version. We suggest using LOCAL_HISTOGRAM_COUNTS_1000000 -// instead. -#define LOCAL_HISTOGRAM_COUNTS(name, sample) \ - LOCAL_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 1000000, 50) - -#endif // BASE_METRICS_HISTOGRAM_MACROS_LOCAL_H_ diff --git a/metrics/histogram_macros_unittest.cc b/metrics/histogram_macros_unittest.cc deleted file mode 100644 index 3c592b00e..000000000 --- a/metrics/histogram_macros_unittest.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/metrics/histogram_macros.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(ScopedHistogramTimer, TwoTimersOneScope) { - SCOPED_UMA_HISTOGRAM_TIMER("TestTimer0"); - SCOPED_UMA_HISTOGRAM_TIMER("TestTimer1"); - SCOPED_UMA_HISTOGRAM_LONG_TIMER("TestLongTimer0"); - SCOPED_UMA_HISTOGRAM_LONG_TIMER("TestLongTimer1"); -} - -// Compile tests for UMA_HISTOGRAM_ENUMERATION with the three different types it -// accepts: -// - integral types -// - unscoped enums -// - scoped enums -TEST(HistogramMacro, IntegralPsuedoEnumeration) { - UMA_HISTOGRAM_ENUMERATION("Test.FauxEnumeration", 1, 10000); -} - -TEST(HistogramMacro, UnscopedEnumeration) { - enum TestEnum : char { - FIRST_VALUE, - SECOND_VALUE, - THIRD_VALUE, - MAX_ENTRIES, - }; - UMA_HISTOGRAM_ENUMERATION("Test.UnscopedEnumeration", SECOND_VALUE, - MAX_ENTRIES); -} - -TEST(HistogramMacro, ScopedEnumeration) { - enum class TestEnum { - FIRST_VALUE, - SECOND_VALUE, - THIRD_VALUE, - kMaxValue = THIRD_VALUE, - }; - UMA_HISTOGRAM_ENUMERATION("Test.ScopedEnumeration", TestEnum::FIRST_VALUE); - - enum class TestEnum2 { - FIRST_VALUE, - SECOND_VALUE, - THIRD_VALUE, - MAX_ENTRIES, - }; - UMA_HISTOGRAM_ENUMERATION("Test.ScopedEnumeration2", TestEnum2::SECOND_VALUE, - TestEnum2::MAX_ENTRIES); -} - -} // namespace base diff --git a/metrics/histogram_samples.cc b/metrics/histogram_samples.cc deleted file mode 100644 index 6830637c0..000000000 --- a/metrics/histogram_samples.cc +++ /dev/null @@ -1,315 +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/metrics/histogram_samples.h" - -#include - -#include "base/compiler_specific.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" -#include "base/numerics/safe_conversions.h" -#include "base/numerics/safe_math.h" -#include "base/pickle.h" - -namespace base { - -namespace { - -// A shorthand constant for the max value of size_t. -constexpr size_t kSizeMax = std::numeric_limits::max(); - -// A constant stored in an AtomicSingleSample (as_atomic) to indicate that the -// sample is "disabled" and no further accumulation should be done with it. The -// value is chosen such that it will be MAX_UINT16 for both |bucket| & |count|, -// and thus less likely to conflict with real use. Conflicts are explicitly -// handled in the code but it's worth making them as unlikely as possible. -constexpr int32_t kDisabledSingleSample = -1; - -class SampleCountPickleIterator : public SampleCountIterator { - public: - explicit SampleCountPickleIterator(PickleIterator* iter); - - bool Done() const override; - void Next() override; - void Get(HistogramBase::Sample* min, - int64_t* max, - HistogramBase::Count* count) const override; - - private: - PickleIterator* const iter_; - - HistogramBase::Sample min_; - int64_t max_; - HistogramBase::Count count_; - bool is_done_; -}; - -SampleCountPickleIterator::SampleCountPickleIterator(PickleIterator* iter) - : iter_(iter), - is_done_(false) { - Next(); -} - -bool SampleCountPickleIterator::Done() const { - return is_done_; -} - -void SampleCountPickleIterator::Next() { - DCHECK(!Done()); - if (!iter_->ReadInt(&min_) || !iter_->ReadInt64(&max_) || - !iter_->ReadInt(&count_)) { - is_done_ = true; - } -} - -void SampleCountPickleIterator::Get(HistogramBase::Sample* min, - int64_t* max, - HistogramBase::Count* count) const { - DCHECK(!Done()); - *min = min_; - *max = max_; - *count = count_; -} - -} // namespace - -static_assert(sizeof(HistogramSamples::AtomicSingleSample) == - sizeof(subtle::Atomic32), - "AtomicSingleSample isn't 32 bits"); - -HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Load() - const { - AtomicSingleSample single_sample = subtle::Acquire_Load(&as_atomic); - - // If the sample was extracted/disabled, it's still zero to the outside. - if (single_sample.as_atomic == kDisabledSingleSample) - single_sample.as_atomic = 0; - - return single_sample.as_parts; -} - -HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Extract( - bool disable) { - AtomicSingleSample single_sample = subtle::NoBarrier_AtomicExchange( - &as_atomic, disable ? kDisabledSingleSample : 0); - if (single_sample.as_atomic == kDisabledSingleSample) - single_sample.as_atomic = 0; - return single_sample.as_parts; -} - -bool HistogramSamples::AtomicSingleSample::Accumulate( - size_t bucket, - HistogramBase::Count count) { - if (count == 0) - return true; - - // Convert the parameters to 16-bit variables because it's all 16-bit below. - // To support decrements/subtractions, divide the |count| into sign/value and - // do the proper operation below. The alternative is to change the single- - // sample's count to be a signed integer (int16_t) and just add an int16_t - // |count16| but that is somewhat wasteful given that the single-sample is - // never expected to have a count less than zero. - if (count < -std::numeric_limits::max() || - count > std::numeric_limits::max() || - bucket > std::numeric_limits::max()) { - return false; - } - bool count_is_negative = count < 0; - uint16_t count16 = static_cast(count_is_negative ? -count : count); - uint16_t bucket16 = static_cast(bucket); - - // A local, unshared copy of the single-sample is necessary so the parts - // can be manipulated without worrying about atomicity. - AtomicSingleSample single_sample; - - bool sample_updated; - do { - subtle::Atomic32 original = subtle::Acquire_Load(&as_atomic); - if (original == kDisabledSingleSample) - return false; - single_sample.as_atomic = original; - if (single_sample.as_atomic != 0) { - // Only the same bucket (parameter and stored) can be counted multiple - // times. - if (single_sample.as_parts.bucket != bucket16) - return false; - } else { - // The |single_ sample| was zero so becomes the |bucket| parameter, the - // contents of which were checked above to fit in 16 bits. - single_sample.as_parts.bucket = bucket16; - } - - // Update count, making sure that it doesn't overflow. - CheckedNumeric new_count(single_sample.as_parts.count); - if (count_is_negative) - new_count -= count16; - else - new_count += count16; - if (!new_count.AssignIfValid(&single_sample.as_parts.count)) - return false; - - // Don't let this become equivalent to the "disabled" value. - if (single_sample.as_atomic == kDisabledSingleSample) - return false; - - // Store the updated single-sample back into memory. |existing| is what - // was in that memory location at the time of the call; if it doesn't - // match |original| then the swap didn't happen so loop again. - subtle::Atomic32 existing = subtle::Release_CompareAndSwap( - &as_atomic, original, single_sample.as_atomic); - sample_updated = (existing == original); - } while (!sample_updated); - - return true; -} - -bool HistogramSamples::AtomicSingleSample::IsDisabled() const { - return subtle::Acquire_Load(&as_atomic) == kDisabledSingleSample; -} - -HistogramSamples::LocalMetadata::LocalMetadata() { - // This is the same way it's done for persistent metadata since no ctor - // is called for the data members in that case. - memset(this, 0, sizeof(*this)); -} - -HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta) - : meta_(meta) { - DCHECK(meta_->id == 0 || meta_->id == id); - - // It's possible that |meta| is contained in initialized, read-only memory - // so it's essential that no write be done in that case. - if (!meta_->id) - meta_->id = id; -} - -// This mustn't do anything with |meta_|. It was passed to the ctor and may -// be invalid by the time this dtor gets called. -HistogramSamples::~HistogramSamples() = default; - -void HistogramSamples::Add(const HistogramSamples& other) { - IncreaseSumAndCount(other.sum(), other.redundant_count()); - std::unique_ptr it = other.Iterator(); - bool success = AddSubtractImpl(it.get(), ADD); - DCHECK(success); -} - -bool HistogramSamples::AddFromPickle(PickleIterator* iter) { - int64_t sum; - HistogramBase::Count redundant_count; - - if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count)) - return false; - - IncreaseSumAndCount(sum, redundant_count); - - SampleCountPickleIterator pickle_iter(iter); - return AddSubtractImpl(&pickle_iter, ADD); -} - -void HistogramSamples::Subtract(const HistogramSamples& other) { - IncreaseSumAndCount(-other.sum(), -other.redundant_count()); - std::unique_ptr it = other.Iterator(); - bool success = AddSubtractImpl(it.get(), SUBTRACT); - DCHECK(success); -} - -void HistogramSamples::Serialize(Pickle* pickle) const { - pickle->WriteInt64(sum()); - pickle->WriteInt(redundant_count()); - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - for (std::unique_ptr it = Iterator(); !it->Done(); - it->Next()) { - it->Get(&min, &max, &count); - pickle->WriteInt(min); - pickle->WriteInt64(max); - pickle->WriteInt(count); - } -} - -bool HistogramSamples::AccumulateSingleSample(HistogramBase::Sample value, - HistogramBase::Count count, - size_t bucket) { - if (single_sample().Accumulate(bucket, count)) { - // Success. Update the (separate) sum and redundant-count. - IncreaseSumAndCount(strict_cast(value) * count, count); - return true; - } - return false; -} - -void HistogramSamples::IncreaseSumAndCount(int64_t sum, - HistogramBase::Count count) { -#ifdef ARCH_CPU_64_BITS - subtle::NoBarrier_AtomicIncrement(&meta_->sum, sum); -#else - meta_->sum += sum; -#endif - subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, count); -} - -void HistogramSamples::RecordNegativeSample(NegativeSampleReason reason, - HistogramBase::Count increment) { - UMA_HISTOGRAM_ENUMERATION("UMA.NegativeSamples.Reason", reason, - MAX_NEGATIVE_SAMPLE_REASONS); - UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.NegativeSamples.Increment", increment, 1, - 1 << 30, 100); - UmaHistogramSparse("UMA.NegativeSamples.Histogram", - static_cast(id())); -} - -SampleCountIterator::~SampleCountIterator() = default; - -bool SampleCountIterator::GetBucketIndex(size_t* index) const { - DCHECK(!Done()); - return false; -} - -SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min, - int64_t max, - HistogramBase::Count count) - : SingleSampleIterator(min, max, count, kSizeMax) {} - -SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min, - int64_t max, - HistogramBase::Count count, - size_t bucket_index) - : min_(min), max_(max), bucket_index_(bucket_index), count_(count) {} - -SingleSampleIterator::~SingleSampleIterator() = default; - -bool SingleSampleIterator::Done() const { - return count_ == 0; -} - -void SingleSampleIterator::Next() { - DCHECK(!Done()); - count_ = 0; -} - -void SingleSampleIterator::Get(HistogramBase::Sample* min, - int64_t* max, - HistogramBase::Count* count) const { - DCHECK(!Done()); - if (min != nullptr) - *min = min_; - if (max != nullptr) - *max = max_; - if (count != nullptr) - *count = count_; -} - -bool SingleSampleIterator::GetBucketIndex(size_t* index) const { - DCHECK(!Done()); - if (bucket_index_ == kSizeMax) - return false; - *index = bucket_index_; - return true; -} - -} // namespace base diff --git a/metrics/histogram_samples.h b/metrics/histogram_samples.h deleted file mode 100644 index 6908873ce..000000000 --- a/metrics/histogram_samples.h +++ /dev/null @@ -1,267 +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_METRICS_HISTOGRAM_SAMPLES_H_ -#define BASE_METRICS_HISTOGRAM_SAMPLES_H_ - -#include -#include - -#include -#include - -#include "base/atomicops.h" -#include "base/macros.h" -#include "base/metrics/histogram_base.h" - -namespace base { - -class Pickle; -class PickleIterator; -class SampleCountIterator; - -// HistogramSamples is a container storing all samples of a histogram. All -// elements must be of a fixed width to ensure 32/64-bit interoperability. -// If this structure changes, bump the version number for kTypeIdHistogram -// in persistent_histogram_allocator.cc. -// -// Note that though these samples are individually consistent (through the use -// of atomic operations on the counts), there is only "eventual consistency" -// overall when multiple threads are accessing this data. That means that the -// sum, redundant-count, etc. could be momentarily out-of-sync with the stored -// counts but will settle to a consistent "steady state" once all threads have -// exited this code. -class BASE_EXPORT HistogramSamples { - public: - // A single bucket and count. To fit within a single atomic on 32-bit build - // architectures, both |bucket| and |count| are limited in size to 16 bits. - // This limits the functionality somewhat but if an entry can't fit then - // the full array of samples can be allocated and used. - struct SingleSample { - uint16_t bucket; - uint16_t count; - }; - - // A structure for managing an atomic single sample. Because this is generally - // used in association with other atomic values, the defined methods use - // acquire/release operations to guarantee ordering with outside values. - union BASE_EXPORT AtomicSingleSample { - AtomicSingleSample() : as_atomic(0) {} - AtomicSingleSample(subtle::Atomic32 rhs) : as_atomic(rhs) {} - - // Returns the single sample in an atomic manner. This in an "acquire" - // load. The returned sample isn't shared and thus its fields can be safely - // accessed. - SingleSample Load() const; - - // Extracts the single sample in an atomic manner. If |disable| is true - // then this object will be set so it will never accumulate another value. - // This is "no barrier" so doesn't enforce ordering with other atomic ops. - SingleSample Extract(bool disable); - - // Adds a given count to the held bucket. If not possible, it returns false - // and leaves the parts unchanged. Once extracted/disabled, this always - // returns false. This in an "acquire/release" operation. - bool Accumulate(size_t bucket, HistogramBase::Count count); - - // Returns if the sample has been "disabled" (via Extract) and thus not - // allowed to accept further accumulation. - bool IsDisabled() const; - - private: - // union field: The actual sample bucket and count. - SingleSample as_parts; - - // union field: The sample as an atomic value. Atomic64 would provide - // more flexibility but isn't available on all builds. This can hold a - // special, internal "disabled" value indicating that it must not accept - // further accumulation. - subtle::Atomic32 as_atomic; - }; - - // A structure of information about the data, common to all sample containers. - // Because of how this is used in persistent memory, it must be a POD object - // that makes sense when initialized to all zeros. - struct Metadata { - // Expected size for 32/64-bit check. - static constexpr size_t kExpectedInstanceSize = 24; - - // Initialized when the sample-set is first created with a value provided - // by the caller. It is generally used to identify the sample-set across - // threads and processes, though not necessarily uniquely as it is possible - // to have multiple sample-sets representing subsets of the data. - uint64_t id; - - // The sum of all the entries, effectivly the sum(sample * count) for - // all samples. Despite being atomic, no guarantees are made on the - // accuracy of this value; there may be races during histogram - // accumulation and snapshotting that we choose to accept. It should - // be treated as approximate. -#ifdef ARCH_CPU_64_BITS - subtle::Atomic64 sum; -#else - // 32-bit systems don't have atomic 64-bit operations. Use a basic type - // and don't worry about "shearing". - int64_t sum; -#endif - - // A "redundant" count helps identify memory corruption. It redundantly - // stores the total number of samples accumulated in the histogram. We - // can compare this count to the sum of the counts (TotalCount() function), - // and detect problems. Note, depending on the implementation of different - // histogram 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::AtomicCount redundant_count; - - // A single histogram value and associated count. This allows histograms - // that typically report only a single value to not require full storage - // to be allocated. - AtomicSingleSample single_sample; // 32 bits - }; - - // Because structures held in persistent memory must be POD, there can be no - // default constructor to clear the fields. This derived class exists just - // to clear them when being allocated on the heap. - struct BASE_EXPORT LocalMetadata : Metadata { - LocalMetadata(); - }; - - HistogramSamples(uint64_t id, Metadata* meta); - virtual ~HistogramSamples(); - - virtual void Accumulate(HistogramBase::Sample value, - HistogramBase::Count count) = 0; - virtual HistogramBase::Count GetCount(HistogramBase::Sample value) const = 0; - virtual HistogramBase::Count TotalCount() const = 0; - - virtual void Add(const HistogramSamples& other); - - // Add from serialized samples. - virtual bool AddFromPickle(PickleIterator* iter); - - virtual void Subtract(const HistogramSamples& other); - - virtual std::unique_ptr Iterator() const = 0; - virtual void Serialize(Pickle* pickle) const; - - // Accessor fuctions. - uint64_t id() const { return meta_->id; } - int64_t sum() const { -#ifdef ARCH_CPU_64_BITS - return subtle::NoBarrier_Load(&meta_->sum); -#else - return meta_->sum; -#endif - } - HistogramBase::Count redundant_count() const { - return subtle::NoBarrier_Load(&meta_->redundant_count); - } - - protected: - enum NegativeSampleReason { - SAMPLES_HAVE_LOGGED_BUT_NOT_SAMPLE, - SAMPLES_SAMPLE_LESS_THAN_LOGGED, - SAMPLES_ADDED_NEGATIVE_COUNT, - SAMPLES_ADD_WENT_NEGATIVE, - SAMPLES_ADD_OVERFLOW, - SAMPLES_ACCUMULATE_NEGATIVE_COUNT, - SAMPLES_ACCUMULATE_WENT_NEGATIVE, - DEPRECATED_SAMPLES_ACCUMULATE_OVERFLOW, - SAMPLES_ACCUMULATE_OVERFLOW, - MAX_NEGATIVE_SAMPLE_REASONS - }; - - // Based on |op| type, add or subtract sample counts data from the iterator. - enum Operator { ADD, SUBTRACT }; - virtual bool AddSubtractImpl(SampleCountIterator* iter, Operator op) = 0; - - // Accumulates to the embedded single-sample field if possible. Returns true - // on success, false otherwise. Sum and redundant-count are also updated in - // the success case. - bool AccumulateSingleSample(HistogramBase::Sample value, - HistogramBase::Count count, - size_t bucket); - - // Atomically adjust the sum and redundant-count. - void IncreaseSumAndCount(int64_t sum, HistogramBase::Count count); - - // Record a negative-sample observation and the reason why. - void RecordNegativeSample(NegativeSampleReason reason, - HistogramBase::Count increment); - - AtomicSingleSample& single_sample() { return meta_->single_sample; } - const AtomicSingleSample& single_sample() const { - return meta_->single_sample; - } - - Metadata* meta() { return meta_; } - - private: - // Depending on derived class meta values can come from local stoarge or - // external storage in which case HistogramSamples class cannot take ownership - // of Metadata*. - Metadata* meta_; - - DISALLOW_COPY_AND_ASSIGN(HistogramSamples); -}; - -class BASE_EXPORT SampleCountIterator { - public: - virtual ~SampleCountIterator(); - - virtual bool Done() const = 0; - virtual void Next() = 0; - - // Get the sample and count at current position. - // |min| |max| and |count| can be NULL if the value is not of interest. - // Note: |max| is int64_t because histograms support logged values in the - // full int32_t range and bucket max is exclusive, so it needs to support - // values up to MAXINT32+1. - // Requires: !Done(); - virtual void Get(HistogramBase::Sample* min, - int64_t* max, - HistogramBase::Count* count) const = 0; - static_assert(std::numeric_limits::max() < - std::numeric_limits::max(), - "Get() |max| must be able to hold Histogram::Sample max + 1"); - - // Get the index of current histogram bucket. - // For histograms that don't use predefined buckets, it returns false. - // Requires: !Done(); - virtual bool GetBucketIndex(size_t* index) const; -}; - -class BASE_EXPORT SingleSampleIterator : public SampleCountIterator { - public: - SingleSampleIterator(HistogramBase::Sample min, - int64_t max, - HistogramBase::Count count); - SingleSampleIterator(HistogramBase::Sample min, - int64_t max, - HistogramBase::Count count, - size_t bucket_index); - ~SingleSampleIterator() override; - - // SampleCountIterator: - bool Done() const override; - void Next() override; - void Get(HistogramBase::Sample* min, - int64_t* max, - HistogramBase::Count* count) const override; - - // SampleVector uses predefined buckets so iterator can return bucket index. - bool GetBucketIndex(size_t* index) const override; - - private: - // Information about the single value to return. - const HistogramBase::Sample min_; - const int64_t max_; - const size_t bucket_index_; - HistogramBase::Count count_; -}; - -} // namespace base - -#endif // BASE_METRICS_HISTOGRAM_SAMPLES_H_ diff --git a/metrics/histogram_samples_unittest.cc b/metrics/histogram_samples_unittest.cc deleted file mode 100644 index 74c743b60..000000000 --- a/metrics/histogram_samples_unittest.cc +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2017 The Chromium 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_samples.h" - -#include - -#include "base/test/gtest_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -using SingleSample = HistogramSamples::SingleSample; -using AtomicSingleSample = HistogramSamples::AtomicSingleSample; - -TEST(SingleSampleTest, Load) { - AtomicSingleSample sample; - ASSERT_TRUE(sample.Accumulate(9, 1)); - - SingleSample s = sample.Load(); - EXPECT_EQ(9U, s.bucket); - EXPECT_EQ(1U, s.count); - - s = sample.Load(); - EXPECT_EQ(9U, s.bucket); - EXPECT_EQ(1U, s.count); -} - -TEST(SingleSampleTest, Extract) { - AtomicSingleSample sample; - ASSERT_TRUE(sample.Accumulate(9, 1)); - - SingleSample s = sample.Extract(/*disable=*/false); - EXPECT_EQ(9U, s.bucket); - EXPECT_EQ(1U, s.count); - - s = sample.Extract(/*disable=*/false); - EXPECT_EQ(0U, s.bucket); - EXPECT_EQ(0U, s.count); -} - -TEST(SingleSampleTest, Disable) { - AtomicSingleSample sample; - EXPECT_EQ(0U, sample.Extract(/*disable=*/false).count); - EXPECT_FALSE(sample.IsDisabled()); - - ASSERT_TRUE(sample.Accumulate(9, 1)); - EXPECT_EQ(1U, sample.Extract(/*disable=*/true).count); - EXPECT_TRUE(sample.IsDisabled()); - - ASSERT_FALSE(sample.Accumulate(9, 1)); - EXPECT_EQ(0U, sample.Extract(/*disable=*/false).count); - EXPECT_FALSE(sample.IsDisabled()); -} - -TEST(SingleSampleTest, Accumulate) { - AtomicSingleSample sample; - - ASSERT_TRUE(sample.Accumulate(9, 1)); - ASSERT_TRUE(sample.Accumulate(9, 2)); - ASSERT_TRUE(sample.Accumulate(9, 4)); - EXPECT_EQ(7U, sample.Extract(/*disable=*/false).count); - - ASSERT_TRUE(sample.Accumulate(9, 4)); - ASSERT_TRUE(sample.Accumulate(9, -2)); - ASSERT_TRUE(sample.Accumulate(9, 1)); - EXPECT_EQ(3U, sample.Extract(/*disable=*/false).count); -} - -TEST(SingleSampleTest, Overflow) { - AtomicSingleSample sample; - - ASSERT_TRUE(sample.Accumulate(9, 1)); - ASSERT_FALSE(sample.Accumulate(9, -2)); - EXPECT_EQ(1U, sample.Extract(/*disable=*/false).count); - - ASSERT_TRUE(sample.Accumulate(9, std::numeric_limits::max())); - ASSERT_FALSE(sample.Accumulate(9, 1)); - EXPECT_EQ(std::numeric_limits::max(), - sample.Extract(/*disable=*/false).count); -} - -} // namespace base diff --git a/metrics/histogram_snapshot_manager.cc b/metrics/histogram_snapshot_manager.cc deleted file mode 100644 index c1b804ebd..000000000 --- a/metrics/histogram_snapshot_manager.cc +++ /dev/null @@ -1,123 +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/metrics/histogram_snapshot_manager.h" - -#include - -#include "base/debug/alias.h" -#include "base/metrics/histogram_flattener.h" -#include "base/metrics/histogram_samples.h" -#include "base/metrics/statistics_recorder.h" -#include "base/stl_util.h" - -namespace base { - -namespace { - -// A simple object to set an "active" flag and clear it upon destruction. It is -// an error if the flag is already set. -class MakeActive { - public: - MakeActive(std::atomic* is_active) : is_active_(is_active) { - bool was_active = is_active_->exchange(true, std::memory_order_relaxed); - CHECK(!was_active); - } - ~MakeActive() { is_active_->store(false, std::memory_order_relaxed); } - - private: - std::atomic* is_active_; - - DISALLOW_COPY_AND_ASSIGN(MakeActive); -}; - -} // namespace - -HistogramSnapshotManager::HistogramSnapshotManager( - HistogramFlattener* histogram_flattener) - : histogram_flattener_(histogram_flattener) { - DCHECK(histogram_flattener_); - is_active_.store(false, std::memory_order_relaxed); -} - -HistogramSnapshotManager::~HistogramSnapshotManager() = default; - -void HistogramSnapshotManager::PrepareDeltas( - const std::vector& histograms, - HistogramBase::Flags flags_to_set, - HistogramBase::Flags required_flags) { - for (HistogramBase* const histogram : histograms) { - histogram->SetFlags(flags_to_set); - if ((histogram->flags() & required_flags) == required_flags) - PrepareDelta(histogram); - } -} - -void HistogramSnapshotManager::PrepareDelta(HistogramBase* histogram) { - histogram->ValidateHistogramContents(); - PrepareSamples(histogram, histogram->SnapshotDelta()); -} - -void HistogramSnapshotManager::PrepareFinalDelta( - const HistogramBase* histogram) { - histogram->ValidateHistogramContents(); - PrepareSamples(histogram, histogram->SnapshotFinalDelta()); -} - -void HistogramSnapshotManager::PrepareSamples( - const HistogramBase* histogram, - std::unique_ptr samples) { - DCHECK(histogram_flattener_); - - // Ensure that there is no concurrent access going on while accessing the - // set of known histograms. The flag will be reset when this object goes - // out of scope. - MakeActive make_active(&is_active_); - - // Get information known about this histogram. If it did not previously - // exist, one will be created and initialized. - SampleInfo* sample_info = &known_histograms_[histogram->name_hash()]; - - // Crash if we detect that our histograms have been overwritten. This may be - // a fair distance from the memory smasher, but we hope to correlate these - // crashes with other events, such as plugins, or usage patterns, etc. - uint32_t corruption = histogram->FindCorruption(*samples); - if (HistogramBase::BUCKET_ORDER_ERROR & corruption) { - // Extract fields useful during debug. - const BucketRanges* ranges = - static_cast(histogram)->bucket_ranges(); - uint32_t ranges_checksum = ranges->checksum(); - uint32_t ranges_calc_checksum = ranges->CalculateChecksum(); - int32_t flags = histogram->flags(); - // The checksum should have caught this, so crash separately if it didn't. - CHECK_NE(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption); - CHECK(false); // Crash for the bucket order corruption. - // Ensure that compiler keeps around pointers to |histogram| and its - // internal |bucket_ranges_| for any minidumps. - base::debug::Alias(&ranges_checksum); - base::debug::Alias(&ranges_calc_checksum); - base::debug::Alias(&flags); - } - // Checksum corruption might not have caused order corruption. - CHECK_EQ(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption); - - // Note, at this point corruption can only be COUNT_HIGH_ERROR or - // COUNT_LOW_ERROR and they never arise together, so we don't need to extract - // bits from corruption. - if (corruption) { - DLOG(ERROR) << "Histogram: \"" << histogram->histogram_name() - << "\" has data corruption: " << corruption; - // Don't record corrupt data to metrics services. - const uint32_t old_corruption = sample_info->inconsistencies; - if (old_corruption == (corruption | old_corruption)) - return; // We've already seen this corruption for this histogram. - sample_info->inconsistencies |= corruption; - return; - } - - if (samples->TotalCount() > 0) - histogram_flattener_->RecordDelta(*histogram, *samples); -} - -} // namespace base diff --git a/metrics/histogram_snapshot_manager.h b/metrics/histogram_snapshot_manager.h deleted file mode 100644 index cf7c149cc..000000000 --- a/metrics/histogram_snapshot_manager.h +++ /dev/null @@ -1,90 +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_METRICS_HISTOGRAM_SNAPSHOT_MANAGER_H_ -#define BASE_METRICS_HISTOGRAM_SNAPSHOT_MANAGER_H_ - -#include - -#include -#include -#include -#include - -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/metrics/histogram_base.h" - -namespace base { - -class HistogramSamples; -class HistogramFlattener; - -// HistogramSnapshotManager handles the logistics of gathering up available -// histograms for recording either to disk or for transmission (such as from -// renderer to browser, or from browser to UMA upload). Since histograms can sit -// in memory for an extended period of time, and are vulnerable to memory -// corruption, this class also validates as much redundancy as it can before -// calling for the marginal change (a.k.a., delta) in a histogram to be -// recorded. -class BASE_EXPORT HistogramSnapshotManager final { - public: - explicit HistogramSnapshotManager(HistogramFlattener* histogram_flattener); - ~HistogramSnapshotManager(); - - // Snapshot all histograms, and ask |histogram_flattener_| to record the - // 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(const std::vector& histograms, - HistogramBase::Flags flags_to_set, - HistogramBase::Flags required_flags); - - // When the collection is not so simple as can be done using a single - // iterator, the steps can be performed separately. Call PerpareDelta() - // as many times as necessary. PrepareFinalDelta() works like PrepareDelta() - // except that it does not update the previous logged values and can thus - // be used with read-only files. - void PrepareDelta(HistogramBase* histogram); - void PrepareFinalDelta(const HistogramBase* histogram); - - private: - FRIEND_TEST_ALL_PREFIXES(HistogramSnapshotManagerTest, CheckMerge); - - // During a snapshot, samples are acquired and aggregated. This structure - // contains all the information for a given histogram that persists between - // collections. - struct SampleInfo { - // The set of inconsistencies (flags) already seen for the histogram. - // See HistogramBase::Inconsistency for values. - uint32_t inconsistencies = 0; - }; - - // Capture and hold samples from a histogram. This does all the heavy - // lifting for PrepareDelta() and PrepareAbsolute(). - void PrepareSamples(const HistogramBase* histogram, - std::unique_ptr samples); - - // |histogram_flattener_| handles the logistics of recording the histogram - // deltas. - HistogramFlattener* const histogram_flattener_; // Weak. - - // For histograms, track what has been previously seen, indexed - // by the hash of the histogram name. - std::map known_histograms_; - - // A flag indicating if a thread is currently doing an operation. This is - // used to check against concurrent access which is not supported. A Thread- - // Checker is not sufficient because it may be guarded by at outside lock - // (as is the case with cronet). - std::atomic is_active_; - - DISALLOW_COPY_AND_ASSIGN(HistogramSnapshotManager); -}; - -} // namespace base - -#endif // BASE_METRICS_HISTOGRAM_SNAPSHOT_MANAGER_H_ diff --git a/metrics/histogram_snapshot_manager_unittest.cc b/metrics/histogram_snapshot_manager_unittest.cc deleted file mode 100644 index 1e2c599ec..000000000 --- a/metrics/histogram_snapshot_manager_unittest.cc +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/metrics/histogram_snapshot_manager.h" - -#include -#include - -#include "base/macros.h" -#include "base/metrics/histogram_delta_serialization.h" -#include "base/metrics/histogram_macros.h" -#include "base/metrics/sample_vector.h" -#include "base/metrics/statistics_recorder.h" -#include "base/stl_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -class HistogramFlattenerDeltaRecorder : public HistogramFlattener { - public: - HistogramFlattenerDeltaRecorder() = default; - - void RecordDelta(const HistogramBase& histogram, - const HistogramSamples& snapshot) override { - recorded_delta_histogram_names_.push_back(histogram.histogram_name()); - // Use CHECK instead of ASSERT to get full stack-trace and thus origin. - CHECK(!ContainsKey(recorded_delta_histogram_sum_, - histogram.histogram_name())); - // Keep pointer to snapshot for testing. This really isn't ideal but the - // snapshot-manager keeps the snapshot alive until it's "forgotten". - recorded_delta_histogram_sum_[histogram.histogram_name()] = snapshot.sum(); - } - - void Reset() { - recorded_delta_histogram_names_.clear(); - recorded_delta_histogram_sum_.clear(); - } - - std::vector GetRecordedDeltaHistogramNames() { - return recorded_delta_histogram_names_; - } - - int64_t GetRecordedDeltaHistogramSum(const std::string& name) { - EXPECT_TRUE(ContainsKey(recorded_delta_histogram_sum_, name)); - return recorded_delta_histogram_sum_[name]; - } - - private: - std::vector recorded_delta_histogram_names_; - std::map recorded_delta_histogram_sum_; - - DISALLOW_COPY_AND_ASSIGN(HistogramFlattenerDeltaRecorder); -}; - -class HistogramSnapshotManagerTest : public testing::Test { - protected: - HistogramSnapshotManagerTest() - : statistics_recorder_(StatisticsRecorder::CreateTemporaryForTesting()), - histogram_snapshot_manager_(&histogram_flattener_delta_recorder_) {} - - ~HistogramSnapshotManagerTest() override = default; - - std::unique_ptr 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, 4); - UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2); - - StatisticsRecorder::PrepareDeltas(false, HistogramBase::kNoFlags, - HistogramBase::kNoFlags, - &histogram_snapshot_manager_); - - const std::vector& 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, 4); - UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2); - - StatisticsRecorder::PrepareDeltas(false, HistogramBase::kNoFlags, - HistogramBase::kUmaTargetedHistogramFlag, - &histogram_snapshot_manager_); - - const std::vector& 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, 4); - UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2); - - StatisticsRecorder::PrepareDeltas(false, HistogramBase::kNoFlags, - HistogramBase::kUmaStabilityHistogramFlag, - &histogram_snapshot_manager_); - - const std::vector& histograms = - histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames(); - EXPECT_EQ(1U, histograms.size()); - EXPECT_EQ("UmaStabilityHistogram", histograms[0]); -} - -} // namespace base diff --git a/metrics/histogram_unittest.cc b/metrics/histogram_unittest.cc deleted file mode 100644 index e516acbe2..000000000 --- a/metrics/histogram_unittest.cc +++ /dev/null @@ -1,882 +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/metrics/histogram.h" - -#include -#include -#include - -#include -#include -#include -#include - -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/metrics/bucket_ranges.h" -#include "base/metrics/dummy_histogram.h" -#include "base/metrics/histogram_macros.h" -#include "base/metrics/metrics_hashes.h" -#include "base/metrics/persistent_histogram_allocator.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/metrics/record_histogram_checker.h" -#include "base/metrics/sample_vector.h" -#include "base/metrics/statistics_recorder.h" -#include "base/pickle.h" -#include "base/strings/stringprintf.h" -#include "base/test/gtest_util.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -const char kExpiredHistogramName[] = "ExpiredHistogram"; - -// Test implementation of RecordHistogramChecker interface. -class TestRecordHistogramChecker : public RecordHistogramChecker { - public: - ~TestRecordHistogramChecker() override = default; - - // RecordHistogramChecker: - bool ShouldRecord(uint64_t histogram_hash) const override { - return histogram_hash != HashMetricName(kExpiredHistogramName); - } -}; - -} // namespace - -// Test parameter indicates if a persistent memory allocator should be used -// for histogram allocation. False will allocate histograms from the process -// heap. -class HistogramTest : public testing::TestWithParam { - protected: - const int32_t kAllocatorMemorySize = 8 << 20; // 8 MiB - - HistogramTest() : use_persistent_histogram_allocator_(GetParam()) {} - - void SetUp() override { - if (use_persistent_histogram_allocator_) - CreatePersistentHistogramAllocator(); - - // Each test will have a clean state (no Histogram / BucketRanges - // registered). - InitializeStatisticsRecorder(); - } - - void TearDown() override { - if (allocator_) { - ASSERT_FALSE(allocator_->IsFull()); - ASSERT_FALSE(allocator_->IsCorrupt()); - } - UninitializeStatisticsRecorder(); - DestroyPersistentHistogramAllocator(); - } - - void InitializeStatisticsRecorder() { - DCHECK(!statistics_recorder_); - statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting(); - } - - void UninitializeStatisticsRecorder() { - statistics_recorder_.reset(); - } - - void CreatePersistentHistogramAllocator() { - GlobalHistogramAllocator::CreateWithLocalMemory( - kAllocatorMemorySize, 0, "HistogramAllocatorTest"); - allocator_ = GlobalHistogramAllocator::Get()->memory_allocator(); - } - - void DestroyPersistentHistogramAllocator() { - allocator_ = nullptr; - GlobalHistogramAllocator::ReleaseForTesting(); - } - - std::unique_ptr SnapshotAllSamples(Histogram* h) { - return h->SnapshotAllSamples(); - } - - const bool use_persistent_histogram_allocator_; - - std::unique_ptr statistics_recorder_; - std::unique_ptr allocator_memory_; - PersistentMemoryAllocator* allocator_ = nullptr; - - private: - DISALLOW_COPY_AND_ASSIGN(HistogramTest); -}; - -// Run all HistogramTest cases with both heap and persistent memory. -INSTANTIATE_TEST_CASE_P(HeapAndPersistent, HistogramTest, testing::Bool()); - - -// Check for basic syntax and use. -TEST_P(HistogramTest, BasicTest) { - // Try basic construction - HistogramBase* histogram = Histogram::FactoryGet( - "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - EXPECT_TRUE(histogram); - - HistogramBase* linear_histogram = LinearHistogram::FactoryGet( - "TestLinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - EXPECT_TRUE(linear_histogram); - - std::vector custom_ranges; - custom_ranges.push_back(1); - custom_ranges.push_back(5); - HistogramBase* custom_histogram = CustomHistogram::FactoryGet( - "TestCustomHistogram", custom_ranges, HistogramBase::kNoFlags); - EXPECT_TRUE(custom_histogram); - - // Macros that create histograms have an internal static variable which will - // continue to point to those from the very first run of this method even - // during subsequent runs. - static bool already_run = false; - if (already_run) - return; - already_run = true; - - // Use standard macros (but with fixed samples) - LOCAL_HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1)); - LOCAL_HISTOGRAM_COUNTS("Test3Histogram", 30); - - LOCAL_HISTOGRAM_ENUMERATION("Test6Histogram", 129, 130); -} - -// Check that the macro correctly matches histograms by name and records their -// data together. -TEST_P(HistogramTest, NameMatchTest) { - // Macros that create histograms have an internal static variable which will - // continue to point to those from the very first run of this method even - // during subsequent runs. - static bool already_run = false; - if (already_run) - return; - already_run = true; - - LOCAL_HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10); - LOCAL_HISTOGRAM_PERCENTAGE("DuplicatedHistogram", 10); - HistogramBase* histogram = LinearHistogram::FactoryGet( - "DuplicatedHistogram", 1, 101, 102, HistogramBase::kNoFlags); - - std::unique_ptr samples = histogram->SnapshotSamples(); - EXPECT_EQ(2, samples->TotalCount()); - EXPECT_EQ(2, samples->GetCount(10)); -} - -// Check that delta calculations work correctly. -TEST_P(HistogramTest, DeltaTest) { - HistogramBase* histogram = - Histogram::FactoryGet("DeltaHistogram", 1, 64, 8, - HistogramBase::kNoFlags); - histogram->Add(1); - histogram->Add(10); - histogram->Add(50); - - std::unique_ptr samples = histogram->SnapshotDelta(); - EXPECT_EQ(3, samples->TotalCount()); - EXPECT_EQ(1, samples->GetCount(1)); - EXPECT_EQ(1, samples->GetCount(10)); - EXPECT_EQ(1, samples->GetCount(50)); - EXPECT_EQ(samples->TotalCount(), samples->redundant_count()); - - samples = histogram->SnapshotDelta(); - EXPECT_EQ(0, samples->TotalCount()); - - histogram->Add(10); - histogram->Add(10); - samples = histogram->SnapshotDelta(); - EXPECT_EQ(2, samples->TotalCount()); - EXPECT_EQ(2, samples->GetCount(10)); - - samples = histogram->SnapshotDelta(); - EXPECT_EQ(0, samples->TotalCount()); -} - -// Check that final-delta calculations work correctly. -TEST_P(HistogramTest, FinalDeltaTest) { - HistogramBase* histogram = - Histogram::FactoryGet("FinalDeltaHistogram", 1, 64, 8, - HistogramBase::kNoFlags); - histogram->Add(1); - histogram->Add(10); - histogram->Add(50); - - std::unique_ptr samples = histogram->SnapshotDelta(); - EXPECT_EQ(3, samples->TotalCount()); - EXPECT_EQ(1, samples->GetCount(1)); - EXPECT_EQ(1, samples->GetCount(10)); - EXPECT_EQ(1, samples->GetCount(50)); - EXPECT_EQ(samples->TotalCount(), samples->redundant_count()); - - histogram->Add(2); - histogram->Add(50); - - samples = histogram->SnapshotFinalDelta(); - EXPECT_EQ(2, samples->TotalCount()); - EXPECT_EQ(1, samples->GetCount(2)); - EXPECT_EQ(1, samples->GetCount(50)); - EXPECT_EQ(samples->TotalCount(), samples->redundant_count()); -} - -TEST_P(HistogramTest, ExponentialRangesTest) { - // Check that we got a nice exponential when there was enough room. - BucketRanges ranges(9); - Histogram::InitializeBucketRanges(1, 64, &ranges); - EXPECT_EQ(0, ranges.range(0)); - int power_of_2 = 1; - for (int i = 1; i < 8; i++) { - EXPECT_EQ(power_of_2, ranges.range(i)); - power_of_2 *= 2; - } - EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8)); - - // Check the corresponding Histogram will use the correct ranges. - Histogram* histogram = static_cast( - Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags)); - EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges())); - - // When bucket count is limited, exponential ranges will partially look like - // linear. - BucketRanges ranges2(16); - Histogram::InitializeBucketRanges(1, 32, &ranges2); - - EXPECT_EQ(0, ranges2.range(0)); - EXPECT_EQ(1, ranges2.range(1)); - EXPECT_EQ(2, ranges2.range(2)); - EXPECT_EQ(3, ranges2.range(3)); - EXPECT_EQ(4, ranges2.range(4)); - EXPECT_EQ(5, ranges2.range(5)); - EXPECT_EQ(6, ranges2.range(6)); - EXPECT_EQ(7, ranges2.range(7)); - EXPECT_EQ(9, ranges2.range(8)); - EXPECT_EQ(11, ranges2.range(9)); - EXPECT_EQ(14, ranges2.range(10)); - EXPECT_EQ(17, ranges2.range(11)); - EXPECT_EQ(21, ranges2.range(12)); - EXPECT_EQ(26, ranges2.range(13)); - EXPECT_EQ(32, ranges2.range(14)); - EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(15)); - - // Check the corresponding Histogram will use the correct ranges. - Histogram* histogram2 = static_cast( - Histogram::FactoryGet("Histogram2", 1, 32, 15, HistogramBase::kNoFlags)); - EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges())); -} - -TEST_P(HistogramTest, LinearRangesTest) { - BucketRanges ranges(9); - LinearHistogram::InitializeBucketRanges(1, 7, &ranges); - // Gets a nice linear set of bucket ranges. - for (int i = 0; i < 8; i++) - EXPECT_EQ(i, ranges.range(i)); - EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges.range(8)); - - // The correspoding LinearHistogram should use the correct ranges. - Histogram* histogram = static_cast( - LinearHistogram::FactoryGet("Linear", 1, 7, 8, HistogramBase::kNoFlags)); - EXPECT_TRUE(ranges.Equals(histogram->bucket_ranges())); - - // Linear ranges are not divisible. - BucketRanges ranges2(6); - LinearHistogram::InitializeBucketRanges(1, 6, &ranges2); - EXPECT_EQ(0, ranges2.range(0)); - EXPECT_EQ(1, ranges2.range(1)); - EXPECT_EQ(3, ranges2.range(2)); - EXPECT_EQ(4, ranges2.range(3)); - EXPECT_EQ(6, ranges2.range(4)); - EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges2.range(5)); - // The correspoding LinearHistogram should use the correct ranges. - Histogram* histogram2 = static_cast( - LinearHistogram::FactoryGet("Linear2", 1, 6, 5, HistogramBase::kNoFlags)); - EXPECT_TRUE(ranges2.Equals(histogram2->bucket_ranges())); -} - -TEST_P(HistogramTest, ArrayToCustomEnumRangesTest) { - const HistogramBase::Sample ranges[3] = {5, 10, 20}; - std::vector ranges_vec = - CustomHistogram::ArrayToCustomEnumRanges(ranges); - ASSERT_EQ(6u, ranges_vec.size()); - EXPECT_EQ(5, ranges_vec[0]); - EXPECT_EQ(6, ranges_vec[1]); - EXPECT_EQ(10, ranges_vec[2]); - EXPECT_EQ(11, ranges_vec[3]); - EXPECT_EQ(20, ranges_vec[4]); - EXPECT_EQ(21, ranges_vec[5]); -} - -TEST_P(HistogramTest, CustomHistogramTest) { - // A well prepared custom ranges. - std::vector custom_ranges; - custom_ranges.push_back(1); - custom_ranges.push_back(2); - - Histogram* histogram = static_cast( - CustomHistogram::FactoryGet("TestCustomHistogram1", custom_ranges, - HistogramBase::kNoFlags)); - const BucketRanges* ranges = histogram->bucket_ranges(); - ASSERT_EQ(4u, ranges->size()); - EXPECT_EQ(0, ranges->range(0)); // Auto added. - EXPECT_EQ(1, ranges->range(1)); - EXPECT_EQ(2, ranges->range(2)); - EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3)); // Auto added. - - // A unordered custom ranges. - custom_ranges.clear(); - custom_ranges.push_back(2); - custom_ranges.push_back(1); - histogram = static_cast( - CustomHistogram::FactoryGet("TestCustomHistogram2", custom_ranges, - HistogramBase::kNoFlags)); - ranges = histogram->bucket_ranges(); - ASSERT_EQ(4u, ranges->size()); - EXPECT_EQ(0, ranges->range(0)); - EXPECT_EQ(1, ranges->range(1)); - EXPECT_EQ(2, ranges->range(2)); - EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3)); - - // A custom ranges with duplicated values. - custom_ranges.clear(); - custom_ranges.push_back(4); - custom_ranges.push_back(1); - custom_ranges.push_back(4); - histogram = static_cast( - CustomHistogram::FactoryGet("TestCustomHistogram3", custom_ranges, - HistogramBase::kNoFlags)); - ranges = histogram->bucket_ranges(); - ASSERT_EQ(4u, ranges->size()); - EXPECT_EQ(0, ranges->range(0)); - EXPECT_EQ(1, ranges->range(1)); - EXPECT_EQ(4, ranges->range(2)); - EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(3)); -} - -TEST_P(HistogramTest, CustomHistogramWithOnly2Buckets) { - // This test exploits the fact that the CustomHistogram can have 2 buckets, - // while the base class Histogram is *supposed* to have at least 3 buckets. - // We should probably change the restriction on the base class (or not inherit - // the base class!). - - std::vector custom_ranges; - custom_ranges.push_back(4); - - Histogram* histogram = static_cast( - CustomHistogram::FactoryGet("2BucketsCustomHistogram", custom_ranges, - HistogramBase::kNoFlags)); - const BucketRanges* ranges = histogram->bucket_ranges(); - ASSERT_EQ(3u, ranges->size()); - EXPECT_EQ(0, ranges->range(0)); - EXPECT_EQ(4, ranges->range(1)); - EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2)); -} - -TEST_P(HistogramTest, AddCountTest) { - const size_t kBucketCount = 50; - Histogram* histogram = static_cast( - Histogram::FactoryGet("AddCountHistogram", 10, 100, kBucketCount, - HistogramBase::kNoFlags)); - - histogram->AddCount(20, 15); - histogram->AddCount(30, 14); - - std::unique_ptr samples = histogram->SnapshotSamples(); - EXPECT_EQ(29, samples->TotalCount()); - EXPECT_EQ(15, samples->GetCount(20)); - EXPECT_EQ(14, samples->GetCount(30)); - - histogram->AddCount(20, 25); - histogram->AddCount(30, 24); - - std::unique_ptr samples2 = histogram->SnapshotSamples(); - EXPECT_EQ(78, samples2->TotalCount()); - EXPECT_EQ(40, samples2->GetCount(20)); - EXPECT_EQ(38, samples2->GetCount(30)); -} - -TEST_P(HistogramTest, AddCount_LargeValuesDontOverflow) { - const size_t kBucketCount = 50; - Histogram* histogram = static_cast( - Histogram::FactoryGet("AddCountHistogram", 10, 1000000000, kBucketCount, - HistogramBase::kNoFlags)); - - histogram->AddCount(200000000, 15); - histogram->AddCount(300000000, 14); - - std::unique_ptr samples = histogram->SnapshotSamples(); - EXPECT_EQ(29, samples->TotalCount()); - EXPECT_EQ(15, samples->GetCount(200000000)); - EXPECT_EQ(14, samples->GetCount(300000000)); - - histogram->AddCount(200000000, 25); - histogram->AddCount(300000000, 24); - - std::unique_ptr samples2 = histogram->SnapshotSamples(); - EXPECT_EQ(78, samples2->TotalCount()); - EXPECT_EQ(40, samples2->GetCount(200000000)); - EXPECT_EQ(38, samples2->GetCount(300000000)); - EXPECT_EQ(19400000000LL, samples2->sum()); -} - -// Some metrics are designed so that they are guaranteed not to overflow between -// snapshots, but could overflow over a long-running session. -// Make sure that counts returned by Histogram::SnapshotDelta do not overflow -// even when a total count (returned by Histogram::SnapshotSample) does. -TEST_P(HistogramTest, AddCount_LargeCountsDontOverflow) { - const size_t kBucketCount = 10; - Histogram* histogram = static_cast(Histogram::FactoryGet( - "AddCountHistogram", 10, 50, kBucketCount, HistogramBase::kNoFlags)); - - const int count = (1 << 30) - 1; - - // Repeat N times to make sure that there is no internal value overflow. - for (int i = 0; i < 10; ++i) { - histogram->AddCount(42, count); - std::unique_ptr samples = histogram->SnapshotDelta(); - EXPECT_EQ(count, samples->TotalCount()); - EXPECT_EQ(count, samples->GetCount(42)); - } -} - -// Make sure histogram handles out-of-bounds data gracefully. -TEST_P(HistogramTest, BoundsTest) { - const size_t kBucketCount = 50; - Histogram* histogram = static_cast( - Histogram::FactoryGet("Bounded", 10, 100, kBucketCount, - HistogramBase::kNoFlags)); - - // Put two samples "out of bounds" above and below. - histogram->Add(5); - histogram->Add(-50); - - histogram->Add(100); - histogram->Add(10000); - - // Verify they landed in the underflow, and overflow buckets. - std::unique_ptr samples = histogram->SnapshotAllSamples(); - EXPECT_EQ(2, samples->GetCountAtIndex(0)); - EXPECT_EQ(0, samples->GetCountAtIndex(1)); - size_t array_size = histogram->bucket_count(); - EXPECT_EQ(kBucketCount, array_size); - EXPECT_EQ(0, samples->GetCountAtIndex(array_size - 2)); - EXPECT_EQ(2, samples->GetCountAtIndex(array_size - 1)); - - std::vector custom_ranges; - custom_ranges.push_back(10); - custom_ranges.push_back(50); - custom_ranges.push_back(100); - Histogram* test_custom_histogram = static_cast( - CustomHistogram::FactoryGet("TestCustomRangeBoundedHistogram", - custom_ranges, HistogramBase::kNoFlags)); - - // Put two samples "out of bounds" above and below. - test_custom_histogram->Add(5); - test_custom_histogram->Add(-50); - test_custom_histogram->Add(100); - test_custom_histogram->Add(1000); - test_custom_histogram->Add(INT_MAX); - - // Verify they landed in the underflow, and overflow buckets. - std::unique_ptr custom_samples = - test_custom_histogram->SnapshotAllSamples(); - EXPECT_EQ(2, custom_samples->GetCountAtIndex(0)); - EXPECT_EQ(0, custom_samples->GetCountAtIndex(1)); - size_t bucket_count = test_custom_histogram->bucket_count(); - EXPECT_EQ(0, custom_samples->GetCountAtIndex(bucket_count - 2)); - EXPECT_EQ(3, custom_samples->GetCountAtIndex(bucket_count - 1)); -} - -// Check to be sure samples land as expected is "correct" buckets. -TEST_P(HistogramTest, BucketPlacementTest) { - Histogram* histogram = static_cast( - Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags)); - - // Add i+1 samples to the i'th bucket. - histogram->Add(0); - int power_of_2 = 1; - for (int i = 1; i < 8; i++) { - for (int j = 0; j <= i; j++) - histogram->Add(power_of_2); - power_of_2 *= 2; - } - - // Check to see that the bucket counts reflect our additions. - std::unique_ptr samples = histogram->SnapshotAllSamples(); - for (int i = 0; i < 8; i++) - EXPECT_EQ(i + 1, samples->GetCountAtIndex(i)); -} - -TEST_P(HistogramTest, CorruptSampleCounts) { - // The internal code creates histograms via macros and thus keeps static - // pointers to them. If those pointers are to persistent memory which will - // be free'd then any following calls to that code will crash with a - // segmentation violation. - if (use_persistent_histogram_allocator_) - return; - - Histogram* histogram = static_cast( - Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags)); - - // Add some samples. - histogram->Add(20); - histogram->Add(40); - - std::unique_ptr snapshot = histogram->SnapshotAllSamples(); - EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES, - histogram->FindCorruption(*snapshot)); - EXPECT_EQ(2, snapshot->redundant_count()); - EXPECT_EQ(2, snapshot->TotalCount()); - - snapshot->counts()[3] += 100; // Sample count won't match redundant count. - EXPECT_EQ(HistogramBase::COUNT_LOW_ERROR, - histogram->FindCorruption(*snapshot)); - snapshot->counts()[2] -= 200; - EXPECT_EQ(HistogramBase::COUNT_HIGH_ERROR, - histogram->FindCorruption(*snapshot)); - - // But we can't spot a corruption if it is compensated for. - snapshot->counts()[1] += 100; - EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES, - histogram->FindCorruption(*snapshot)); -} - -TEST_P(HistogramTest, CorruptBucketBounds) { - Histogram* histogram = static_cast( - Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags)); - - std::unique_ptr snapshot = histogram->SnapshotSamples(); - EXPECT_EQ(HistogramBase::NO_INCONSISTENCIES, - histogram->FindCorruption(*snapshot)); - - BucketRanges* bucket_ranges = - const_cast(histogram->bucket_ranges()); - HistogramBase::Sample tmp = bucket_ranges->range(1); - bucket_ranges->set_range(1, bucket_ranges->range(2)); - bucket_ranges->set_range(2, tmp); - EXPECT_EQ( - HistogramBase::BUCKET_ORDER_ERROR | HistogramBase::RANGE_CHECKSUM_ERROR, - histogram->FindCorruption(*snapshot)); - - bucket_ranges->set_range(2, bucket_ranges->range(1)); - bucket_ranges->set_range(1, tmp); - EXPECT_EQ(0U, histogram->FindCorruption(*snapshot)); - - // Show that two simple changes don't offset each other - bucket_ranges->set_range(3, bucket_ranges->range(3) + 1); - EXPECT_EQ(HistogramBase::RANGE_CHECKSUM_ERROR, - histogram->FindCorruption(*snapshot)); - - bucket_ranges->set_range(4, bucket_ranges->range(4) - 1); - EXPECT_EQ(HistogramBase::RANGE_CHECKSUM_ERROR, - histogram->FindCorruption(*snapshot)); - - // Repair histogram so that destructor won't DCHECK(). - bucket_ranges->set_range(3, bucket_ranges->range(3) - 1); - bucket_ranges->set_range(4, bucket_ranges->range(4) + 1); -} - -TEST_P(HistogramTest, HistogramSerializeInfo) { - Histogram* histogram = static_cast( - Histogram::FactoryGet("Histogram", 1, 64, 8, - HistogramBase::kIPCSerializationSourceFlag)); - Pickle pickle; - histogram->SerializeInfo(&pickle); - - PickleIterator iter(pickle); - - int type; - EXPECT_TRUE(iter.ReadInt(&type)); - EXPECT_EQ(HISTOGRAM, type); - - std::string name; - EXPECT_TRUE(iter.ReadString(&name)); - EXPECT_EQ("Histogram", name); - - int flag; - EXPECT_TRUE(iter.ReadInt(&flag)); - EXPECT_EQ(HistogramBase::kIPCSerializationSourceFlag, - flag & ~HistogramBase::kIsPersistent); - - int min; - EXPECT_TRUE(iter.ReadInt(&min)); - EXPECT_EQ(1, min); - - int max; - EXPECT_TRUE(iter.ReadInt(&max)); - EXPECT_EQ(64, max); - - uint32_t bucket_count; - EXPECT_TRUE(iter.ReadUInt32(&bucket_count)); - EXPECT_EQ(8u, bucket_count); - - uint32_t checksum; - EXPECT_TRUE(iter.ReadUInt32(&checksum)); - EXPECT_EQ(histogram->bucket_ranges()->checksum(), checksum); - - // No more data in the pickle. - EXPECT_FALSE(iter.SkipBytes(1)); -} - -TEST_P(HistogramTest, CustomHistogramSerializeInfo) { - std::vector custom_ranges; - custom_ranges.push_back(10); - custom_ranges.push_back(100); - - HistogramBase* custom_histogram = CustomHistogram::FactoryGet( - "TestCustomRangeBoundedHistogram", - custom_ranges, - HistogramBase::kNoFlags); - Pickle pickle; - custom_histogram->SerializeInfo(&pickle); - - // Validate the pickle. - PickleIterator iter(pickle); - - int i; - std::string s; - uint32_t bucket_count; - uint32_t ui32; - EXPECT_TRUE(iter.ReadInt(&i) && iter.ReadString(&s) && iter.ReadInt(&i) && - iter.ReadInt(&i) && iter.ReadInt(&i) && - iter.ReadUInt32(&bucket_count) && iter.ReadUInt32(&ui32)); - EXPECT_EQ(3u, bucket_count); - - int range; - EXPECT_TRUE(iter.ReadInt(&range)); - EXPECT_EQ(10, range); - EXPECT_TRUE(iter.ReadInt(&range)); - EXPECT_EQ(100, range); - - // No more data in the pickle. - EXPECT_FALSE(iter.SkipBytes(1)); -} - -TEST_P(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(DummyHistogram::GetInstance(), bad_histogram); - bad_histogram = Histogram::FactoryGet( - "BadConstruction", 0, 99, 8, HistogramBase::kNoFlags); - EXPECT_EQ(DummyHistogram::GetInstance(), 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(DummyHistogram::GetInstance(), bad_histogram); - bad_histogram = LinearHistogram::FactoryGet( - "BadConstructionLinear", 10, 100, 8, HistogramBase::kNoFlags); - EXPECT_EQ(DummyHistogram::GetInstance(), bad_histogram); -} - -TEST_P(HistogramTest, FactoryTime) { - const int kTestCreateCount = 1 << 14; // Must be power-of-2. - const int kTestLookupCount = 100000; - const int kTestAddCount = 1000000; - - // Create all histogram names in advance for accurate timing below. - std::vector histogram_names; - for (int i = 0; i < kTestCreateCount; ++i) { - histogram_names.push_back( - StringPrintf("TestHistogram.%d", i % kTestCreateCount)); - } - - // Calculate cost of creating histograms. - TimeTicks create_start = TimeTicks::Now(); - for (int i = 0; i < kTestCreateCount; ++i) { - Histogram::FactoryGet(histogram_names[i], 1, 100, 10, - HistogramBase::kNoFlags); - } - TimeDelta create_ticks = TimeTicks::Now() - create_start; - int64_t create_ms = create_ticks.InMilliseconds(); - - VLOG(1) << kTestCreateCount << " histogram creations took " << create_ms - << "ms or about " - << (create_ms * 1000000) / kTestCreateCount - << "ns each."; - - // Calculate cost of looking up existing histograms. - TimeTicks lookup_start = TimeTicks::Now(); - for (int i = 0; i < kTestLookupCount; ++i) { - // 6007 is co-prime with kTestCreateCount and so will do lookups in an - // order less likely to be cacheable (but still hit them all) should the - // underlying storage use the exact histogram name as the key. - const int i_mult = 6007; - static_assert(i_mult < INT_MAX / kTestCreateCount, "Multiplier too big"); - int index = (i * i_mult) & (kTestCreateCount - 1); - Histogram::FactoryGet(histogram_names[index], 1, 100, 10, - HistogramBase::kNoFlags); - } - TimeDelta lookup_ticks = TimeTicks::Now() - lookup_start; - int64_t lookup_ms = lookup_ticks.InMilliseconds(); - - VLOG(1) << kTestLookupCount << " histogram lookups took " << lookup_ms - << "ms or about " - << (lookup_ms * 1000000) / kTestLookupCount - << "ns each."; - - // Calculate cost of accessing histograms. - HistogramBase* histogram = Histogram::FactoryGet( - histogram_names[0], 1, 100, 10, HistogramBase::kNoFlags); - ASSERT_TRUE(histogram); - TimeTicks add_start = TimeTicks::Now(); - for (int i = 0; i < kTestAddCount; ++i) - histogram->Add(i & 127); - TimeDelta add_ticks = TimeTicks::Now() - add_start; - int64_t add_ms = add_ticks.InMilliseconds(); - - VLOG(1) << kTestAddCount << " histogram adds took " << add_ms - << "ms or about " - << (add_ms * 1000000) / kTestAddCount - << "ns each."; -} - -TEST_P(HistogramTest, ScaledLinearHistogram) { - ScaledLinearHistogram scaled("SLH", 1, 5, 6, 100, HistogramBase::kNoFlags); - - scaled.AddScaledCount(0, 1); - scaled.AddScaledCount(1, 49); - scaled.AddScaledCount(2, 50); - scaled.AddScaledCount(3, 101); - scaled.AddScaledCount(4, 160); - scaled.AddScaledCount(5, 130); - scaled.AddScaledCount(6, 140); - - std::unique_ptr samples = - SnapshotAllSamples(scaled.histogram()); - EXPECT_EQ(0, samples->GetCountAtIndex(0)); - EXPECT_EQ(0, samples->GetCountAtIndex(1)); - EXPECT_EQ(1, samples->GetCountAtIndex(2)); - EXPECT_EQ(1, samples->GetCountAtIndex(3)); - EXPECT_EQ(2, samples->GetCountAtIndex(4)); - EXPECT_EQ(3, samples->GetCountAtIndex(5)); - - // Make sure the macros compile properly. This can only be run when - // there is no persistent allocator which can be discarded and leave - // dangling pointers. - if (!use_persistent_histogram_allocator_) { - enum EnumWithMax { - kA = 0, - kB = 1, - kC = 2, - kMaxValue = kC, - }; - UMA_HISTOGRAM_SCALED_EXACT_LINEAR("h1", 1, 5000, 5, 100); - UMA_HISTOGRAM_SCALED_ENUMERATION("h2", kB, 5000, 100); - } -} - -// For Histogram, LinearHistogram and CustomHistogram, the minimum for a -// declared range is 1, while the maximum is (HistogramBase::kSampleType_MAX - -// 1). But we accept ranges exceeding those limits, and silently clamped to -// those limits. This is for backwards compatibility. -TEST(HistogramDeathTest, BadRangesTest) { - HistogramBase* histogram = Histogram::FactoryGet( - "BadRanges", 0, HistogramBase::kSampleType_MAX, 8, - HistogramBase::kNoFlags); - EXPECT_TRUE( - histogram->HasConstructionArguments( - 1, HistogramBase::kSampleType_MAX - 1, 8)); - - HistogramBase* linear_histogram = LinearHistogram::FactoryGet( - "BadRangesLinear", 0, HistogramBase::kSampleType_MAX, 8, - HistogramBase::kNoFlags); - EXPECT_TRUE( - linear_histogram->HasConstructionArguments( - 1, HistogramBase::kSampleType_MAX - 1, 8)); - - std::vector custom_ranges; - custom_ranges.push_back(0); - custom_ranges.push_back(5); - Histogram* custom_histogram = static_cast( - CustomHistogram::FactoryGet( - "BadRangesCustom", custom_ranges, HistogramBase::kNoFlags)); - const BucketRanges* ranges = custom_histogram->bucket_ranges(); - ASSERT_EQ(3u, ranges->size()); - EXPECT_EQ(0, ranges->range(0)); - EXPECT_EQ(5, ranges->range(1)); - EXPECT_EQ(HistogramBase::kSampleType_MAX, ranges->range(2)); - - // CustomHistogram does not accepts kSampleType_MAX as range. - custom_ranges.push_back(HistogramBase::kSampleType_MAX); - EXPECT_DEATH_IF_SUPPORTED( - CustomHistogram::FactoryGet("BadRangesCustom2", custom_ranges, - HistogramBase::kNoFlags), - ""); - - // CustomHistogram needs at least 1 valid range. - custom_ranges.clear(); - custom_ranges.push_back(0); - EXPECT_DEATH_IF_SUPPORTED( - CustomHistogram::FactoryGet("BadRangesCustom3", custom_ranges, - HistogramBase::kNoFlags), - ""); -} - -TEST_P(HistogramTest, ExpiredHistogramTest) { - auto record_checker = std::make_unique(); - StatisticsRecorder::SetRecordChecker(std::move(record_checker)); - - HistogramBase* expired = Histogram::FactoryGet(kExpiredHistogramName, 1, 1000, - 10, HistogramBase::kNoFlags); - ASSERT_TRUE(expired); - expired->Add(5); - expired->Add(500); - auto samples = expired->SnapshotDelta(); - EXPECT_EQ(0, samples->TotalCount()); - - HistogramBase* linear_expired = LinearHistogram::FactoryGet( - kExpiredHistogramName, 1, 1000, 10, HistogramBase::kNoFlags); - ASSERT_TRUE(linear_expired); - linear_expired->Add(5); - linear_expired->Add(500); - samples = linear_expired->SnapshotDelta(); - EXPECT_EQ(0, samples->TotalCount()); - - std::vector custom_ranges; - custom_ranges.push_back(1); - custom_ranges.push_back(5); - HistogramBase* custom_expired = CustomHistogram::FactoryGet( - kExpiredHistogramName, custom_ranges, HistogramBase::kNoFlags); - ASSERT_TRUE(custom_expired); - custom_expired->Add(2); - custom_expired->Add(4); - samples = custom_expired->SnapshotDelta(); - EXPECT_EQ(0, samples->TotalCount()); - - HistogramBase* valid = Histogram::FactoryGet("ValidHistogram", 1, 1000, 10, - HistogramBase::kNoFlags); - ASSERT_TRUE(valid); - valid->Add(5); - valid->Add(500); - samples = valid->SnapshotDelta(); - EXPECT_EQ(2, samples->TotalCount()); - - HistogramBase* linear_valid = LinearHistogram::FactoryGet( - "LinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - ASSERT_TRUE(linear_valid); - linear_valid->Add(5); - linear_valid->Add(500); - samples = linear_valid->SnapshotDelta(); - EXPECT_EQ(2, samples->TotalCount()); - - HistogramBase* custom_valid = CustomHistogram::FactoryGet( - "CustomHistogram", custom_ranges, HistogramBase::kNoFlags); - ASSERT_TRUE(custom_valid); - custom_valid->Add(2); - custom_valid->Add(4); - samples = custom_valid->SnapshotDelta(); - EXPECT_EQ(2, samples->TotalCount()); -} - -} // namespace base diff --git a/metrics/histogram_unittest.nc b/metrics/histogram_unittest.nc deleted file mode 100644 index c9c265799..000000000 --- a/metrics/histogram_unittest.nc +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This is a "No Compile Test" suite. -// http://dev.chromium.org/developers/testing/no-compile-tests - -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" - -namespace base { - -#if defined(NCTEST_DIFFERENT_ENUM) // [r"\|sample\| and \|boundary\| shouldn't be of different enums"] - -void WontCompile() { - enum TypeA { A }; - enum TypeB { B }; - UMA_HISTOGRAM_ENUMERATION("", A, B); -} - -#elif defined(NCTEST_DIFFERENT_ENUM_CLASS) // [r"\|sample\| and \|boundary\| shouldn't be of different enums"] - -void WontCompile() { - enum class TypeA { A }; - enum class TypeB { B }; - UMA_HISTOGRAM_ENUMERATION("", TypeA::A, TypeB::B); -} - -#elif defined(NCTEST_DIFFERENT_ENUM_MIXED) // [r"\|sample\| and \|boundary\| shouldn't be of different enums"] - -void WontCompile() { - enum class TypeA { A }; - enum TypeB { B }; - UMA_HISTOGRAM_ENUMERATION("", TypeA::A, B); -} - -#elif defined(NCTEST_NEGATIVE_ENUM_MAX) // [r'static_assert failed "\|boundary\| is out of range of HistogramBase::Sample"'] - -void WontCompile() { - // Buckets for enumeration start from 0, so a boundary < 0 is illegal. - enum class TypeA { A = -1 }; - UMA_HISTOGRAM_ENUMERATION("", TypeA::A, TypeA::A); -} - -#elif defined(NCTEST_ENUM_MAX_OUT_OF_RANGE) // [r'static_assert failed "\|boundary\| is out of range of HistogramBase::Sample"'] - -void WontCompile() { - // HistogramBase::Sample is an int and can't hold larger values. - enum class TypeA : uint32_t { A = 0xffffffff }; - UMA_HISTOGRAM_ENUMERATION("", TypeA::A, TypeA::A); -} - -#elif defined(NCTEST_SAMPLE_NOT_ENUM) // [r'static_assert failed "Unexpected: \|boundary\| is enum, but \|sample\| is not."'] - -void WontCompile() { - enum TypeA { A }; - UMA_HISTOGRAM_ENUMERATION("", 0, TypeA::A); -} - -#elif defined(NCTEST_FUNCTION_INT) // [r"Non enum passed to UmaHistogramEnumeration"] - -void WontCompile() { - UmaHistogramEnumeration("", 1, 2); -} - -#elif defined(NCTEST_FUNCTION_DIFFERENT_ENUM) // [r"no matching function for call to 'UmaHistogramEnumeration'"] - -void WontCompile() { - enum TypeA { A }; - enum TypeB { B }; - UmaHistogramEnumeration("", A, B); -} - -#elif defined(NCTEST_FUNCTION_FIRST_NOT_ENUM) // [r"no matching function for call to 'UmaHistogramEnumeration'"] - -void WontCompile() { - enum TypeB { B }; - UmaHistogramEnumeration("", 1, B); -} - -#elif defined(NCTEST_FUNCTION_SECOND_NOT_ENUM) // [r"no matching function for call to 'UmaHistogramEnumeration'"] - -void WontCompile() { - enum TypeA { A }; - UmaHistogramEnumeration("", A, 2); -} - -#endif - -} // namespace base diff --git a/metrics/metrics_hashes.cc b/metrics/metrics_hashes.cc deleted file mode 100644 index 5672b06de..000000000 --- a/metrics/metrics_hashes.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/metrics/metrics_hashes.h" - -#include "base/logging.h" -#include "base/md5.h" -#include "base/sys_byteorder.h" - -namespace base { - -namespace { - -// Converts the 8-byte prefix of an MD5 hash into a uint64_t value. -inline uint64_t DigestToUInt64(const base::MD5Digest& digest) { - uint64_t value; - DCHECK_GE(sizeof(digest.a), sizeof(value)); - memcpy(&value, digest.a, sizeof(value)); - return base::NetToHost64(value); -} - -} // namespace - -uint64_t HashMetricName(base::StringPiece name) { - base::MD5Digest digest; - base::MD5Sum(name.data(), name.size(), &digest); - return DigestToUInt64(digest); -} - -} // namespace metrics diff --git a/metrics/metrics_hashes.h b/metrics/metrics_hashes.h deleted file mode 100644 index d05c4ba9b..000000000 --- a/metrics/metrics_hashes.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_METRICS_METRICS_HASHES_H_ -#define BASE_METRICS_METRICS_HASHES_H_ - -#include - -#include "base/base_export.h" -#include "base/strings/string_piece.h" - -namespace base { - -// Computes a uint64_t hash of a given string based on its MD5 hash. Suitable -// for metric names. -BASE_EXPORT uint64_t HashMetricName(base::StringPiece name); - -} // namespace metrics - -#endif // BASE_METRICS_METRICS_HASHES_H_ diff --git a/metrics/metrics_hashes_unittest.cc b/metrics/metrics_hashes_unittest.cc deleted file mode 100644 index aea254ef4..000000000 --- a/metrics/metrics_hashes_unittest.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/metrics/metrics_hashes.h" - -#include -#include - -#include "base/format_macros.h" -#include "base/macros.h" -#include "base/strings/stringprintf.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -// Make sure our ID hashes are the same as what we see on the server side. -TEST(MetricsUtilTest, HashMetricName) { - static const struct { - std::string input; - std::string output; - } cases[] = { - {"Back", "0x0557fa923dcee4d0"}, - {"Forward", "0x67d2f6740a8eaebf"}, - {"NewTab", "0x290eb683f96572f1"}, - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - uint64_t hash = HashMetricName(cases[i].input); - std::string hash_hex = base::StringPrintf("0x%016" PRIx64, hash); - EXPECT_EQ(cases[i].output, hash_hex); - } -} - -} // namespace metrics diff --git a/metrics/persistent_histogram_allocator.cc b/metrics/persistent_histogram_allocator.cc deleted file mode 100644 index cb01c410f..000000000 --- a/metrics/persistent_histogram_allocator.cc +++ /dev/null @@ -1,1008 +0,0 @@ -// Copyright 2016 The Chromium 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/persistent_histogram_allocator.h" - -#include - -#include "base/atomicops.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/important_file_writer.h" -#include "base/files/memory_mapped_file.h" -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_samples.h" -#include "base/metrics/metrics_hashes.h" -#include "base/metrics/persistent_sample_map.h" -#include "base/metrics/sparse_histogram.h" -#include "base/metrics/statistics_recorder.h" -#include "base/numerics/safe_conversions.h" -#include "base/pickle.h" -#include "base/process/process_handle.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_split.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/lock.h" - -namespace base { - -namespace { - -// Type identifiers used when storing in persistent memory so they can be -// identified during extraction; the first 4 bytes of the SHA1 of the name -// is used as a unique integer. A "version number" is added to the base -// so that, if the structure of that object changes, stored older versions -// will be safely ignored. -enum : uint32_t { - kTypeIdRangesArray = 0xBCEA225A + 1, // SHA1(RangesArray) v1 - kTypeIdCountsArray = 0x53215530 + 1, // SHA1(CountsArray) v1 -}; - -// The current globally-active persistent allocator for all new histograms. -// The object held here will obviously not be destructed at process exit -// but that's best since PersistentMemoryAllocator objects (that underlie -// GlobalHistogramAllocator objects) are explicitly forbidden from doing -// anything essential at exit anyway due to the fact that they depend on data -// managed elsewhere and which could be destructed first. An AtomicWord is -// used instead of std::atomic because the latter can create global ctors -// and dtors. -subtle::AtomicWord g_histogram_allocator = 0; - -// Take an array of range boundaries and create a proper BucketRanges object -// which is returned to the caller. A return of nullptr indicates that the -// passed boundaries are invalid. -std::unique_ptr CreateRangesFromData( - HistogramBase::Sample* ranges_data, - uint32_t ranges_checksum, - size_t count) { - // To avoid racy destruction at shutdown, the following may be leaked. - std::unique_ptr ranges(new BucketRanges(count)); - DCHECK_EQ(count, ranges->size()); - for (size_t i = 0; i < count; ++i) { - if (i > 0 && ranges_data[i] <= ranges_data[i - 1]) - return nullptr; - ranges->set_range(i, ranges_data[i]); - } - - ranges->ResetChecksum(); - if (ranges->checksum() != ranges_checksum) - return nullptr; - - return ranges; -} - -// Calculate the number of bytes required to store all of a histogram's -// "counts". This will return zero (0) if |bucket_count| is not valid. -size_t CalculateRequiredCountsBytes(size_t bucket_count) { - // 2 because each "sample count" also requires a backup "logged count" - // used for calculating the delta during snapshot operations. - const size_t kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount); - - // If the |bucket_count| is such that it would overflow the return type, - // perhaps as the result of a malicious actor, then return zero to - // indicate the problem to the caller. - if (bucket_count > std::numeric_limits::max() / kBytesPerBucket) - return 0; - - return bucket_count * kBytesPerBucket; -} - -} // namespace - -const Feature kPersistentHistogramsFeature{ - "PersistentHistograms", FEATURE_DISABLED_BY_DEFAULT -}; - - -PersistentSparseHistogramDataManager::PersistentSparseHistogramDataManager( - PersistentMemoryAllocator* allocator) - : allocator_(allocator), record_iterator_(allocator) {} - -PersistentSparseHistogramDataManager::~PersistentSparseHistogramDataManager() = - default; - -PersistentSampleMapRecords* -PersistentSparseHistogramDataManager::UseSampleMapRecords(uint64_t id, - const void* user) { - base::AutoLock auto_lock(lock_); - return GetSampleMapRecordsWhileLocked(id)->Acquire(user); -} - -PersistentSampleMapRecords* -PersistentSparseHistogramDataManager::GetSampleMapRecordsWhileLocked( - uint64_t id) { - lock_.AssertAcquired(); - - auto found = sample_records_.find(id); - if (found != sample_records_.end()) - return found->second.get(); - - std::unique_ptr& samples = sample_records_[id]; - samples = std::make_unique(this, id); - return samples.get(); -} - -bool PersistentSparseHistogramDataManager::LoadRecords( - PersistentSampleMapRecords* sample_map_records) { - // DataManager must be locked in order to access the found_ field of any - // PersistentSampleMapRecords object. - base::AutoLock auto_lock(lock_); - bool found = false; - - // If there are already "found" entries for the passed object, move them. - if (!sample_map_records->found_.empty()) { - sample_map_records->records_.reserve(sample_map_records->records_.size() + - sample_map_records->found_.size()); - sample_map_records->records_.insert(sample_map_records->records_.end(), - sample_map_records->found_.begin(), - sample_map_records->found_.end()); - sample_map_records->found_.clear(); - found = true; - } - - // Acquiring a lock is a semi-expensive operation so load some records with - // each call. More than this number may be loaded if it takes longer to - // find at least one matching record for the passed object. - const int kMinimumNumberToLoad = 10; - const uint64_t match_id = sample_map_records->sample_map_id_; - - // Loop while no enty is found OR we haven't yet loaded the minimum number. - // This will continue reading even after a match is found. - for (int count = 0; !found || count < kMinimumNumberToLoad; ++count) { - // Get the next sample-record. The iterator will always resume from where - // it left off even if it previously had nothing further to return. - uint64_t found_id; - PersistentMemoryAllocator::Reference ref = - PersistentSampleMap::GetNextPersistentRecord(record_iterator_, - &found_id); - - // Stop immediately if there are none. - if (!ref) - break; - - // The sample-record could be for any sparse histogram. Add the reference - // to the appropriate collection for later use. - if (found_id == match_id) { - sample_map_records->records_.push_back(ref); - found = true; - } else { - PersistentSampleMapRecords* samples = - GetSampleMapRecordsWhileLocked(found_id); - DCHECK(samples); - samples->found_.push_back(ref); - } - } - - return found; -} - - -PersistentSampleMapRecords::PersistentSampleMapRecords( - PersistentSparseHistogramDataManager* data_manager, - uint64_t sample_map_id) - : data_manager_(data_manager), sample_map_id_(sample_map_id) {} - -PersistentSampleMapRecords::~PersistentSampleMapRecords() = default; - -PersistentSampleMapRecords* PersistentSampleMapRecords::Acquire( - const void* user) { - DCHECK(!user_); - user_ = user; - seen_ = 0; - return this; -} - -void PersistentSampleMapRecords::Release(const void* user) { - DCHECK_EQ(user_, user); - user_ = nullptr; -} - -PersistentMemoryAllocator::Reference PersistentSampleMapRecords::GetNext() { - DCHECK(user_); - - // If there are no unseen records, lock and swap in all the found ones. - if (records_.size() == seen_) { - if (!data_manager_->LoadRecords(this)) - return false; - } - - // Return the next record. Records *must* be returned in the same order - // they are found in the persistent memory in order to ensure that all - // objects using this data always have the same state. Race conditions - // can cause duplicate records so using the "first found" is the only - // guarantee that all objects always access the same one. - DCHECK_LT(seen_, records_.size()); - return records_[seen_++]; -} - -PersistentMemoryAllocator::Reference PersistentSampleMapRecords::CreateNew( - HistogramBase::Sample value) { - return PersistentSampleMap::CreatePersistentRecord(data_manager_->allocator_, - sample_map_id_, value); -} - - -// This data will be held in persistent memory in order for processes to -// locate and use histograms created elsewhere. -struct PersistentHistogramAllocator::PersistentHistogramData { - // SHA1(Histogram): Increment this if structure changes! - static constexpr uint32_t kPersistentTypeId = 0xF1645910 + 3; - - // Expected size for 32/64-bit check. - static constexpr size_t kExpectedInstanceSize = - 40 + 2 * HistogramSamples::Metadata::kExpectedInstanceSize; - - int32_t histogram_type; - int32_t flags; - int32_t minimum; - int32_t maximum; - uint32_t bucket_count; - PersistentMemoryAllocator::Reference ranges_ref; - uint32_t ranges_checksum; - subtle::Atomic32 counts_ref; // PersistentMemoryAllocator::Reference - HistogramSamples::Metadata samples_metadata; - HistogramSamples::Metadata logged_metadata; - - // Space for the histogram name will be added during the actual allocation - // request. This must be the last field of the structure. A zero-size array - // or a "flexible" array would be preferred but is not (yet) valid C++. - char name[sizeof(uint64_t)]; // Force 64-bit alignment on 32-bit builds. -}; - -PersistentHistogramAllocator::Iterator::Iterator( - PersistentHistogramAllocator* allocator) - : allocator_(allocator), memory_iter_(allocator->memory_allocator()) {} - -std::unique_ptr -PersistentHistogramAllocator::Iterator::GetNextWithIgnore(Reference ignore) { - PersistentMemoryAllocator::Reference ref; - while ((ref = memory_iter_.GetNextOfType()) != 0) { - if (ref != ignore) - return allocator_->GetHistogram(ref); - } - return nullptr; -} - - -PersistentHistogramAllocator::PersistentHistogramAllocator( - std::unique_ptr memory) - : memory_allocator_(std::move(memory)), - sparse_histogram_data_manager_(memory_allocator_.get()) {} - -PersistentHistogramAllocator::~PersistentHistogramAllocator() = default; - -std::unique_ptr PersistentHistogramAllocator::GetHistogram( - Reference ref) { - // Unfortunately, the histogram "pickle" methods cannot be used as part of - // the persistance because the deserialization methods always create local - // count data (while these must reference the persistent counts) and always - // add it to the local list of known histograms (while these may be simple - // references to histograms in other processes). - PersistentHistogramData* data = - memory_allocator_->GetAsObject(ref); - const size_t length = memory_allocator_->GetAllocSize(ref); - - // Check that metadata is reasonable: name is null-terminated and non-empty, - // ID fields have been loaded with a hash of the name (0 is considered - // unset/invalid). - if (!data || data->name[0] == '\0' || - reinterpret_cast(data)[length - 1] != '\0' || - data->samples_metadata.id == 0 || data->logged_metadata.id == 0 || - // Note: Sparse histograms use |id + 1| in |logged_metadata|. - (data->logged_metadata.id != data->samples_metadata.id && - data->logged_metadata.id != data->samples_metadata.id + 1) || - // Most non-matching values happen due to truncated names. Ideally, we - // could just verify the name length based on the overall alloc length, - // but that doesn't work because the allocated block may have been - // aligned to the next boundary value. - HashMetricName(data->name) != data->samples_metadata.id) { - return nullptr; - } - return CreateHistogram(data); -} - -std::unique_ptr PersistentHistogramAllocator::AllocateHistogram( - HistogramType histogram_type, - const std::string& name, - int minimum, - int maximum, - const BucketRanges* bucket_ranges, - int32_t flags, - Reference* ref_ptr) { - // If the allocator is corrupt, don't waste time trying anything else. - // This also allows differentiating on the dashboard between allocations - // failed due to a corrupt allocator and the number of process instances - // with one, the latter being idicated by "newly corrupt", below. - if (memory_allocator_->IsCorrupt()) - return nullptr; - - // Create the metadata necessary for a persistent sparse histogram. This - // is done first because it is a small subset of what is required for - // other histograms. The type is "under construction" so that a crash - // during the datafill doesn't leave a bad record around that could cause - // confusion by another process trying to read it. It will be corrected - // once histogram construction is complete. - PersistentHistogramData* histogram_data = - memory_allocator_->New( - offsetof(PersistentHistogramData, name) + name.length() + 1); - if (histogram_data) { - memcpy(histogram_data->name, name.c_str(), name.size() + 1); - histogram_data->histogram_type = histogram_type; - histogram_data->flags = flags | HistogramBase::kIsPersistent; - } - - // Create the remaining metadata necessary for regular histograms. - if (histogram_type != SPARSE_HISTOGRAM) { - size_t bucket_count = bucket_ranges->bucket_count(); - size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count); - if (counts_bytes == 0) { - // |bucket_count| was out-of-range. - return nullptr; - } - - // Since the StasticsRecorder keeps a global collection of BucketRanges - // objects for re-use, it would be dangerous for one to hold a reference - // from a persistent allocator that is not the global one (which is - // permanent once set). If this stops being the case, this check can - // become an "if" condition beside "!ranges_ref" below and before - // set_persistent_reference() farther down. - DCHECK_EQ(this, GlobalHistogramAllocator::Get()); - - // Re-use an existing BucketRanges persistent allocation if one is known; - // otherwise, create one. - PersistentMemoryAllocator::Reference ranges_ref = - bucket_ranges->persistent_reference(); - if (!ranges_ref) { - size_t ranges_count = bucket_count + 1; - size_t ranges_bytes = ranges_count * sizeof(HistogramBase::Sample); - ranges_ref = - memory_allocator_->Allocate(ranges_bytes, kTypeIdRangesArray); - if (ranges_ref) { - HistogramBase::Sample* ranges_data = - memory_allocator_->GetAsArray( - ranges_ref, kTypeIdRangesArray, ranges_count); - if (ranges_data) { - for (size_t i = 0; i < bucket_ranges->size(); ++i) - ranges_data[i] = bucket_ranges->range(i); - bucket_ranges->set_persistent_reference(ranges_ref); - } else { - // This should never happen but be tolerant if it does. - ranges_ref = PersistentMemoryAllocator::kReferenceNull; - } - } - } else { - DCHECK_EQ(kTypeIdRangesArray, memory_allocator_->GetType(ranges_ref)); - } - - - // Only continue here if all allocations were successful. If they weren't, - // there is no way to free the space but that's not really a problem since - // the allocations only fail because the space is full or corrupt and so - // any future attempts will also fail. - if (ranges_ref && histogram_data) { - histogram_data->minimum = minimum; - histogram_data->maximum = maximum; - // |bucket_count| must fit within 32-bits or the allocation of the counts - // array would have failed for being too large; the allocator supports - // less than 4GB total size. - histogram_data->bucket_count = static_cast(bucket_count); - histogram_data->ranges_ref = ranges_ref; - histogram_data->ranges_checksum = bucket_ranges->checksum(); - } else { - histogram_data = nullptr; // Clear this for proper handling below. - } - } - - if (histogram_data) { - // Create the histogram using resources in persistent memory. This ends up - // resolving the "ref" values stored in histogram_data instad of just - // using what is already known above but avoids duplicating the switch - // statement here and serves as a double-check that everything is - // correct before commiting the new histogram to persistent space. - std::unique_ptr histogram = CreateHistogram(histogram_data); - DCHECK(histogram); - DCHECK_NE(0U, histogram_data->samples_metadata.id); - DCHECK_NE(0U, histogram_data->logged_metadata.id); - - PersistentMemoryAllocator::Reference histogram_ref = - memory_allocator_->GetAsReference(histogram_data); - if (ref_ptr != nullptr) - *ref_ptr = histogram_ref; - - // By storing the reference within the allocator to this histogram, the - // next import (which will happen before the next histogram creation) - // will know to skip it. - // See also the comment in ImportHistogramsToStatisticsRecorder(). - subtle::NoBarrier_Store(&last_created_, histogram_ref); - return histogram; - } - - return nullptr; -} - -void PersistentHistogramAllocator::FinalizeHistogram(Reference ref, - bool registered) { - if (registered) { - // If the created persistent histogram was registered then it needs to - // be marked as "iterable" in order to be found by other processes. This - // happens only after the histogram is fully formed so it's impossible for - // code iterating through the allocator to read a partially created record. - memory_allocator_->MakeIterable(ref); - } else { - // If it wasn't registered then a race condition must have caused two to - // be created. The allocator does not support releasing the acquired memory - // so just change the type to be empty. - memory_allocator_->ChangeType(ref, 0, - PersistentHistogramData::kPersistentTypeId, - /*clear=*/false); - } -} - -void PersistentHistogramAllocator::MergeHistogramDeltaToStatisticsRecorder( - HistogramBase* histogram) { - DCHECK(histogram); - - HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram); - if (!existing) { - // The above should never fail but if it does, no real harm is done. - // The data won't be merged but it also won't be recorded as merged - // so a future try, if successful, will get what was missed. If it - // continues to fail, some metric data will be lost but that is better - // than crashing. - return; - } - - // Merge the delta from the passed object to the one in the SR. - existing->AddSamples(*histogram->SnapshotDelta()); -} - -void PersistentHistogramAllocator::MergeHistogramFinalDeltaToStatisticsRecorder( - const HistogramBase* histogram) { - DCHECK(histogram); - - HistogramBase* existing = GetOrCreateStatisticsRecorderHistogram(histogram); - if (!existing) { - // The above should never fail but if it does, no real harm is done. - // Some metric data will be lost but that is better than crashing. - return; - } - - // Merge the delta from the passed object to the one in the SR. - existing->AddSamples(*histogram->SnapshotFinalDelta()); -} - -PersistentSampleMapRecords* PersistentHistogramAllocator::UseSampleMapRecords( - uint64_t id, - const void* user) { - return sparse_histogram_data_manager_.UseSampleMapRecords(id, user); -} - -void PersistentHistogramAllocator::CreateTrackingHistograms(StringPiece name) { - memory_allocator_->CreateTrackingHistograms(name); -} - -void PersistentHistogramAllocator::UpdateTrackingHistograms() { - memory_allocator_->UpdateTrackingHistograms(); -} - -void PersistentHistogramAllocator::ClearLastCreatedReferenceForTesting() { - subtle::NoBarrier_Store(&last_created_, 0); -} - -std::unique_ptr PersistentHistogramAllocator::CreateHistogram( - PersistentHistogramData* histogram_data_ptr) { - if (!histogram_data_ptr) - return nullptr; - - // Sparse histograms are quite different so handle them as a special case. - if (histogram_data_ptr->histogram_type == SPARSE_HISTOGRAM) { - std::unique_ptr histogram = - SparseHistogram::PersistentCreate(this, histogram_data_ptr->name, - &histogram_data_ptr->samples_metadata, - &histogram_data_ptr->logged_metadata); - DCHECK(histogram); - histogram->SetFlags(histogram_data_ptr->flags); - return histogram; - } - - // Copy the configuration fields from histogram_data_ptr to local storage - // because anything in persistent memory cannot be trusted as it could be - // changed at any moment by a malicious actor that shares access. The local - // values are validated below and then used to create the histogram, knowing - // they haven't changed between validation and use. - int32_t histogram_type = histogram_data_ptr->histogram_type; - int32_t histogram_flags = histogram_data_ptr->flags; - int32_t histogram_minimum = histogram_data_ptr->minimum; - int32_t histogram_maximum = histogram_data_ptr->maximum; - uint32_t histogram_bucket_count = histogram_data_ptr->bucket_count; - uint32_t histogram_ranges_ref = histogram_data_ptr->ranges_ref; - uint32_t histogram_ranges_checksum = histogram_data_ptr->ranges_checksum; - - HistogramBase::Sample* ranges_data = - memory_allocator_->GetAsArray( - histogram_ranges_ref, kTypeIdRangesArray, - PersistentMemoryAllocator::kSizeAny); - - const uint32_t max_buckets = - std::numeric_limits::max() / sizeof(HistogramBase::Sample); - size_t required_bytes = - (histogram_bucket_count + 1) * sizeof(HistogramBase::Sample); - size_t allocated_bytes = - memory_allocator_->GetAllocSize(histogram_ranges_ref); - if (!ranges_data || histogram_bucket_count < 2 || - histogram_bucket_count >= max_buckets || - allocated_bytes < required_bytes) { - return nullptr; - } - - std::unique_ptr created_ranges = CreateRangesFromData( - ranges_data, histogram_ranges_checksum, histogram_bucket_count + 1); - if (!created_ranges) - return nullptr; - const BucketRanges* ranges = - StatisticsRecorder::RegisterOrDeleteDuplicateRanges( - created_ranges.release()); - - size_t counts_bytes = CalculateRequiredCountsBytes(histogram_bucket_count); - PersistentMemoryAllocator::Reference counts_ref = - subtle::Acquire_Load(&histogram_data_ptr->counts_ref); - if (counts_bytes == 0 || - (counts_ref != 0 && - memory_allocator_->GetAllocSize(counts_ref) < counts_bytes)) { - return nullptr; - } - - // The "counts" data (including both samples and logged samples) is a delayed - // persistent allocation meaning that though its size and storage for a - // reference is defined, no space is reserved until actually needed. When - // it is needed, memory will be allocated from the persistent segment and - // a reference to it stored at the passed address. Other threads can then - // notice the valid reference and access the same data. - DelayedPersistentAllocation counts_data(memory_allocator_.get(), - &histogram_data_ptr->counts_ref, - kTypeIdCountsArray, counts_bytes, 0); - - // A second delayed allocations is defined using the same reference storage - // location as the first so the allocation of one will automatically be found - // by the other. Within the block, the first half of the space is for "counts" - // and the second half is for "logged counts". - DelayedPersistentAllocation logged_data( - memory_allocator_.get(), &histogram_data_ptr->counts_ref, - kTypeIdCountsArray, counts_bytes, counts_bytes / 2, - /*make_iterable=*/false); - - // Create the right type of histogram. - const char* name = histogram_data_ptr->name; - std::unique_ptr histogram; - switch (histogram_type) { - case HISTOGRAM: - histogram = Histogram::PersistentCreate( - name, histogram_minimum, histogram_maximum, ranges, counts_data, - logged_data, &histogram_data_ptr->samples_metadata, - &histogram_data_ptr->logged_metadata); - DCHECK(histogram); - break; - case LINEAR_HISTOGRAM: - histogram = LinearHistogram::PersistentCreate( - name, histogram_minimum, histogram_maximum, ranges, counts_data, - logged_data, &histogram_data_ptr->samples_metadata, - &histogram_data_ptr->logged_metadata); - DCHECK(histogram); - break; - case BOOLEAN_HISTOGRAM: - histogram = BooleanHistogram::PersistentCreate( - name, ranges, counts_data, logged_data, - &histogram_data_ptr->samples_metadata, - &histogram_data_ptr->logged_metadata); - DCHECK(histogram); - break; - case CUSTOM_HISTOGRAM: - histogram = CustomHistogram::PersistentCreate( - name, ranges, counts_data, logged_data, - &histogram_data_ptr->samples_metadata, - &histogram_data_ptr->logged_metadata); - DCHECK(histogram); - break; - default: - return nullptr; - } - - if (histogram) { - DCHECK_EQ(histogram_type, histogram->GetHistogramType()); - histogram->SetFlags(histogram_flags); - } - - return histogram; -} - -HistogramBase* -PersistentHistogramAllocator::GetOrCreateStatisticsRecorderHistogram( - const HistogramBase* histogram) { - // This should never be called on the global histogram allocator as objects - // created there are already within the global statistics recorder. - DCHECK_NE(GlobalHistogramAllocator::Get(), this); - DCHECK(histogram); - - HistogramBase* existing = - StatisticsRecorder::FindHistogram(histogram->histogram_name()); - if (existing) - return existing; - - // Adding the passed histogram to the SR would cause a problem if the - // allocator that holds it eventually goes away. Instead, create a new - // one from a serialized version. Deserialization calls the appropriate - // FactoryGet() which will create the histogram in the global persistent- - // histogram allocator if such is set. - base::Pickle pickle; - histogram->SerializeInfo(&pickle); - PickleIterator iter(pickle); - existing = DeserializeHistogramInfo(&iter); - if (!existing) - return nullptr; - - // Make sure there is no "serialization" flag set. - DCHECK_EQ(0, existing->flags() & HistogramBase::kIPCSerializationSourceFlag); - // Record the newly created histogram in the SR. - return StatisticsRecorder::RegisterOrDeleteDuplicate(existing); -} - -GlobalHistogramAllocator::~GlobalHistogramAllocator() = default; - -// static -void GlobalHistogramAllocator::CreateWithPersistentMemory( - void* base, - size_t size, - size_t page_size, - uint64_t id, - StringPiece name) { - Set(WrapUnique( - new GlobalHistogramAllocator(std::make_unique( - base, size, page_size, id, name, false)))); -} - -// static -void GlobalHistogramAllocator::CreateWithLocalMemory( - size_t size, - uint64_t id, - StringPiece name) { - Set(WrapUnique(new GlobalHistogramAllocator( - std::make_unique(size, id, name)))); -} - -#if !defined(OS_NACL) -// static -bool GlobalHistogramAllocator::CreateWithFile( - const FilePath& file_path, - size_t size, - uint64_t id, - StringPiece name) { - bool exists = PathExists(file_path); - File file( - file_path, File::FLAG_OPEN_ALWAYS | File::FLAG_SHARE_DELETE | - File::FLAG_READ | File::FLAG_WRITE); - - std::unique_ptr mmfile(new MemoryMappedFile()); - if (exists) { - size = saturated_cast(file.GetLength()); - mmfile->Initialize(std::move(file), MemoryMappedFile::READ_WRITE); - } else { - mmfile->Initialize(std::move(file), {0, size}, - MemoryMappedFile::READ_WRITE_EXTEND); - } - if (!mmfile->IsValid() || - !FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)) { - return false; - } - - Set(WrapUnique(new GlobalHistogramAllocator( - std::make_unique(std::move(mmfile), size, - id, name, false)))); - Get()->SetPersistentLocation(file_path); - return true; -} - -// static -bool GlobalHistogramAllocator::CreateWithActiveFile(const FilePath& base_path, - const FilePath& active_path, - const FilePath& spare_path, - size_t size, - uint64_t id, - StringPiece name) { - // Old "active" becomes "base". - if (!base::ReplaceFile(active_path, base_path, nullptr)) - base::DeleteFile(base_path, /*recursive=*/false); - DCHECK(!base::PathExists(active_path)); - - // Move any "spare" into "active". Okay to continue if file doesn't exist. - if (!spare_path.empty()) { - base::ReplaceFile(spare_path, active_path, nullptr); - DCHECK(!base::PathExists(spare_path)); - } - - return base::GlobalHistogramAllocator::CreateWithFile(active_path, size, id, - name); -} - -// static -bool GlobalHistogramAllocator::CreateWithActiveFileInDir(const FilePath& dir, - size_t size, - uint64_t id, - StringPiece name) { - FilePath base_path, active_path, spare_path; - ConstructFilePaths(dir, name, &base_path, &active_path, &spare_path); - return CreateWithActiveFile(base_path, active_path, spare_path, size, id, - name); -} - -// static -FilePath GlobalHistogramAllocator::ConstructFilePath(const FilePath& dir, - StringPiece name) { - return dir.AppendASCII(name).AddExtension( - PersistentMemoryAllocator::kFileExtension); -} - -// static -FilePath GlobalHistogramAllocator::ConstructFilePathForUploadDir( - const FilePath& dir, - StringPiece name, - base::Time stamp, - ProcessId pid) { - return ConstructFilePath( - dir, - StringPrintf("%.*s-%lX-%lX", static_cast(name.length()), name.data(), - static_cast(stamp.ToTimeT()), static_cast(pid))); -} - -// static -bool GlobalHistogramAllocator::ParseFilePath(const FilePath& path, - std::string* out_name, - Time* out_stamp, - ProcessId* out_pid) { - std::string filename = path.BaseName().AsUTF8Unsafe(); - std::vector parts = base::SplitStringPiece( - filename, "-.", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); - if (parts.size() != 4) - return false; - - if (out_name) - *out_name = parts[0].as_string(); - - if (out_stamp) { - int64_t stamp; - if (!HexStringToInt64(parts[1], &stamp)) - return false; - *out_stamp = Time::FromTimeT(static_cast(stamp)); - } - - if (out_pid) { - int64_t pid; - if (!HexStringToInt64(parts[2], &pid)) - return false; - *out_pid = static_cast(pid); - } - - return true; -} - -// static -void GlobalHistogramAllocator::ConstructFilePaths(const FilePath& dir, - StringPiece name, - FilePath* out_base_path, - FilePath* out_active_path, - FilePath* out_spare_path) { - if (out_base_path) - *out_base_path = ConstructFilePath(dir, name); - - if (out_active_path) { - *out_active_path = - ConstructFilePath(dir, name.as_string().append("-active")); - } - - if (out_spare_path) { - *out_spare_path = ConstructFilePath(dir, name.as_string().append("-spare")); - } -} - -// static -void GlobalHistogramAllocator::ConstructFilePathsForUploadDir( - const FilePath& active_dir, - const FilePath& upload_dir, - const std::string& name, - FilePath* out_upload_path, - FilePath* out_active_path, - FilePath* out_spare_path) { - if (out_upload_path) { - *out_upload_path = ConstructFilePathForUploadDir( - upload_dir, name, Time::Now(), GetCurrentProcId()); - } - - if (out_active_path) { - *out_active_path = - ConstructFilePath(active_dir, name + std::string("-active")); - } - - if (out_spare_path) { - *out_spare_path = - ConstructFilePath(active_dir, name + std::string("-spare")); - } -} - -// static -bool GlobalHistogramAllocator::CreateSpareFile(const FilePath& spare_path, - size_t size) { - FilePath temp_spare_path = spare_path.AddExtension(FILE_PATH_LITERAL(".tmp")); - bool success = true; - { - File spare_file(temp_spare_path, File::FLAG_CREATE_ALWAYS | - File::FLAG_READ | File::FLAG_WRITE); - if (!spare_file.IsValid()) - return false; - - MemoryMappedFile mmfile; - mmfile.Initialize(std::move(spare_file), {0, size}, - MemoryMappedFile::READ_WRITE_EXTEND); - success = mmfile.IsValid(); - } - - if (success) - success = ReplaceFile(temp_spare_path, spare_path, nullptr); - - if (!success) - DeleteFile(temp_spare_path, /*recursive=*/false); - - return success; -} - -// static -bool GlobalHistogramAllocator::CreateSpareFileInDir(const FilePath& dir, - size_t size, - StringPiece name) { - FilePath spare_path; - ConstructFilePaths(dir, name, nullptr, nullptr, &spare_path); - return CreateSpareFile(spare_path, size); -} -#endif // !defined(OS_NACL) - -// static -void GlobalHistogramAllocator::CreateWithSharedMemoryHandle( - const SharedMemoryHandle& handle, - size_t size) { - std::unique_ptr shm( - new SharedMemory(handle, /*readonly=*/false)); - if (!shm->Map(size) || - !SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable(*shm)) { - return; - } - - Set(WrapUnique(new GlobalHistogramAllocator( - std::make_unique( - std::move(shm), 0, StringPiece(), /*readonly=*/false)))); -} - -// static -void GlobalHistogramAllocator::Set( - std::unique_ptr allocator) { - // Releasing or changing an allocator is extremely dangerous because it - // likely has histograms stored within it. If the backing memory is also - // also released, future accesses to those histograms will seg-fault. - CHECK(!subtle::NoBarrier_Load(&g_histogram_allocator)); - subtle::Release_Store(&g_histogram_allocator, - reinterpret_cast(allocator.release())); - size_t existing = StatisticsRecorder::GetHistogramCount(); - - DVLOG_IF(1, existing) - << existing << " histograms were created before persistence was enabled."; -} - -// static -GlobalHistogramAllocator* GlobalHistogramAllocator::Get() { - return reinterpret_cast( - subtle::Acquire_Load(&g_histogram_allocator)); -} - -// static -std::unique_ptr -GlobalHistogramAllocator::ReleaseForTesting() { - GlobalHistogramAllocator* histogram_allocator = Get(); - if (!histogram_allocator) - return nullptr; - PersistentMemoryAllocator* memory_allocator = - histogram_allocator->memory_allocator(); - - // Before releasing the memory, it's necessary to have the Statistics- - // Recorder forget about the histograms contained therein; otherwise, - // some operations will try to access them and the released memory. - PersistentMemoryAllocator::Iterator iter(memory_allocator); - const PersistentHistogramData* data; - while ((data = iter.GetNextOfObject()) != nullptr) { - StatisticsRecorder::ForgetHistogramForTesting(data->name); - } - - subtle::Release_Store(&g_histogram_allocator, 0); - return WrapUnique(histogram_allocator); -}; - -void GlobalHistogramAllocator::SetPersistentLocation(const FilePath& location) { - persistent_location_ = location; -} - -const FilePath& GlobalHistogramAllocator::GetPersistentLocation() const { - return persistent_location_; -} - -bool GlobalHistogramAllocator::WriteToPersistentLocation() { -#if defined(OS_NACL) - // NACL doesn't support file operations, including ImportantFileWriter. - NOTREACHED(); - return false; -#else - // Stop if no destination is set. - if (persistent_location_.empty()) { - NOTREACHED() << "Could not write \"" << Name() << "\" persistent histograms" - << " to file because no location was set."; - return false; - } - - StringPiece contents(static_cast(data()), used()); - if (!ImportantFileWriter::WriteFileAtomically(persistent_location_, - contents)) { - LOG(ERROR) << "Could not write \"" << Name() << "\" persistent histograms" - << " to file: " << persistent_location_.value(); - return false; - } - - return true; -#endif -} - -void GlobalHistogramAllocator::DeletePersistentLocation() { - memory_allocator()->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED); - -#if defined(OS_NACL) - NOTREACHED(); -#else - if (persistent_location_.empty()) - return; - - // Open (with delete) and then immediately close the file by going out of - // scope. This is the only cross-platform safe way to delete a file that may - // be open elsewhere. Open handles will continue to operate normally but - // new opens will not be possible. - File file(persistent_location_, - File::FLAG_OPEN | File::FLAG_READ | File::FLAG_DELETE_ON_CLOSE); -#endif -} - -GlobalHistogramAllocator::GlobalHistogramAllocator( - std::unique_ptr memory) - : PersistentHistogramAllocator(std::move(memory)), - import_iterator_(this) { -} - -void GlobalHistogramAllocator::ImportHistogramsToStatisticsRecorder() { - // Skip the import if it's the histogram that was last created. Should a - // race condition cause the "last created" to be overwritten before it - // is recognized here then the histogram will be created and be ignored - // when it is detected as a duplicate by the statistics-recorder. This - // simple check reduces the time of creating persistent histograms by - // about 40%. - Reference record_to_ignore = last_created(); - - // There is no lock on this because the iterator is lock-free while still - // guaranteed to only return each entry only once. The StatisticsRecorder - // has its own lock so the Register operation is safe. - while (true) { - std::unique_ptr histogram = - import_iterator_.GetNextWithIgnore(record_to_ignore); - if (!histogram) - break; - StatisticsRecorder::RegisterOrDeleteDuplicate(histogram.release()); - } -} - -} // namespace base diff --git a/metrics/persistent_histogram_allocator.h b/metrics/persistent_histogram_allocator.h deleted file mode 100644 index 395511fb7..000000000 --- a/metrics/persistent_histogram_allocator.h +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_METRICS_HISTOGRAM_PERSISTENCE_H_ -#define BASE_METRICS_HISTOGRAM_PERSISTENCE_H_ - -#include -#include - -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/feature_list.h" -#include "base/memory/shared_memory.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/strings/string_piece.h" -#include "base/synchronization/lock.h" - -namespace base { - -class BucketRanges; -class FilePath; -class PersistentSampleMapRecords; -class PersistentSparseHistogramDataManager; - -// Feature definition for enabling histogram persistence. -BASE_EXPORT extern const Feature kPersistentHistogramsFeature; - - -// A data manager for sparse histograms so each instance of such doesn't have -// to separately iterate over the entire memory segment. Though this class -// will generally be accessed through the PersistentHistogramAllocator above, -// it can be used independently on any PersistentMemoryAllocator (making it -// useable for testing). This object supports only one instance of a sparse -// histogram for a given id. Tests that create multiple identical histograms, -// perhaps to simulate multiple processes, should create a separate manager -// for each. -class BASE_EXPORT PersistentSparseHistogramDataManager { - public: - // Constructs the data manager. The allocator must live longer than any - // managers that reference it. - explicit PersistentSparseHistogramDataManager( - PersistentMemoryAllocator* allocator); - - ~PersistentSparseHistogramDataManager(); - - // Returns the object that manages the persistent-sample-map records for a - // given |id|. Only one |user| of this data is allowed at a time. This does - // an automatic Acquire() on the records. The user must call Release() on - // the returned object when it is finished with it. Ownership of the records - // object stays with this manager. - PersistentSampleMapRecords* UseSampleMapRecords(uint64_t id, - const void* user); - - // Convenience method that gets the object for a given reference so callers - // don't have to also keep their own pointer to the appropriate allocator. - template - T* GetAsObject(PersistentMemoryAllocator::Reference ref) { - return allocator_->GetAsObject(ref); - } - - private: - friend class PersistentSampleMapRecords; - - // Gets the object holding records for a given sample-map id when |lock_| - // has already been acquired. - PersistentSampleMapRecords* GetSampleMapRecordsWhileLocked(uint64_t id); - - // Loads sample-map records looking for those belonging to the specified - // |load_id|. Records found for other sample-maps are held for later use - // without having to iterate again. This should be called only from a - // PersistentSampleMapRecords object because those objects have a contract - // that there are no other threads accessing the internal records_ field - // of the object that is passed in. - bool LoadRecords(PersistentSampleMapRecords* sample_map_records); - - // Weak-pointer to the allocator used by the sparse histograms. - PersistentMemoryAllocator* allocator_; - - // Iterator within the allocator for finding sample records. - PersistentMemoryAllocator::Iterator record_iterator_; - - // Mapping of sample-map IDs to their sample records. - std::map> - sample_records_; - - // A lock used for synchronizing changes to sample_records_. - base::Lock lock_; - - DISALLOW_COPY_AND_ASSIGN(PersistentSparseHistogramDataManager); -}; - - -// This class manages sample-records used by a PersistentSampleMap container -// that underlies a persistent SparseHistogram object. It is broken out into a -// top-level class so that it can be forward-declared in other header files -// rather than include this entire file as would be necessary if it were -// declared within the PersistentSparseHistogramDataManager class above. -class BASE_EXPORT PersistentSampleMapRecords { - public: - // Constructs an instance of this class. The manager object must live longer - // than all instances of this class that reference it, which is not usually - // a problem since these objects are generally managed from within that - // manager instance. - PersistentSampleMapRecords(PersistentSparseHistogramDataManager* data_manager, - uint64_t sample_map_id); - - ~PersistentSampleMapRecords(); - - // Resets the internal state for a new object using this data. The return - // value is "this" as a convenience. - PersistentSampleMapRecords* Acquire(const void* user); - - // Indicates that the using object is done with this data. - void Release(const void* user); - - // Gets the next reference to a persistent sample-map record. The type and - // layout of the data being referenced is defined entirely within the - // PersistentSampleMap class. - PersistentMemoryAllocator::Reference GetNext(); - - // Creates a new persistent sample-map record for sample |value| and returns - // a reference to it. - PersistentMemoryAllocator::Reference CreateNew(HistogramBase::Sample value); - - // Convenience method that gets the object for a given reference so callers - // don't have to also keep their own pointer to the appropriate allocator. - // This is expected to be used with the SampleRecord structure defined inside - // the persistent_sample_map.cc file but since that isn't exported (for - // cleanliness of the interface), a template is defined that will be - // resolved when used inside that file. - template - T* GetAsObject(PersistentMemoryAllocator::Reference ref) { - return data_manager_->GetAsObject(ref); - } - - private: - friend PersistentSparseHistogramDataManager; - - // Weak-pointer to the parent data-manager object. - PersistentSparseHistogramDataManager* data_manager_; - - // ID of PersistentSampleMap to which these records apply. - const uint64_t sample_map_id_; - - // The current user of this set of records. It is used to ensure that no - // more than one object is using these records at a given time. - const void* user_ = nullptr; - - // This is the count of how many "records" have already been read by the - // owning sample-map. - size_t seen_ = 0; - - // This is the set of records previously found for a sample map. Because - // there is ever only one object with a given ID (typically a hash of a - // histogram name) and because the parent SparseHistogram has acquired - // its own lock before accessing the PersistentSampleMap it controls, this - // list can be accessed without acquiring any additional lock. - std::vector records_; - - // This is the set of records found during iteration through memory. It - // is appended in bulk to "records". Access to this vector can be done - // only while holding the parent manager's lock. - std::vector found_; - - DISALLOW_COPY_AND_ASSIGN(PersistentSampleMapRecords); -}; - - -// This class manages histograms created within a PersistentMemoryAllocator. -class BASE_EXPORT PersistentHistogramAllocator { - public: - // A reference to a histogram. While this is implemented as PMA::Reference, - // it is not conceptually the same thing. Outside callers should always use - // a Reference matching the class it is for and not mix the two. - using Reference = PersistentMemoryAllocator::Reference; - - // Iterator used for fetching persistent histograms from an allocator. - // It is lock-free and thread-safe. - // See PersistentMemoryAllocator::Iterator for more information. - class BASE_EXPORT Iterator { - public: - // Constructs an iterator on a given |allocator|, starting at the beginning. - // The allocator must live beyond the lifetime of the iterator. - explicit Iterator(PersistentHistogramAllocator* allocator); - - // Gets the next histogram from persistent memory; returns null if there - // are no more histograms to be found. This may still be called again - // later to retrieve any new histograms added in the meantime. - std::unique_ptr GetNext() { return GetNextWithIgnore(0); } - - // Gets the next histogram from persistent memory, ignoring one particular - // reference in the process. Pass |ignore| of zero (0) to ignore nothing. - std::unique_ptr GetNextWithIgnore(Reference ignore); - - private: - // Weak-pointer to histogram allocator being iterated over. - PersistentHistogramAllocator* allocator_; - - // The iterator used for stepping through objects in persistent memory. - // It is lock-free and thread-safe which is why this class is also such. - PersistentMemoryAllocator::Iterator memory_iter_; - - DISALLOW_COPY_AND_ASSIGN(Iterator); - }; - - // A PersistentHistogramAllocator is constructed from a PersistentMemory- - // Allocator object of which it takes ownership. - explicit PersistentHistogramAllocator( - std::unique_ptr memory); - virtual ~PersistentHistogramAllocator(); - - // Direct access to underlying memory allocator. If the segment is shared - // across threads or processes, reading data through these values does - // not guarantee consistency. Use with care. Do not write. - PersistentMemoryAllocator* memory_allocator() { - return memory_allocator_.get(); - } - - // Implement the "metadata" API of a PersistentMemoryAllocator, forwarding - // those requests to the real one. - uint64_t Id() const { return memory_allocator_->Id(); } - const char* Name() const { return memory_allocator_->Name(); } - const void* data() const { return memory_allocator_->data(); } - size_t length() const { return memory_allocator_->length(); } - size_t size() const { return memory_allocator_->size(); } - size_t used() const { return memory_allocator_->used(); } - - // Recreate a Histogram from data held in persistent memory. Though this - // object will be local to the current process, the sample data will be - // shared with all other threads referencing it. This method takes a |ref| - // to where the top-level histogram data may be found in this allocator. - // This method will return null if any problem is detected with the data. - std::unique_ptr GetHistogram(Reference ref); - - // Allocate a new persistent histogram. The returned histogram will not - // be able to be located by other allocators until it is "finalized". - std::unique_ptr AllocateHistogram( - HistogramType histogram_type, - const std::string& name, - int minimum, - int maximum, - const BucketRanges* bucket_ranges, - int32_t flags, - Reference* ref_ptr); - - // Finalize the creation of the histogram, making it available to other - // processes if |registered| (as in: added to the StatisticsRecorder) is - // True, forgetting it otherwise. - void FinalizeHistogram(Reference ref, bool registered); - - // Merges the data in a persistent histogram with one held globally by the - // StatisticsRecorder, updating the "logged" samples within the passed - // object so that repeated merges are allowed. Don't call this on a "global" - // allocator because histograms created there will already be in the SR. - void MergeHistogramDeltaToStatisticsRecorder(HistogramBase* histogram); - - // As above but merge the "final" delta. No update of "logged" samples is - // done which means it can operate on read-only objects. It's essential, - // however, not to call this more than once or those final samples will - // get recorded again. - void MergeHistogramFinalDeltaToStatisticsRecorder( - const HistogramBase* histogram); - - // Returns the object that manages the persistent-sample-map records for a - // given |id|. Only one |user| of this data is allowed at a time. This does - // an automatic Acquire() on the records. The user must call Release() on - // the returned object when it is finished with it. Ownership stays with - // this allocator. - PersistentSampleMapRecords* UseSampleMapRecords(uint64_t id, - const void* user); - - // Create internal histograms for tracking memory use and allocation sizes - // for allocator of |name| (which can simply be the result of Name()). This - // is done seperately from construction for situations such as when the - // histograms will be backed by memory provided by this very allocator. - // - // IMPORTANT: Callers must update tools/metrics/histograms/histograms.xml - // with the following histograms: - // UMA.PersistentAllocator.name.Allocs - // UMA.PersistentAllocator.name.UsedPct - void CreateTrackingHistograms(StringPiece name); - void UpdateTrackingHistograms(); - - // Clears the internal |last_created_| reference so testing can validate - // operation without that optimization. - void ClearLastCreatedReferenceForTesting(); - - protected: - // The structure used to hold histogram data in persistent memory. It is - // defined and used entirely within the .cc file. - struct PersistentHistogramData; - - // Gets the reference of the last histogram created, used to avoid - // trying to import what was just created. - PersistentHistogramAllocator::Reference last_created() { - return subtle::NoBarrier_Load(&last_created_); - } - - // Gets the next histogram in persistent data based on iterator while - // ignoring a particular reference if it is found. - std::unique_ptr GetNextHistogramWithIgnore(Iterator* iter, - Reference ignore); - - private: - // Create a histogram based on saved (persistent) information about it. - std::unique_ptr CreateHistogram( - PersistentHistogramData* histogram_data_ptr); - - // Gets or creates an object in the global StatisticsRecorder matching - // the |histogram| passed. Null is returned if one was not found and - // one could not be created. - HistogramBase* GetOrCreateStatisticsRecorderHistogram( - const HistogramBase* histogram); - - // The memory allocator that provides the actual histogram storage. - std::unique_ptr memory_allocator_; - - // The data-manager used to improve performance of sparse histograms. - PersistentSparseHistogramDataManager sparse_histogram_data_manager_; - - // A reference to the last-created histogram in the allocator, used to avoid - // trying to import what was just created. - // TODO(bcwhite): Change this to std::atomic when available. - subtle::Atomic32 last_created_ = 0; - - DISALLOW_COPY_AND_ASSIGN(PersistentHistogramAllocator); -}; - - -// A special case of the PersistentHistogramAllocator that operates on a -// global scale, collecting histograms created through standard macros and -// the FactoryGet() method. -class BASE_EXPORT GlobalHistogramAllocator - : public PersistentHistogramAllocator { - public: - ~GlobalHistogramAllocator() override; - - // Create a global allocator using the passed-in memory |base|, |size|, and - // other parameters. Ownership of the memory segment remains with the caller. - static void CreateWithPersistentMemory(void* base, - size_t size, - size_t page_size, - uint64_t id, - StringPiece name); - - // Create a global allocator using an internal block of memory of the - // specified |size| taken from the heap. - static void CreateWithLocalMemory(size_t size, uint64_t id, StringPiece name); - -#if !defined(OS_NACL) - // Create a global allocator by memory-mapping a |file|. If the file does - // not exist, it will be created with the specified |size|. If the file does - // exist, the allocator will use and add to its contents, ignoring the passed - // size in favor of the existing size. Returns whether the global allocator - // was set. - static bool CreateWithFile(const FilePath& file_path, - size_t size, - uint64_t id, - StringPiece name); - - // Creates a new file at |active_path|. If it already exists, it will first be - // moved to |base_path|. In all cases, any old file at |base_path| will be - // removed. If |spare_path| is non-empty and exists, that will be renamed and - // used as the active file. Otherwise, the file will be created using the - // given size, id, and name. Returns whether the global allocator was set. - static bool CreateWithActiveFile(const FilePath& base_path, - const FilePath& active_path, - const FilePath& spare_path, - size_t size, - uint64_t id, - StringPiece name); - - // Uses ConstructBaseActivePairFilePaths() to build a pair of file names which - // are then used for CreateWithActiveFile(). |name| is used for both the - // internal name for the allocator and also for the name of the file inside - // |dir|. - static bool CreateWithActiveFileInDir(const FilePath& dir, - size_t size, - uint64_t id, - StringPiece name); - - // Constructs a filename using a name. - static FilePath ConstructFilePath(const FilePath& dir, StringPiece name); - - // Like above but with timestamp and pid for use in upload directories. - static FilePath ConstructFilePathForUploadDir(const FilePath& dir, - StringPiece name, - base::Time stamp, - ProcessId pid); - - // Parses a filename to extract name, timestamp, and pid. - static bool ParseFilePath(const FilePath& path, - std::string* out_name, - Time* out_stamp, - ProcessId* out_pid); - - // Constructs a set of names in |dir| based on name that can be used for a - // base + active persistent memory mapped location for CreateWithActiveFile(). - // The spare path is a file that can be pre-created and moved to be active - // without any startup penalty that comes from constructing the file. |name| - // will be used as the basename of the file inside |dir|. |out_base_path|, - // |out_active_path|, or |out_spare_path| may be null if not needed. - static void ConstructFilePaths(const FilePath& dir, - StringPiece name, - FilePath* out_base_path, - FilePath* out_active_path, - FilePath* out_spare_path); - - // As above but puts the base files in a different "upload" directory. This - // is useful when moving all completed files into a single directory for easy - // upload management. - static void ConstructFilePathsForUploadDir(const FilePath& active_dir, - const FilePath& upload_dir, - const std::string& name, - FilePath* out_upload_path, - FilePath* out_active_path, - FilePath* out_spare_path); - - // Create a "spare" file that can later be made the "active" file. This - // should be done on a background thread if possible. - static bool CreateSpareFile(const FilePath& spare_path, size_t size); - - // Same as above but uses standard names. |name| is the name of the allocator - // and is also used to create the correct filename. - static bool CreateSpareFileInDir(const FilePath& dir_path, - size_t size, - StringPiece name); -#endif - - // Create a global allocator using a block of shared memory accessed - // through the given |handle| and |size|. The allocator takes ownership - // of the handle and closes it upon destruction, though the memory will - // continue to live if other processes have access to it. - static void CreateWithSharedMemoryHandle(const SharedMemoryHandle& handle, - size_t size); - - // Sets a GlobalHistogramAllocator for globally storing histograms in - // a space that can be persisted or shared between processes. There is only - // ever one allocator for all such histograms created by a single process. - // This takes ownership of the object and should be called as soon as - // possible during startup to capture as many histograms as possible and - // while operating single-threaded so there are no race-conditions. - static void Set(std::unique_ptr allocator); - - // Gets a pointer to the global histogram allocator. Returns null if none - // exists. - static GlobalHistogramAllocator* Get(); - - // This access to the persistent allocator is only for testing; it extracts - // the current allocator completely. This allows easy creation of histograms - // within persistent memory segments which can then be extracted and used in - // other ways. - static std::unique_ptr ReleaseForTesting(); - - // Stores a pathname to which the contents of this allocator should be saved - // in order to persist the data for a later use. - void SetPersistentLocation(const FilePath& location); - - // Retrieves a previously set pathname to which the contents of this allocator - // are to be saved. - const FilePath& GetPersistentLocation() const; - - // Writes the internal data to a previously set location. This is generally - // called when a process is exiting from a section of code that may not know - // the filesystem. The data is written in an atomic manner. The return value - // indicates success. - bool WriteToPersistentLocation(); - - // If there is a global metrics file being updated on disk, mark it to be - // deleted when the process exits. - void DeletePersistentLocation(); - - private: - friend class StatisticsRecorder; - - // Creates a new global histogram allocator. - explicit GlobalHistogramAllocator( - std::unique_ptr memory); - - // Import new histograms from the global histogram allocator. It's possible - // for other processes to create histograms in the active memory segment; - // this adds those to the internal list of known histograms to avoid creating - // duplicates that would have to be merged during reporting. Every call to - // this method resumes from the last entry it saw; it costs nothing if - // nothing new has been added. - void ImportHistogramsToStatisticsRecorder(); - - // Builds a FilePath for a metrics file. - static FilePath MakeMetricsFilePath(const FilePath& dir, StringPiece name); - - // Import always continues from where it left off, making use of a single - // iterator to continue the work. - Iterator import_iterator_; - - // The location to which the data should be persisted. - FilePath persistent_location_; - - DISALLOW_COPY_AND_ASSIGN(GlobalHistogramAllocator); -}; - -} // namespace base - -#endif // BASE_METRICS_HISTOGRAM_PERSISTENCE_H_ diff --git a/metrics/persistent_histogram_allocator_unittest.cc b/metrics/persistent_histogram_allocator_unittest.cc deleted file mode 100644 index 7e07386d1..000000000 --- a/metrics/persistent_histogram_allocator_unittest.cc +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright 2016 The Chromium 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/persistent_histogram_allocator.h" - -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/bucket_ranges.h" -#include "base/metrics/histogram_macros.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/metrics/statistics_recorder.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -class PersistentHistogramAllocatorTest : public testing::Test { - protected: - const int32_t kAllocatorMemorySize = 64 << 10; // 64 KiB - - PersistentHistogramAllocatorTest() - : statistics_recorder_(StatisticsRecorder::CreateTemporaryForTesting()) { - CreatePersistentHistogramAllocator(); - } - ~PersistentHistogramAllocatorTest() override { - DestroyPersistentHistogramAllocator(); - } - - void CreatePersistentHistogramAllocator() { - allocator_memory_.reset(new char[kAllocatorMemorySize]); - - GlobalHistogramAllocator::ReleaseForTesting(); - memset(allocator_memory_.get(), 0, kAllocatorMemorySize); - GlobalHistogramAllocator::CreateWithPersistentMemory( - allocator_memory_.get(), kAllocatorMemorySize, 0, 0, - "PersistentHistogramAllocatorTest"); - allocator_ = GlobalHistogramAllocator::Get()->memory_allocator(); - } - - void DestroyPersistentHistogramAllocator() { - allocator_ = nullptr; - GlobalHistogramAllocator::ReleaseForTesting(); - } - - std::unique_ptr statistics_recorder_; - std::unique_ptr allocator_memory_; - PersistentMemoryAllocator* allocator_ = nullptr; - - private: - DISALLOW_COPY_AND_ASSIGN(PersistentHistogramAllocatorTest); -}; - -TEST_F(PersistentHistogramAllocatorTest, CreateAndIterate) { - PersistentMemoryAllocator::MemoryInfo meminfo0; - allocator_->GetMemoryInfo(&meminfo0); - - // Try basic construction - HistogramBase* histogram = Histogram::FactoryGet( - "TestHistogram", 1, 1000, 10, HistogramBase::kIsPersistent); - EXPECT_TRUE(histogram); - histogram->CheckName("TestHistogram"); - PersistentMemoryAllocator::MemoryInfo meminfo1; - allocator_->GetMemoryInfo(&meminfo1); - EXPECT_GT(meminfo0.free, meminfo1.free); - - HistogramBase* linear_histogram = LinearHistogram::FactoryGet( - "TestLinearHistogram", 1, 1000, 10, HistogramBase::kIsPersistent); - EXPECT_TRUE(linear_histogram); - linear_histogram->CheckName("TestLinearHistogram"); - PersistentMemoryAllocator::MemoryInfo meminfo2; - allocator_->GetMemoryInfo(&meminfo2); - EXPECT_GT(meminfo1.free, meminfo2.free); - - HistogramBase* boolean_histogram = BooleanHistogram::FactoryGet( - "TestBooleanHistogram", HistogramBase::kIsPersistent); - EXPECT_TRUE(boolean_histogram); - boolean_histogram->CheckName("TestBooleanHistogram"); - PersistentMemoryAllocator::MemoryInfo meminfo3; - allocator_->GetMemoryInfo(&meminfo3); - EXPECT_GT(meminfo2.free, meminfo3.free); - - std::vector custom_ranges; - custom_ranges.push_back(1); - custom_ranges.push_back(5); - HistogramBase* custom_histogram = CustomHistogram::FactoryGet( - "TestCustomHistogram", custom_ranges, HistogramBase::kIsPersistent); - EXPECT_TRUE(custom_histogram); - custom_histogram->CheckName("TestCustomHistogram"); - PersistentMemoryAllocator::MemoryInfo meminfo4; - allocator_->GetMemoryInfo(&meminfo4); - EXPECT_GT(meminfo3.free, meminfo4.free); - - PersistentMemoryAllocator::Iterator iter(allocator_); - uint32_t type; - EXPECT_NE(0U, iter.GetNext(&type)); // Histogram - EXPECT_NE(0U, iter.GetNext(&type)); // LinearHistogram - EXPECT_NE(0U, iter.GetNext(&type)); // BooleanHistogram - EXPECT_NE(0U, iter.GetNext(&type)); // CustomHistogram - EXPECT_EQ(0U, iter.GetNext(&type)); - - // Create a second allocator and have it access the memory of the first. - std::unique_ptr recovered; - PersistentHistogramAllocator recovery( - std::make_unique( - allocator_memory_.get(), kAllocatorMemorySize, 0, 0, "", false)); - PersistentHistogramAllocator::Iterator histogram_iter(&recovery); - - recovered = histogram_iter.GetNext(); - ASSERT_TRUE(recovered); - recovered->CheckName("TestHistogram"); - - recovered = histogram_iter.GetNext(); - ASSERT_TRUE(recovered); - recovered->CheckName("TestLinearHistogram"); - - recovered = histogram_iter.GetNext(); - ASSERT_TRUE(recovered); - recovered->CheckName("TestBooleanHistogram"); - - recovered = histogram_iter.GetNext(); - ASSERT_TRUE(recovered); - recovered->CheckName("TestCustomHistogram"); - - recovered = histogram_iter.GetNext(); - EXPECT_FALSE(recovered); -} - -TEST_F(PersistentHistogramAllocatorTest, ConstructPaths) { - const FilePath dir_path(FILE_PATH_LITERAL("foo/")); - const std::string dir_string = - dir_path.NormalizePathSeparators().AsUTF8Unsafe(); - - FilePath path = GlobalHistogramAllocator::ConstructFilePath(dir_path, "bar"); - EXPECT_EQ(dir_string + "bar.pma", path.AsUTF8Unsafe()); - - std::string name; - Time stamp; - ProcessId pid; - EXPECT_FALSE( - GlobalHistogramAllocator::ParseFilePath(path, &name, nullptr, nullptr)); - EXPECT_FALSE( - GlobalHistogramAllocator::ParseFilePath(path, nullptr, &stamp, nullptr)); - EXPECT_FALSE( - GlobalHistogramAllocator::ParseFilePath(path, nullptr, nullptr, &pid)); - - path = GlobalHistogramAllocator::ConstructFilePathForUploadDir( - dir_path, "bar", Time::FromTimeT(12345), 6789); - EXPECT_EQ(dir_string + "bar-3039-1A85.pma", path.AsUTF8Unsafe()); - ASSERT_TRUE( - GlobalHistogramAllocator::ParseFilePath(path, &name, &stamp, &pid)); - EXPECT_EQ(name, "bar"); - EXPECT_EQ(Time::FromTimeT(12345), stamp); - EXPECT_EQ(static_cast(6789), pid); -} - -TEST_F(PersistentHistogramAllocatorTest, CreateWithFile) { - const char temp_name[] = "CreateWithFileTest"; - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name); - const size_t temp_size = 64 << 10; // 64 KiB - - // Test creation of a new file. - GlobalHistogramAllocator::ReleaseForTesting(); - GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, temp_name); - EXPECT_EQ(std::string(temp_name), - GlobalHistogramAllocator::Get()->memory_allocator()->Name()); - - // Test re-open of a possibly-existing file. - GlobalHistogramAllocator::ReleaseForTesting(); - GlobalHistogramAllocator::CreateWithFile(temp_file, temp_size, 0, ""); - EXPECT_EQ(std::string(temp_name), - GlobalHistogramAllocator::Get()->memory_allocator()->Name()); - - // Test re-open of an known-existing file. - GlobalHistogramAllocator::ReleaseForTesting(); - GlobalHistogramAllocator::CreateWithFile(temp_file, 0, 0, ""); - EXPECT_EQ(std::string(temp_name), - GlobalHistogramAllocator::Get()->memory_allocator()->Name()); - - // Final release so file and temp-dir can be removed. - GlobalHistogramAllocator::ReleaseForTesting(); -} - -TEST_F(PersistentHistogramAllocatorTest, CreateSpareFile) { - const char temp_name[] = "CreateSpareFileTest.pma"; - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath temp_file = temp_dir.GetPath().AppendASCII(temp_name); - const size_t temp_size = 64 << 10; // 64 KiB - - ASSERT_TRUE(GlobalHistogramAllocator::CreateSpareFile(temp_file, temp_size)); - - File file(temp_file, File::FLAG_OPEN | File::FLAG_READ); - ASSERT_TRUE(file.IsValid()); - EXPECT_EQ(static_cast(temp_size), file.GetLength()); - - char buffer[256]; - for (size_t pos = 0; pos < temp_size; pos += sizeof(buffer)) { - ASSERT_EQ(static_cast(sizeof(buffer)), - file.ReadAtCurrentPos(buffer, sizeof(buffer))); - for (size_t i = 0; i < sizeof(buffer); ++i) - EXPECT_EQ(0, buffer[i]); - } -} - -TEST_F(PersistentHistogramAllocatorTest, StatisticsRecorderMerge) { - const char LinearHistogramName[] = "SRTLinearHistogram"; - const char SparseHistogramName[] = "SRTSparseHistogram"; - const size_t starting_sr_count = StatisticsRecorder::GetHistogramCount(); - - // Create a local StatisticsRecorder in which the newly created histogram - // will be recorded. The global allocator must be replaced after because the - // act of releasing will cause the active SR to forget about all histograms - // in the relased memory. - std::unique_ptr local_sr = - StatisticsRecorder::CreateTemporaryForTesting(); - EXPECT_EQ(0U, StatisticsRecorder::GetHistogramCount()); - std::unique_ptr old_allocator = - GlobalHistogramAllocator::ReleaseForTesting(); - GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, ""); - ASSERT_TRUE(GlobalHistogramAllocator::Get()); - - // Create a linear histogram for merge testing. - HistogramBase* histogram1 = - LinearHistogram::FactoryGet(LinearHistogramName, 1, 10, 10, 0); - ASSERT_TRUE(histogram1); - EXPECT_EQ(1U, StatisticsRecorder::GetHistogramCount()); - histogram1->Add(3); - histogram1->Add(1); - histogram1->Add(4); - histogram1->AddCount(1, 4); - histogram1->Add(6); - - // Create a sparse histogram for merge testing. - HistogramBase* histogram2 = - SparseHistogram::FactoryGet(SparseHistogramName, 0); - ASSERT_TRUE(histogram2); - EXPECT_EQ(2U, StatisticsRecorder::GetHistogramCount()); - histogram2->Add(3); - histogram2->Add(1); - histogram2->Add(4); - histogram2->AddCount(1, 4); - histogram2->Add(6); - - // Destroy the local SR and ensure that we're back to the initial state and - // restore the global allocator. Histograms created in the local SR will - // become unmanaged. - std::unique_ptr new_allocator = - GlobalHistogramAllocator::ReleaseForTesting(); - local_sr.reset(); - EXPECT_EQ(starting_sr_count, StatisticsRecorder::GetHistogramCount()); - GlobalHistogramAllocator::Set(std::move(old_allocator)); - - // Create a "recovery" allocator using the same memory as the local one. - PersistentHistogramAllocator recovery1( - std::make_unique( - const_cast(new_allocator->memory_allocator()->data()), - new_allocator->memory_allocator()->size(), 0, 0, "", false)); - PersistentHistogramAllocator::Iterator histogram_iter1(&recovery1); - - // Get the histograms that were created locally (and forgotten) and merge - // them into the global SR. New objects will be created. - std::unique_ptr recovered; - while (true) { - recovered = histogram_iter1.GetNext(); - if (!recovered) - break; - - recovery1.MergeHistogramDeltaToStatisticsRecorder(recovered.get()); - HistogramBase* found = - StatisticsRecorder::FindHistogram(recovered->histogram_name()); - EXPECT_NE(recovered.get(), found); - }; - EXPECT_EQ(starting_sr_count + 2, StatisticsRecorder::GetHistogramCount()); - - // Check the merged histograms for accuracy. - HistogramBase* found = StatisticsRecorder::FindHistogram(LinearHistogramName); - ASSERT_TRUE(found); - std::unique_ptr snapshot = found->SnapshotSamples(); - EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount()); - EXPECT_EQ(1, snapshot->GetCount(3)); - EXPECT_EQ(5, snapshot->GetCount(1)); - EXPECT_EQ(1, snapshot->GetCount(4)); - EXPECT_EQ(1, snapshot->GetCount(6)); - - found = StatisticsRecorder::FindHistogram(SparseHistogramName); - ASSERT_TRUE(found); - snapshot = found->SnapshotSamples(); - EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount()); - EXPECT_EQ(1, snapshot->GetCount(3)); - EXPECT_EQ(5, snapshot->GetCount(1)); - EXPECT_EQ(1, snapshot->GetCount(4)); - EXPECT_EQ(1, snapshot->GetCount(6)); - - // Perform additional histogram increments. - histogram1->AddCount(1, 3); - histogram1->Add(6); - histogram2->AddCount(1, 3); - histogram2->Add(7); - - // Do another merge. - PersistentHistogramAllocator recovery2( - std::make_unique( - const_cast(new_allocator->memory_allocator()->data()), - new_allocator->memory_allocator()->size(), 0, 0, "", false)); - PersistentHistogramAllocator::Iterator histogram_iter2(&recovery2); - while (true) { - recovered = histogram_iter2.GetNext(); - if (!recovered) - break; - recovery2.MergeHistogramDeltaToStatisticsRecorder(recovered.get()); - }; - EXPECT_EQ(starting_sr_count + 2, StatisticsRecorder::GetHistogramCount()); - - // And verify. - found = StatisticsRecorder::FindHistogram(LinearHistogramName); - snapshot = found->SnapshotSamples(); - EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount()); - EXPECT_EQ(1, snapshot->GetCount(3)); - EXPECT_EQ(8, snapshot->GetCount(1)); - EXPECT_EQ(1, snapshot->GetCount(4)); - EXPECT_EQ(2, snapshot->GetCount(6)); - - found = StatisticsRecorder::FindHistogram(SparseHistogramName); - snapshot = found->SnapshotSamples(); - EXPECT_EQ(found->SnapshotSamples()->TotalCount(), snapshot->TotalCount()); - EXPECT_EQ(1, snapshot->GetCount(3)); - EXPECT_EQ(8, snapshot->GetCount(1)); - EXPECT_EQ(1, snapshot->GetCount(4)); - EXPECT_EQ(1, snapshot->GetCount(6)); - EXPECT_EQ(1, snapshot->GetCount(7)); -} - -TEST_F(PersistentHistogramAllocatorTest, RangesDeDuplication) { - // This corresponds to the "ranges_ref" field of the PersistentHistogramData - // structure defined (privately) inside persistent_histogram_allocator.cc. - const int kRangesRefIndex = 5; - - // Create two histograms with the same ranges. - HistogramBase* histogram1 = - Histogram::FactoryGet("TestHistogram1", 1, 1000, 10, 0); - HistogramBase* histogram2 = - Histogram::FactoryGet("TestHistogram2", 1, 1000, 10, 0); - const uint32_t ranges_ref = static_cast(histogram1) - ->bucket_ranges() - ->persistent_reference(); - ASSERT_NE(0U, ranges_ref); - EXPECT_EQ(ranges_ref, static_cast(histogram2) - ->bucket_ranges() - ->persistent_reference()); - - // Make sure that the persistent data record is also correct. Two histograms - // will be fetched; other allocations are not "iterable". - PersistentMemoryAllocator::Iterator iter(allocator_); - uint32_t type; - uint32_t ref1 = iter.GetNext(&type); - uint32_t ref2 = iter.GetNext(&type); - EXPECT_EQ(0U, iter.GetNext(&type)); - EXPECT_NE(0U, ref1); - EXPECT_NE(0U, ref2); - EXPECT_NE(ref1, ref2); - - uint32_t* data1 = - allocator_->GetAsArray(ref1, 0, kRangesRefIndex + 1); - uint32_t* data2 = - allocator_->GetAsArray(ref2, 0, kRangesRefIndex + 1); - EXPECT_EQ(ranges_ref, data1[kRangesRefIndex]); - EXPECT_EQ(ranges_ref, data2[kRangesRefIndex]); -} - -} // namespace base diff --git a/metrics/persistent_histogram_storage.cc b/metrics/persistent_histogram_storage.cc deleted file mode 100644 index e2a56d7df..000000000 --- a/metrics/persistent_histogram_storage.cc +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2018 The Chromium 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/persistent_histogram_storage.h" - -#include "base/files/file_util.h" -#include "base/files/important_file_writer.h" -#include "base/logging.h" -#include "base/metrics/persistent_histogram_allocator.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/time/time.h" -#include "build/build_config.h" - -namespace { - -constexpr size_t kAllocSize = 1 << 20; // 1 MiB - -} // namespace - -namespace base { - -PersistentHistogramStorage::PersistentHistogramStorage( - StringPiece allocator_name, - StorageDirManagement storage_dir_management) - : storage_dir_management_(storage_dir_management) { - DCHECK(!allocator_name.empty()); - DCHECK(IsStringASCII(allocator_name)); - - GlobalHistogramAllocator::CreateWithLocalMemory(kAllocSize, - 0, // No identifier. - allocator_name); - GlobalHistogramAllocator::Get()->CreateTrackingHistograms(allocator_name); -} - -PersistentHistogramStorage::~PersistentHistogramStorage() { - PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get(); - allocator->UpdateTrackingHistograms(); - - // TODO(chengx): Investigate making early return depend on whethere there are - // metrics to report at this point or not. - if (disabled_) - return; - - // Stop if the storage base directory has not been properly set. - if (storage_base_dir_.empty()) { - LOG(ERROR) - << "Could not write \"" << allocator->Name() - << "\" persistent histograms to file as the storage base directory " - "is not properly set."; - return; - } - - FilePath storage_dir = storage_base_dir_.AppendASCII(allocator->Name()); - - switch (storage_dir_management_) { - case StorageDirManagement::kCreate: - if (!CreateDirectory(storage_dir)) { - LOG(ERROR) - << "Could not write \"" << allocator->Name() - << "\" persistent histograms to file as the storage directory " - "cannot be created."; - return; - } - break; - case StorageDirManagement::kUseExisting: - if (!DirectoryExists(storage_dir)) { - // When the consumer of this class decides to use an existing storage - // directory, it should ensure the directory's existence if it's - // essential. - LOG(ERROR) - << "Could not write \"" << allocator->Name() - << "\" persistent histograms to file as the storage directory " - "does not exist."; - return; - } - break; - } - - // Save data using the current time as the filename. The actual filename - // doesn't matter (so long as it ends with the correct extension) but this - // works as well as anything. - Time::Exploded exploded; - Time::Now().LocalExplode(&exploded); - const FilePath file_path = - storage_dir - .AppendASCII(StringPrintf("%04d%02d%02d%02d%02d%02d", exploded.year, - exploded.month, exploded.day_of_month, - exploded.hour, exploded.minute, - exploded.second)) - .AddExtension(PersistentMemoryAllocator::kFileExtension); - - StringPiece contents(static_cast(allocator->data()), - allocator->used()); - if (!ImportantFileWriter::WriteFileAtomically(file_path, contents)) { - LOG(ERROR) << "Persistent histograms fail to write to file: " - << file_path.value(); - } -} - -} // namespace base diff --git a/metrics/persistent_histogram_storage.h b/metrics/persistent_histogram_storage.h deleted file mode 100644 index 397236dd7..000000000 --- a/metrics/persistent_histogram_storage.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018 The Chromium 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_PERSISTENT_HISTOGRAM_STORAGE_H_ -#define BASE_METRICS_PERSISTENT_HISTOGRAM_STORAGE_H_ - -#include "base/base_export.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" - -namespace base { - -// This class creates a fixed sized persistent memory to allow histograms to be -// stored in it. When a PersistentHistogramStorage is destructed, histograms -// recorded during its lifetime are persisted in the directory -// |storage_base_dir_|/|allocator_name| (see the ctor for allocator_name). -// Histograms are not persisted if the storage directory does not exist on -// destruction. PersistentHistogramStorage should be instantiated as early as -// possible in the process lifetime and should never be instantiated again. -// Persisted histograms will eventually be reported by Chrome. -class BASE_EXPORT PersistentHistogramStorage { - public: - enum class StorageDirManagement { kCreate, kUseExisting }; - - // Creates a process-wide storage location for histograms that will be written - // to a file within a directory provided by |set_storage_base_dir()| on - // destruction. - // The |allocator_name| is used both as an internal name for the allocator, - // well as the leaf directory name for the file to which the histograms are - // persisted. The string must be ASCII. - // |storage_dir_management| specifies if this instance reuses an existing - // storage directory, or is responsible for creating one. - PersistentHistogramStorage(StringPiece allocator_name, - StorageDirManagement storage_dir_management); - - ~PersistentHistogramStorage(); - - // The storage directory isn't always known during initial construction so - // it's set separately. The last one wins if there are multiple calls to this - // method. - void set_storage_base_dir(const FilePath& storage_base_dir) { - storage_base_dir_ = storage_base_dir; - } - - // Disables histogram storage. - void Disable() { disabled_ = true; } - - private: - // Metrics files are written into directory - // |storage_base_dir_|/|allocator_name| (see the ctor for allocator_name). - FilePath storage_base_dir_; - - // The setting of the storage directory management. - const StorageDirManagement storage_dir_management_; - - // A flag indicating if histogram storage is disabled. It starts with false, - // but can be set to true by the caller who decides to throw away its - // histogram data. - bool disabled_ = false; - - DISALLOW_COPY_AND_ASSIGN(PersistentHistogramStorage); -}; - -} // namespace base - -#endif // BASE_METRICS_PERSISTENT_HISTOGRAM_STORAGE_H_ diff --git a/metrics/persistent_histogram_storage_unittest.cc b/metrics/persistent_histogram_storage_unittest.cc deleted file mode 100644 index 0b9b1ce6b..000000000 --- a/metrics/persistent_histogram_storage_unittest.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 The Chromium 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/persistent_histogram_storage.h" - -#include - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/metrics/histogram_macros.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -// Name of the allocator for storing histograms. -constexpr char kTestHistogramAllocatorName[] = "TestMetrics"; - -} // namespace - -class PersistentHistogramStorageTest : public testing::Test { - protected: - PersistentHistogramStorageTest() = default; - ~PersistentHistogramStorageTest() override = default; - - // Creates a unique temporary directory, and sets the test storage directory. - void SetUp() override { - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - test_storage_dir_ = - temp_dir_path().AppendASCII(kTestHistogramAllocatorName); - } - - // Gets the path to the temporary directory. - const FilePath& temp_dir_path() { return temp_dir_.GetPath(); } - - const FilePath& test_storage_dir() { return test_storage_dir_; } - - private: - // A temporary directory where all file IO operations take place. - ScopedTempDir temp_dir_; - - // The directory into which metrics files are written. - FilePath test_storage_dir_; - - DISALLOW_COPY_AND_ASSIGN(PersistentHistogramStorageTest); -}; - -// TODO(chengx): Re-enable the test on OS_IOS after issue 836789 is fixed. -// PersistentHistogramStorage is only used on OS_WIN now, so disabling this -// test on OS_IOS is fine. -#if !defined(OS_NACL) && !defined(OS_IOS) -TEST_F(PersistentHistogramStorageTest, HistogramWriteTest) { - auto persistent_histogram_storage = - std::make_unique( - kTestHistogramAllocatorName, - PersistentHistogramStorage::StorageDirManagement::kCreate); - - persistent_histogram_storage->set_storage_base_dir(temp_dir_path()); - - // Log some random data. - UMA_HISTOGRAM_BOOLEAN("Some.Test.Metric", true); - - // Deleting the object causes the data to be written to the disk. - persistent_histogram_storage.reset(); - - // The storage directory and the histogram file are created during the - // destruction of the PersistentHistogramStorage instance. - EXPECT_TRUE(DirectoryExists(test_storage_dir())); - EXPECT_FALSE(IsDirectoryEmpty(test_storage_dir())); -} -#endif // !defined(OS_NACL) && !defined(OS_IOS) - -} // namespace base diff --git a/metrics/persistent_memory_allocator.cc b/metrics/persistent_memory_allocator.cc deleted file mode 100644 index 9b18a000d..000000000 --- a/metrics/persistent_memory_allocator.cc +++ /dev/null @@ -1,1204 +0,0 @@ -// Copyright (c) 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/metrics/persistent_memory_allocator.h" - -#include -#include - -#if defined(OS_WIN) -#include -#include "winbase.h" -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -#include -#endif - -#include "base/files/memory_mapped_file.h" -#include "base/logging.h" -#include "base/memory/shared_memory.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/sparse_histogram.h" -#include "base/numerics/safe_conversions.h" -#include "base/sys_info.h" -#include "base/threading/thread_restrictions.h" -#include "build/build_config.h" - -namespace { - -// Limit of memory segment size. It has to fit in an unsigned 32-bit number -// and should be a power of 2 in order to accomodate almost any page size. -const uint32_t kSegmentMaxSize = 1 << 30; // 1 GiB - -// A constant (random) value placed in the shared metadata to identify -// an already initialized memory segment. -const uint32_t kGlobalCookie = 0x408305DC; - -// The current version of the metadata. If updates are made that change -// the metadata, the version number can be queried to operate in a backward- -// compatible manner until the memory segment is completely re-initalized. -const uint32_t kGlobalVersion = 2; - -// Constant values placed in the block headers to indicate its state. -const uint32_t kBlockCookieFree = 0; -const uint32_t kBlockCookieQueue = 1; -const uint32_t kBlockCookieWasted = (uint32_t)-1; -const uint32_t kBlockCookieAllocated = 0xC8799269; - -// TODO(bcwhite): When acceptable, consider moving flags to std::atomic -// types rather than combined bitfield. - -// Flags stored in the flags_ field of the SharedMetadata structure below. -enum : int { - kFlagCorrupt = 1 << 0, - kFlagFull = 1 << 1 -}; - -// Errors that are logged in "errors" histogram. -enum AllocatorError : int { - kMemoryIsCorrupt = 1, -}; - -bool CheckFlag(const volatile std::atomic* flags, int flag) { - uint32_t loaded_flags = flags->load(std::memory_order_relaxed); - return (loaded_flags & flag) != 0; -} - -void SetFlag(volatile std::atomic* flags, int flag) { - uint32_t loaded_flags = flags->load(std::memory_order_relaxed); - for (;;) { - uint32_t new_flags = (loaded_flags & ~flag) | flag; - // In the failue case, actual "flags" value stored in loaded_flags. - // These access are "relaxed" because they are completely independent - // of all other values. - if (flags->compare_exchange_weak(loaded_flags, new_flags, - std::memory_order_relaxed, - std::memory_order_relaxed)) { - break; - } - } -} - -} // namespace - -namespace base { - -// All allocations and data-structures must be aligned to this byte boundary. -// Alignment as large as the physical bus between CPU and RAM is _required_ -// for some architectures, is simply more efficient on other CPUs, and -// generally a Good Idea(tm) for all platforms as it reduces/eliminates the -// chance that a type will span cache lines. Alignment mustn't be less -// than 8 to ensure proper alignment for all types. The rest is a balance -// between reducing spans across multiple cache lines and wasted space spent -// padding out allocations. An alignment of 16 would ensure that the block -// header structure always sits in a single cache line. An average of about -// 1/2 this value will be wasted with every allocation. -const uint32_t PersistentMemoryAllocator::kAllocAlignment = 8; - -// The block-header is placed at the top of every allocation within the -// segment to describe the data that follows it. -struct PersistentMemoryAllocator::BlockHeader { - uint32_t size; // Number of bytes in this block, including header. - uint32_t cookie; // Constant value indicating completed allocation. - std::atomic type_id; // Arbitrary number indicating data type. - std::atomic next; // Pointer to the next block when iterating. -}; - -// The shared metadata exists once at the top of the memory segment to -// describe the state of the allocator to all processes. The size of this -// structure must be a multiple of 64-bits to ensure compatibility between -// architectures. -struct PersistentMemoryAllocator::SharedMetadata { - uint32_t cookie; // Some value that indicates complete initialization. - uint32_t size; // Total size of memory segment. - uint32_t page_size; // Paging size within memory segment. - uint32_t version; // Version code so upgrades don't break. - uint64_t id; // Arbitrary ID number given by creator. - uint32_t name; // Reference to stored name string. - uint32_t padding1; // Pad-out read-only data to 64-bit alignment. - - // Above is read-only after first construction. Below may be changed and - // so must be marked "volatile" to provide correct inter-process behavior. - - // State of the memory, plus some padding to keep alignment. - volatile std::atomic memory_state; // MemoryState enum values. - uint8_t padding2[3]; - - // Bitfield of information flags. Access to this should be done through - // the CheckFlag() and SetFlag() methods defined above. - volatile std::atomic flags; - - // Offset/reference to first free space in segment. - volatile std::atomic freeptr; - - // The "iterable" queue is an M&S Queue as described here, append-only: - // https://www.research.ibm.com/people/m/michael/podc-1996.pdf - // |queue| needs to be 64-bit aligned and is itself a multiple of 64 bits. - volatile std::atomic tailptr; // Last block of iteration queue. - volatile BlockHeader queue; // Empty block for linked-list head/tail. -}; - -// The "queue" block header is used to detect "last node" so that zero/null -// can be used to indicate that it hasn't been added at all. It is part of -// the SharedMetadata structure which itself is always located at offset zero. -const PersistentMemoryAllocator::Reference - PersistentMemoryAllocator::kReferenceQueue = - offsetof(SharedMetadata, queue); - -const base::FilePath::CharType PersistentMemoryAllocator::kFileExtension[] = - FILE_PATH_LITERAL(".pma"); - - -PersistentMemoryAllocator::Iterator::Iterator( - const PersistentMemoryAllocator* allocator) - : allocator_(allocator), last_record_(kReferenceQueue), record_count_(0) {} - -PersistentMemoryAllocator::Iterator::Iterator( - const PersistentMemoryAllocator* allocator, - Reference starting_after) - : allocator_(allocator), last_record_(0), record_count_(0) { - Reset(starting_after); -} - -void PersistentMemoryAllocator::Iterator::Reset() { - last_record_.store(kReferenceQueue, std::memory_order_relaxed); - record_count_.store(0, std::memory_order_relaxed); -} - -void PersistentMemoryAllocator::Iterator::Reset(Reference starting_after) { - if (starting_after == 0) { - Reset(); - return; - } - - last_record_.store(starting_after, std::memory_order_relaxed); - record_count_.store(0, std::memory_order_relaxed); - - // Ensure that the starting point is a valid, iterable block (meaning it can - // be read and has a non-zero "next" pointer). - const volatile BlockHeader* block = - allocator_->GetBlock(starting_after, 0, 0, false, false); - if (!block || block->next.load(std::memory_order_relaxed) == 0) { - NOTREACHED(); - last_record_.store(kReferenceQueue, std::memory_order_release); - } -} - -PersistentMemoryAllocator::Reference -PersistentMemoryAllocator::Iterator::GetLast() { - Reference last = last_record_.load(std::memory_order_relaxed); - if (last == kReferenceQueue) - return kReferenceNull; - return last; -} - -PersistentMemoryAllocator::Reference -PersistentMemoryAllocator::Iterator::GetNext(uint32_t* type_return) { - // Make a copy of the existing count of found-records, acquiring all changes - // made to the allocator, notably "freeptr" (see comment in loop for why - // the load of that value cannot be moved above here) that occurred during - // any previous runs of this method, including those by parallel threads - // that interrupted it. It pairs with the Release at the end of this method. - // - // Otherwise, if the compiler were to arrange the two loads such that - // "count" was fetched _after_ "freeptr" then it would be possible for - // this thread to be interrupted between them and other threads perform - // multiple allocations, make-iterables, and iterations (with the included - // increment of |record_count_|) culminating in the check at the bottom - // mistakenly determining that a loop exists. Isn't this stuff fun? - uint32_t count = record_count_.load(std::memory_order_acquire); - - Reference last = last_record_.load(std::memory_order_acquire); - Reference next; - while (true) { - const volatile BlockHeader* block = - allocator_->GetBlock(last, 0, 0, true, false); - if (!block) // Invalid iterator state. - return kReferenceNull; - - // The compiler and CPU can freely reorder all memory accesses on which - // there are no dependencies. It could, for example, move the load of - // "freeptr" to above this point because there are no explicit dependencies - // between it and "next". If it did, however, then another block could - // be queued after that but before the following load meaning there is - // one more queued block than the future "detect loop by having more - // blocks that could fit before freeptr" will allow. - // - // By "acquiring" the "next" value here, it's synchronized to the enqueue - // of the node which in turn is synchronized to the allocation (which sets - // freeptr). Thus, the scenario above cannot happen. - next = block->next.load(std::memory_order_acquire); - if (next == kReferenceQueue) // No next allocation in queue. - return kReferenceNull; - block = allocator_->GetBlock(next, 0, 0, false, false); - if (!block) { // Memory is corrupt. - allocator_->SetCorrupt(); - return kReferenceNull; - } - - // Update the "last_record" pointer to be the reference being returned. - // If it fails then another thread has already iterated past it so loop - // again. Failing will also load the existing value into "last" so there - // is no need to do another such load when the while-loop restarts. A - // "strong" compare-exchange is used because failing unnecessarily would - // mean repeating some fairly costly validations above. - if (last_record_.compare_exchange_strong( - last, next, std::memory_order_acq_rel, std::memory_order_acquire)) { - *type_return = block->type_id.load(std::memory_order_relaxed); - break; - } - } - - // Memory corruption could cause a loop in the list. Such must be detected - // so as to not cause an infinite loop in the caller. This is done by simply - // making sure it doesn't iterate more times than the absolute maximum - // number of allocations that could have been made. Callers are likely - // to loop multiple times before it is detected but at least it stops. - const uint32_t freeptr = std::min( - allocator_->shared_meta()->freeptr.load(std::memory_order_relaxed), - allocator_->mem_size_); - const uint32_t max_records = - freeptr / (sizeof(BlockHeader) + kAllocAlignment); - if (count > max_records) { - allocator_->SetCorrupt(); - return kReferenceNull; - } - - // Increment the count and release the changes made above. It pairs with - // the Acquire at the top of this method. Note that this operation is not - // strictly synchonized with fetching of the object to return, which would - // have to be done inside the loop and is somewhat complicated to achieve. - // It does not matter if it falls behind temporarily so long as it never - // gets ahead. - record_count_.fetch_add(1, std::memory_order_release); - return next; -} - -PersistentMemoryAllocator::Reference -PersistentMemoryAllocator::Iterator::GetNextOfType(uint32_t type_match) { - Reference ref; - uint32_t type_found; - while ((ref = GetNext(&type_found)) != 0) { - if (type_found == type_match) - return ref; - } - return kReferenceNull; -} - - -// static -bool PersistentMemoryAllocator::IsMemoryAcceptable(const void* base, - size_t size, - size_t page_size, - bool readonly) { - return ((base && reinterpret_cast(base) % kAllocAlignment == 0) && - (size >= sizeof(SharedMetadata) && size <= kSegmentMaxSize) && - (size % kAllocAlignment == 0 || readonly) && - (page_size == 0 || size % page_size == 0 || readonly)); -} - -PersistentMemoryAllocator::PersistentMemoryAllocator(void* base, - size_t size, - size_t page_size, - uint64_t id, - base::StringPiece name, - bool readonly) - : PersistentMemoryAllocator(Memory(base, MEM_EXTERNAL), - size, - page_size, - id, - name, - readonly) {} - -PersistentMemoryAllocator::PersistentMemoryAllocator(Memory memory, - size_t size, - size_t page_size, - uint64_t id, - base::StringPiece name, - bool readonly) - : mem_base_(static_cast(memory.base)), - mem_type_(memory.type), - mem_size_(static_cast(size)), - mem_page_(static_cast((page_size ? page_size : size))), -#if defined(OS_NACL) - vm_page_size_(4096U), // SysInfo is not built for NACL. -#else - vm_page_size_(SysInfo::VMAllocationGranularity()), -#endif - readonly_(readonly), - corrupt_(0), - allocs_histogram_(nullptr), - used_histogram_(nullptr), - errors_histogram_(nullptr) { - // These asserts ensure that the structures are 32/64-bit agnostic and meet - // all the requirements of use within the allocator. They access private - // definitions and so cannot be moved to the global scope. - static_assert(sizeof(PersistentMemoryAllocator::BlockHeader) == 16, - "struct is not portable across different natural word widths"); - static_assert(sizeof(PersistentMemoryAllocator::SharedMetadata) == 64, - "struct is not portable across different natural word widths"); - - static_assert(sizeof(BlockHeader) % kAllocAlignment == 0, - "BlockHeader is not a multiple of kAllocAlignment"); - static_assert(sizeof(SharedMetadata) % kAllocAlignment == 0, - "SharedMetadata is not a multiple of kAllocAlignment"); - static_assert(kReferenceQueue % kAllocAlignment == 0, - "\"queue\" is not aligned properly; must be at end of struct"); - - // Ensure that memory segment is of acceptable size. - CHECK(IsMemoryAcceptable(memory.base, size, page_size, readonly)); - - // These atomics operate inter-process and so must be lock-free. The local - // casts are to make sure it can be evaluated at compile time to a constant. - CHECK(((SharedMetadata*)nullptr)->freeptr.is_lock_free()); - CHECK(((SharedMetadata*)nullptr)->flags.is_lock_free()); - CHECK(((BlockHeader*)nullptr)->next.is_lock_free()); - CHECK(corrupt_.is_lock_free()); - - if (shared_meta()->cookie != kGlobalCookie) { - if (readonly) { - SetCorrupt(); - return; - } - - // This block is only executed when a completely new memory segment is - // being initialized. It's unshared and single-threaded... - volatile BlockHeader* const first_block = - reinterpret_cast(mem_base_ + - sizeof(SharedMetadata)); - if (shared_meta()->cookie != 0 || - shared_meta()->size != 0 || - shared_meta()->version != 0 || - shared_meta()->freeptr.load(std::memory_order_relaxed) != 0 || - shared_meta()->flags.load(std::memory_order_relaxed) != 0 || - shared_meta()->id != 0 || - shared_meta()->name != 0 || - shared_meta()->tailptr != 0 || - shared_meta()->queue.cookie != 0 || - shared_meta()->queue.next.load(std::memory_order_relaxed) != 0 || - first_block->size != 0 || - first_block->cookie != 0 || - first_block->type_id.load(std::memory_order_relaxed) != 0 || - first_block->next != 0) { - // ...or something malicious has been playing with the metadata. - SetCorrupt(); - } - - // This is still safe to do even if corruption has been detected. - shared_meta()->cookie = kGlobalCookie; - shared_meta()->size = mem_size_; - shared_meta()->page_size = mem_page_; - shared_meta()->version = kGlobalVersion; - shared_meta()->id = id; - shared_meta()->freeptr.store(sizeof(SharedMetadata), - std::memory_order_release); - - // Set up the queue of iterable allocations. - shared_meta()->queue.size = sizeof(BlockHeader); - shared_meta()->queue.cookie = kBlockCookieQueue; - shared_meta()->queue.next.store(kReferenceQueue, std::memory_order_release); - shared_meta()->tailptr.store(kReferenceQueue, std::memory_order_release); - - // Allocate space for the name so other processes can learn it. - if (!name.empty()) { - const size_t name_length = name.length() + 1; - shared_meta()->name = Allocate(name_length, 0); - char* name_cstr = GetAsArray(shared_meta()->name, 0, name_length); - if (name_cstr) - memcpy(name_cstr, name.data(), name.length()); - } - - shared_meta()->memory_state.store(MEMORY_INITIALIZED, - std::memory_order_release); - } else { - if (shared_meta()->size == 0 || shared_meta()->version != kGlobalVersion || - shared_meta()->freeptr.load(std::memory_order_relaxed) == 0 || - shared_meta()->tailptr == 0 || shared_meta()->queue.cookie == 0 || - shared_meta()->queue.next.load(std::memory_order_relaxed) == 0) { - SetCorrupt(); - } - if (!readonly) { - // The allocator is attaching to a previously initialized segment of - // memory. If the initialization parameters differ, make the best of it - // by reducing the local construction parameters to match those of - // the actual memory area. This ensures that the local object never - // tries to write outside of the original bounds. - // Because the fields are const to ensure that no code other than the - // constructor makes changes to them as well as to give optimization - // hints to the compiler, it's necessary to const-cast them for changes - // here. - if (shared_meta()->size < mem_size_) - *const_cast(&mem_size_) = shared_meta()->size; - if (shared_meta()->page_size < mem_page_) - *const_cast(&mem_page_) = shared_meta()->page_size; - - // Ensure that settings are still valid after the above adjustments. - if (!IsMemoryAcceptable(memory.base, mem_size_, mem_page_, readonly)) - SetCorrupt(); - } - } -} - -PersistentMemoryAllocator::~PersistentMemoryAllocator() { - // It's strictly forbidden to do any memory access here in case there is - // some issue with the underlying memory segment. The "Local" allocator - // makes use of this to allow deletion of the segment on the heap from - // within its destructor. -} - -uint64_t PersistentMemoryAllocator::Id() const { - return shared_meta()->id; -} - -const char* PersistentMemoryAllocator::Name() const { - Reference name_ref = shared_meta()->name; - const char* name_cstr = - GetAsArray(name_ref, 0, PersistentMemoryAllocator::kSizeAny); - if (!name_cstr) - return ""; - - size_t name_length = GetAllocSize(name_ref); - if (name_cstr[name_length - 1] != '\0') { - NOTREACHED(); - SetCorrupt(); - return ""; - } - - return name_cstr; -} - -void PersistentMemoryAllocator::CreateTrackingHistograms( - base::StringPiece name) { - if (name.empty() || readonly_) - return; - std::string name_string = name.as_string(); - -#if 0 - // This histogram wasn't being used so has been disabled. It is left here - // in case development of a new use of the allocator could benefit from - // recording (temporarily and locally) the allocation sizes. - DCHECK(!allocs_histogram_); - allocs_histogram_ = Histogram::FactoryGet( - "UMA.PersistentAllocator." + name_string + ".Allocs", 1, 10000, 50, - HistogramBase::kUmaTargetedHistogramFlag); -#endif - - DCHECK(!used_histogram_); - used_histogram_ = LinearHistogram::FactoryGet( - "UMA.PersistentAllocator." + name_string + ".UsedPct", 1, 101, 21, - HistogramBase::kUmaTargetedHistogramFlag); - - DCHECK(!errors_histogram_); - errors_histogram_ = SparseHistogram::FactoryGet( - "UMA.PersistentAllocator." + name_string + ".Errors", - HistogramBase::kUmaTargetedHistogramFlag); -} - -void PersistentMemoryAllocator::Flush(bool sync) { - FlushPartial(used(), sync); -} - -void PersistentMemoryAllocator::SetMemoryState(uint8_t memory_state) { - shared_meta()->memory_state.store(memory_state, std::memory_order_relaxed); - FlushPartial(sizeof(SharedMetadata), false); -} - -uint8_t PersistentMemoryAllocator::GetMemoryState() const { - return shared_meta()->memory_state.load(std::memory_order_relaxed); -} - -size_t PersistentMemoryAllocator::used() const { - return std::min(shared_meta()->freeptr.load(std::memory_order_relaxed), - mem_size_); -} - -PersistentMemoryAllocator::Reference PersistentMemoryAllocator::GetAsReference( - const void* memory, - uint32_t type_id) const { - uintptr_t address = reinterpret_cast(memory); - if (address < reinterpret_cast(mem_base_)) - return kReferenceNull; - - uintptr_t offset = address - reinterpret_cast(mem_base_); - if (offset >= mem_size_ || offset < sizeof(BlockHeader)) - return kReferenceNull; - - Reference ref = static_cast(offset) - sizeof(BlockHeader); - if (!GetBlockData(ref, type_id, kSizeAny)) - return kReferenceNull; - - return ref; -} - -size_t PersistentMemoryAllocator::GetAllocSize(Reference ref) const { - const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); - if (!block) - return 0; - uint32_t size = block->size; - // Header was verified by GetBlock() but a malicious actor could change - // the value between there and here. Check it again. - if (size <= sizeof(BlockHeader) || ref + size > mem_size_) { - SetCorrupt(); - return 0; - } - return size - sizeof(BlockHeader); -} - -uint32_t PersistentMemoryAllocator::GetType(Reference ref) const { - const volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); - if (!block) - return 0; - return block->type_id.load(std::memory_order_relaxed); -} - -bool PersistentMemoryAllocator::ChangeType(Reference ref, - uint32_t to_type_id, - uint32_t from_type_id, - bool clear) { - DCHECK(!readonly_); - volatile BlockHeader* const block = GetBlock(ref, 0, 0, false, false); - if (!block) - return false; - - // "Strong" exchanges are used below because there is no loop that can retry - // in the wake of spurious failures possible with "weak" exchanges. It is, - // in aggregate, an "acquire-release" operation so no memory accesses can be - // reordered either before or after this method (since changes based on type - // could happen on either side). - - if (clear) { - // If clearing the memory, first change it to the "transitioning" type so - // there can be no confusion by other threads. After the memory is cleared, - // it can be changed to its final type. - if (!block->type_id.compare_exchange_strong( - from_type_id, kTypeIdTransitioning, std::memory_order_acquire, - std::memory_order_acquire)) { - // Existing type wasn't what was expected: fail (with no changes) - return false; - } - - // Clear the memory in an atomic manner. Using "release" stores force - // every write to be done after the ones before it. This is better than - // using memset because (a) it supports "volatile" and (b) it creates a - // reliable pattern upon which other threads may rely. - volatile std::atomic* data = - reinterpret_cast*>( - reinterpret_cast(block) + sizeof(BlockHeader)); - const uint32_t words = (block->size - sizeof(BlockHeader)) / sizeof(int); - DCHECK_EQ(0U, (block->size - sizeof(BlockHeader)) % sizeof(int)); - for (uint32_t i = 0; i < words; ++i) { - data->store(0, std::memory_order_release); - ++data; - } - - // If the destination type is "transitioning" then skip the final exchange. - if (to_type_id == kTypeIdTransitioning) - return true; - - // Finish the change to the desired type. - from_type_id = kTypeIdTransitioning; // Exchange needs modifiable original. - bool success = block->type_id.compare_exchange_strong( - from_type_id, to_type_id, std::memory_order_release, - std::memory_order_relaxed); - DCHECK(success); // Should never fail. - return success; - } - - // One step change to the new type. Will return false if the existing value - // doesn't match what is expected. - return block->type_id.compare_exchange_strong(from_type_id, to_type_id, - std::memory_order_acq_rel, - std::memory_order_acquire); -} - -PersistentMemoryAllocator::Reference PersistentMemoryAllocator::Allocate( - size_t req_size, - uint32_t type_id) { - Reference ref = AllocateImpl(req_size, type_id); - if (ref) { - // Success: Record this allocation in usage stats (if active). - if (allocs_histogram_) - allocs_histogram_->Add(static_cast(req_size)); - } else { - // Failure: Record an allocation of zero for tracking. - if (allocs_histogram_) - allocs_histogram_->Add(0); - } - return ref; -} - -PersistentMemoryAllocator::Reference PersistentMemoryAllocator::AllocateImpl( - size_t req_size, - uint32_t type_id) { - DCHECK(!readonly_); - - // Validate req_size to ensure it won't overflow when used as 32-bit value. - if (req_size > kSegmentMaxSize - sizeof(BlockHeader)) { - NOTREACHED(); - return kReferenceNull; - } - - // Round up the requested size, plus header, to the next allocation alignment. - uint32_t size = static_cast(req_size + sizeof(BlockHeader)); - size = (size + (kAllocAlignment - 1)) & ~(kAllocAlignment - 1); - if (size <= sizeof(BlockHeader) || size > mem_page_) { - NOTREACHED(); - return kReferenceNull; - } - - // Get the current start of unallocated memory. Other threads may - // update this at any time and cause us to retry these operations. - // This value should be treated as "const" to avoid confusion through - // the code below but recognize that any failed compare-exchange operation - // involving it will cause it to be loaded with a more recent value. The - // code should either exit or restart the loop in that case. - /* const */ uint32_t freeptr = - shared_meta()->freeptr.load(std::memory_order_acquire); - - // Allocation is lockless so we do all our caculation and then, if saving - // indicates a change has occurred since we started, scrap everything and - // start over. - for (;;) { - if (IsCorrupt()) - return kReferenceNull; - - if (freeptr + size > mem_size_) { - SetFlag(&shared_meta()->flags, kFlagFull); - return kReferenceNull; - } - - // Get pointer to the "free" block. If something has been allocated since - // the load of freeptr above, it is still safe as nothing will be written - // to that location until after the compare-exchange below. - volatile BlockHeader* const block = GetBlock(freeptr, 0, 0, false, true); - if (!block) { - SetCorrupt(); - return kReferenceNull; - } - - // An allocation cannot cross page boundaries. If it would, create a - // "wasted" block and begin again at the top of the next page. This - // area could just be left empty but we fill in the block header just - // for completeness sake. - const uint32_t page_free = mem_page_ - freeptr % mem_page_; - if (size > page_free) { - if (page_free <= sizeof(BlockHeader)) { - SetCorrupt(); - return kReferenceNull; - } - const uint32_t new_freeptr = freeptr + page_free; - if (shared_meta()->freeptr.compare_exchange_strong( - freeptr, new_freeptr, std::memory_order_acq_rel, - std::memory_order_acquire)) { - block->size = page_free; - block->cookie = kBlockCookieWasted; - } - continue; - } - - // Don't leave a slice at the end of a page too small for anything. This - // can result in an allocation up to two alignment-sizes greater than the - // minimum required by requested-size + header + alignment. - if (page_free - size < sizeof(BlockHeader) + kAllocAlignment) - size = page_free; - - const uint32_t new_freeptr = freeptr + size; - if (new_freeptr > mem_size_) { - SetCorrupt(); - return kReferenceNull; - } - - // Save our work. Try again if another thread has completed an allocation - // while we were processing. A "weak" exchange would be permissable here - // because the code will just loop and try again but the above processing - // is significant so make the extra effort of a "strong" exchange. - if (!shared_meta()->freeptr.compare_exchange_strong( - freeptr, new_freeptr, std::memory_order_acq_rel, - std::memory_order_acquire)) { - continue; - } - - // Given that all memory was zeroed before ever being given to an instance - // of this class and given that we only allocate in a monotomic fashion - // going forward, it must be that the newly allocated block is completely - // full of zeros. If we find anything in the block header that is NOT a - // zero then something must have previously run amuck through memory, - // writing beyond the allocated space and into unallocated space. - if (block->size != 0 || - block->cookie != kBlockCookieFree || - block->type_id.load(std::memory_order_relaxed) != 0 || - block->next.load(std::memory_order_relaxed) != 0) { - SetCorrupt(); - return kReferenceNull; - } - - // Make sure the memory exists by writing to the first byte of every memory - // page it touches beyond the one containing the block header itself. - // As the underlying storage is often memory mapped from disk or shared - // space, sometimes things go wrong and those address don't actually exist - // leading to a SIGBUS (or Windows equivalent) at some arbitrary location - // in the code. This should concentrate all those failures into this - // location for easy tracking and, eventually, proper handling. - volatile char* mem_end = reinterpret_cast(block) + size; - volatile char* mem_begin = reinterpret_cast( - (reinterpret_cast(block) + sizeof(BlockHeader) + - (vm_page_size_ - 1)) & - ~static_cast(vm_page_size_ - 1)); - for (volatile char* memory = mem_begin; memory < mem_end; - memory += vm_page_size_) { - // It's required that a memory segment start as all zeros and thus the - // newly allocated block is all zeros at this point. Thus, writing a - // zero to it allows testing that the memory exists without actually - // changing its contents. The compiler doesn't know about the requirement - // and so cannot optimize-away these writes. - *memory = 0; - } - - // Load information into the block header. There is no "release" of the - // data here because this memory can, currently, be seen only by the thread - // performing the allocation. When it comes time to share this, the thread - // will call MakeIterable() which does the release operation. - block->size = size; - block->cookie = kBlockCookieAllocated; - block->type_id.store(type_id, std::memory_order_relaxed); - return freeptr; - } -} - -void PersistentMemoryAllocator::GetMemoryInfo(MemoryInfo* meminfo) const { - uint32_t remaining = std::max( - mem_size_ - shared_meta()->freeptr.load(std::memory_order_relaxed), - (uint32_t)sizeof(BlockHeader)); - meminfo->total = mem_size_; - meminfo->free = remaining - sizeof(BlockHeader); -} - -void PersistentMemoryAllocator::MakeIterable(Reference ref) { - DCHECK(!readonly_); - if (IsCorrupt()) - return; - volatile BlockHeader* block = GetBlock(ref, 0, 0, false, false); - if (!block) // invalid reference - return; - if (block->next.load(std::memory_order_acquire) != 0) // Already iterable. - return; - block->next.store(kReferenceQueue, std::memory_order_release); // New tail. - - // Try to add this block to the tail of the queue. May take multiple tries. - // If so, tail will be automatically updated with a more recent value during - // compare-exchange operations. - uint32_t tail = shared_meta()->tailptr.load(std::memory_order_acquire); - for (;;) { - // Acquire the current tail-pointer released by previous call to this - // method and validate it. - block = GetBlock(tail, 0, 0, true, false); - if (!block) { - SetCorrupt(); - return; - } - - // Try to insert the block at the tail of the queue. The tail node always - // has an existing value of kReferenceQueue; if that is somehow not the - // existing value then another thread has acted in the meantime. A "strong" - // exchange is necessary so the "else" block does not get executed when - // that is not actually the case (which can happen with a "weak" exchange). - uint32_t next = kReferenceQueue; // Will get replaced with existing value. - if (block->next.compare_exchange_strong(next, ref, - std::memory_order_acq_rel, - std::memory_order_acquire)) { - // Update the tail pointer to the new offset. If the "else" clause did - // not exist, then this could be a simple Release_Store to set the new - // value but because it does, it's possible that other threads could add - // one or more nodes at the tail before reaching this point. We don't - // have to check the return value because it either operates correctly - // or the exact same operation has already been done (by the "else" - // clause) on some other thread. - shared_meta()->tailptr.compare_exchange_strong(tail, ref, - std::memory_order_release, - std::memory_order_relaxed); - return; - } else { - // In the unlikely case that a thread crashed or was killed between the - // update of "next" and the update of "tailptr", it is necessary to - // perform the operation that would have been done. There's no explicit - // check for crash/kill which means that this operation may also happen - // even when the other thread is in perfect working order which is what - // necessitates the CompareAndSwap above. - shared_meta()->tailptr.compare_exchange_strong(tail, next, - std::memory_order_acq_rel, - std::memory_order_acquire); - } - } -} - -// The "corrupted" state is held both locally and globally (shared). The -// shared flag can't be trusted since a malicious actor could overwrite it. -// Because corruption can be detected during read-only operations such as -// iteration, this method may be called by other "const" methods. In this -// case, it's safe to discard the constness and modify the local flag and -// maybe even the shared flag if the underlying data isn't actually read-only. -void PersistentMemoryAllocator::SetCorrupt() const { - if (!corrupt_.load(std::memory_order_relaxed) && - !CheckFlag( - const_cast*>(&shared_meta()->flags), - kFlagCorrupt)) { - LOG(ERROR) << "Corruption detected in shared-memory segment."; - RecordError(kMemoryIsCorrupt); - } - - corrupt_.store(true, std::memory_order_relaxed); - if (!readonly_) { - SetFlag(const_cast*>(&shared_meta()->flags), - kFlagCorrupt); - } -} - -bool PersistentMemoryAllocator::IsCorrupt() const { - if (corrupt_.load(std::memory_order_relaxed) || - CheckFlag(&shared_meta()->flags, kFlagCorrupt)) { - SetCorrupt(); // Make sure all indicators are set. - return true; - } - return false; -} - -bool PersistentMemoryAllocator::IsFull() const { - return CheckFlag(&shared_meta()->flags, kFlagFull); -} - -// Dereference a block |ref| and ensure that it's valid for the desired -// |type_id| and |size|. |special| indicates that we may try to access block -// headers not available to callers but still accessed by this module. By -// having internal dereferences go through this same function, the allocator -// is hardened against corruption. -const volatile PersistentMemoryAllocator::BlockHeader* -PersistentMemoryAllocator::GetBlock(Reference ref, uint32_t type_id, - uint32_t size, bool queue_ok, - bool free_ok) const { - // Handle special cases. - if (ref == kReferenceQueue && queue_ok) - return reinterpret_cast(mem_base_ + ref); - - // Validation of parameters. - if (ref < sizeof(SharedMetadata)) - return nullptr; - if (ref % kAllocAlignment != 0) - return nullptr; - size += sizeof(BlockHeader); - if (ref + size > mem_size_) - return nullptr; - - // Validation of referenced block-header. - if (!free_ok) { - const volatile BlockHeader* const block = - reinterpret_cast(mem_base_ + ref); - if (block->cookie != kBlockCookieAllocated) - return nullptr; - if (block->size < size) - return nullptr; - if (ref + block->size > mem_size_) - return nullptr; - if (type_id != 0 && - block->type_id.load(std::memory_order_relaxed) != type_id) { - return nullptr; - } - } - - // Return pointer to block data. - return reinterpret_cast(mem_base_ + ref); -} - -void PersistentMemoryAllocator::FlushPartial(size_t length, bool sync) { - // Generally there is nothing to do as every write is done through volatile - // memory with atomic instructions to guarantee consistency. This (virtual) - // method exists so that derivced classes can do special things, such as - // tell the OS to write changes to disk now rather than when convenient. -} - -void PersistentMemoryAllocator::RecordError(int error) const { - if (errors_histogram_) - errors_histogram_->Add(error); -} - -const volatile void* PersistentMemoryAllocator::GetBlockData( - Reference ref, - uint32_t type_id, - uint32_t size) const { - DCHECK(size > 0); - const volatile BlockHeader* block = - GetBlock(ref, type_id, size, false, false); - if (!block) - return nullptr; - return reinterpret_cast(block) + sizeof(BlockHeader); -} - -void PersistentMemoryAllocator::UpdateTrackingHistograms() { - DCHECK(!readonly_); - if (used_histogram_) { - MemoryInfo meminfo; - GetMemoryInfo(&meminfo); - HistogramBase::Sample used_percent = static_cast( - ((meminfo.total - meminfo.free) * 100ULL / meminfo.total)); - used_histogram_->Add(used_percent); - } -} - - -//----- LocalPersistentMemoryAllocator ----------------------------------------- - -LocalPersistentMemoryAllocator::LocalPersistentMemoryAllocator( - size_t size, - uint64_t id, - base::StringPiece name) - : PersistentMemoryAllocator(AllocateLocalMemory(size), - size, 0, id, name, false) {} - -LocalPersistentMemoryAllocator::~LocalPersistentMemoryAllocator() { - DeallocateLocalMemory(const_cast(mem_base_), mem_size_, mem_type_); -} - -// static -PersistentMemoryAllocator::Memory -LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size) { - void* address; - -#if defined(OS_WIN) - address = - ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); - if (address) - return Memory(address, MEM_VIRTUAL); - UmaHistogramSparse("UMA.LocalPersistentMemoryAllocator.Failures.Win", - ::GetLastError()); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // MAP_ANON is deprecated on Linux but MAP_ANONYMOUS is not universal on Mac. - // MAP_SHARED is not available on Linux <2.4 but required on Mac. - address = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_SHARED, -1, 0); - if (address != MAP_FAILED) - return Memory(address, MEM_VIRTUAL); - UmaHistogramSparse("UMA.LocalPersistentMemoryAllocator.Failures.Posix", - errno); -#else -#error This architecture is not (yet) supported. -#endif - - // As a last resort, just allocate the memory from the heap. This will - // achieve the same basic result but the acquired memory has to be - // explicitly zeroed and thus realized immediately (i.e. all pages are - // added to the process now istead of only when first accessed). - address = malloc(size); - DPCHECK(address); - memset(address, 0, size); - return Memory(address, MEM_MALLOC); -} - -// static -void LocalPersistentMemoryAllocator::DeallocateLocalMemory(void* memory, - size_t size, - MemoryType type) { - if (type == MEM_MALLOC) { - free(memory); - return; - } - - DCHECK_EQ(MEM_VIRTUAL, type); -#if defined(OS_WIN) - BOOL success = ::VirtualFree(memory, 0, MEM_DECOMMIT); - DCHECK(success); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - int result = ::munmap(memory, size); - DCHECK_EQ(0, result); -#else -#error This architecture is not (yet) supported. -#endif -} - - -//----- SharedPersistentMemoryAllocator ---------------------------------------- - -SharedPersistentMemoryAllocator::SharedPersistentMemoryAllocator( - std::unique_ptr memory, - uint64_t id, - base::StringPiece name, - bool read_only) - : PersistentMemoryAllocator( - Memory(static_cast(memory->memory()), MEM_SHARED), - memory->mapped_size(), - 0, - id, - name, - read_only), - shared_memory_(std::move(memory)) {} - -SharedPersistentMemoryAllocator::~SharedPersistentMemoryAllocator() = default; - -// static -bool SharedPersistentMemoryAllocator::IsSharedMemoryAcceptable( - const SharedMemory& memory) { - return IsMemoryAcceptable(memory.memory(), memory.mapped_size(), 0, false); -} - - -#if !defined(OS_NACL) -//----- FilePersistentMemoryAllocator ------------------------------------------ - -FilePersistentMemoryAllocator::FilePersistentMemoryAllocator( - std::unique_ptr file, - size_t max_size, - uint64_t id, - base::StringPiece name, - bool read_only) - : PersistentMemoryAllocator( - Memory(const_cast(file->data()), MEM_FILE), - max_size != 0 ? max_size : file->length(), - 0, - id, - name, - read_only), - mapped_file_(std::move(file)) {} - -FilePersistentMemoryAllocator::~FilePersistentMemoryAllocator() = default; - -// static -bool FilePersistentMemoryAllocator::IsFileAcceptable( - const MemoryMappedFile& file, - bool read_only) { - return IsMemoryAcceptable(file.data(), file.length(), 0, read_only); -} - -void FilePersistentMemoryAllocator::FlushPartial(size_t length, bool sync) { - if (sync) - AssertBlockingAllowed(); - if (IsReadonly()) - return; - -#if defined(OS_WIN) - // Windows doesn't support asynchronous flush. - AssertBlockingAllowed(); - BOOL success = ::FlushViewOfFile(data(), length); - DPCHECK(success); -#elif defined(OS_MACOSX) - // On OSX, "invalidate" removes all cached pages, forcing a re-read from - // disk. That's not applicable to "flush" so omit it. - int result = - ::msync(const_cast(data()), length, sync ? MS_SYNC : MS_ASYNC); - DCHECK_NE(EINVAL, result); -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - // On POSIX, "invalidate" forces _other_ processes to recognize what has - // been written to disk and so is applicable to "flush". - int result = ::msync(const_cast(data()), length, - MS_INVALIDATE | (sync ? MS_SYNC : MS_ASYNC)); - DCHECK_NE(EINVAL, result); -#else -#error Unsupported OS. -#endif -} -#endif // !defined(OS_NACL) - -//----- DelayedPersistentAllocation -------------------------------------------- - -// Forwarding constructors. -DelayedPersistentAllocation::DelayedPersistentAllocation( - PersistentMemoryAllocator* allocator, - subtle::Atomic32* ref, - uint32_t type, - size_t size, - bool make_iterable) - : DelayedPersistentAllocation( - allocator, - reinterpret_cast*>(ref), - type, - size, - 0, - make_iterable) {} - -DelayedPersistentAllocation::DelayedPersistentAllocation( - PersistentMemoryAllocator* allocator, - subtle::Atomic32* ref, - uint32_t type, - size_t size, - size_t offset, - bool make_iterable) - : DelayedPersistentAllocation( - allocator, - reinterpret_cast*>(ref), - type, - size, - offset, - make_iterable) {} - -DelayedPersistentAllocation::DelayedPersistentAllocation( - PersistentMemoryAllocator* allocator, - std::atomic* ref, - uint32_t type, - size_t size, - bool make_iterable) - : DelayedPersistentAllocation(allocator, - ref, - type, - size, - 0, - make_iterable) {} - -// Real constructor. -DelayedPersistentAllocation::DelayedPersistentAllocation( - PersistentMemoryAllocator* allocator, - std::atomic* ref, - uint32_t type, - size_t size, - size_t offset, - bool make_iterable) - : allocator_(allocator), - type_(type), - size_(checked_cast(size)), - offset_(checked_cast(offset)), - make_iterable_(make_iterable), - reference_(ref) { - DCHECK(allocator_); - DCHECK_NE(0U, type_); - DCHECK_LT(0U, size_); - DCHECK(reference_); -} - -DelayedPersistentAllocation::~DelayedPersistentAllocation() = default; - -void* DelayedPersistentAllocation::Get() const { - // Relaxed operations are acceptable here because it's not protecting the - // contents of the allocation in any way. - Reference ref = reference_->load(std::memory_order_acquire); - if (!ref) { - ref = allocator_->Allocate(size_, type_); - if (!ref) - return nullptr; - - // Store the new reference in its proper location using compare-and-swap. - // Use a "strong" exchange to ensure no false-negatives since the operation - // cannot be retried. - Reference existing = 0; // Must be mutable; receives actual value. - if (reference_->compare_exchange_strong(existing, ref, - std::memory_order_release, - std::memory_order_relaxed)) { - if (make_iterable_) - allocator_->MakeIterable(ref); - } else { - // Failure indicates that something else has raced ahead, performed the - // allocation, and stored its reference. Purge the allocation that was - // just done and use the other one instead. - DCHECK_EQ(type_, allocator_->GetType(existing)); - DCHECK_LE(size_, allocator_->GetAllocSize(existing)); - allocator_->ChangeType(ref, 0, type_, /*clear=*/false); - ref = existing; - } - } - - char* mem = allocator_->GetAsArray(ref, type_, size_); - if (!mem) { - // This should never happen but be tolerant if it does as corruption from - // the outside is something to guard against. - NOTREACHED(); - return nullptr; - } - return mem + offset_; -} - -} // namespace base diff --git a/metrics/persistent_memory_allocator.h b/metrics/persistent_memory_allocator.h deleted file mode 100644 index 978a362cd..000000000 --- a/metrics/persistent_memory_allocator.h +++ /dev/null @@ -1,872 +0,0 @@ -// Copyright (c) 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_ -#define BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_ - -#include - -#include -#include -#include - -#include "base/atomicops.h" -#include "base/base_export.h" -#include "base/files/file_path.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/strings/string_piece.h" - -namespace base { - -class HistogramBase; -class MemoryMappedFile; -class SharedMemory; - -// Simple allocator for pieces of a memory block that may be persistent -// to some storage or shared across multiple processes. This class resides -// under base/metrics because it was written for that purpose. It is, -// however, fully general-purpose and can be freely moved to base/memory -// if other uses are found. -// -// This class provides for thread-secure (i.e. safe against other threads -// or processes that may be compromised and thus have malicious intent) -// allocation of memory within a designated block and also a mechanism by -// which other threads can learn of these allocations. -// -// There is (currently) no way to release an allocated block of data because -// doing so would risk invalidating pointers held by other processes and -// greatly complicate the allocation algorithm. -// -// Construction of this object can accept new, clean (i.e. zeroed) memory -// or previously initialized memory. In the first case, construction must -// be allowed to complete before letting other allocators attach to the same -// segment. In other words, don't share the segment until at least one -// allocator has been attached to it. -// -// Note that memory not in active use is not accessed so it is possible to -// use virtual memory, including memory-mapped files, as backing storage with -// the OS "pinning" new (zeroed) physical RAM pages only as they are needed. -// -// OBJECTS: Although the allocator can be used in a "malloc" sense, fetching -// character arrays and manipulating that memory manually, the better way is -// generally to use the "object" methods to create and manage allocations. In -// this way the sizing, type-checking, and construction are all automatic. For -// this to work, however, every type of stored object must define two public -// "constexpr" values, kPersistentTypeId and kExpectedInstanceSize, as such: -// -// struct MyPersistentObjectType { -// // SHA1(MyPersistentObjectType): Increment this if structure changes! -// static constexpr uint32_t kPersistentTypeId = 0x3E15F6DE + 1; -// -// // Expected size for 32/64-bit check. Update this if structure changes! -// static constexpr size_t kExpectedInstanceSize = 20; -// -// ... -// }; -// -// kPersistentTypeId: This value is an arbitrary identifier that allows the -// identification of these objects in the allocator, including the ability -// to find them via iteration. The number is arbitrary but using the first -// four bytes of the SHA1 hash of the type name means that there shouldn't -// be any conflicts with other types that may also be stored in the memory. -// The fully qualified name (e.g. base::debug::MyPersistentObjectType) could -// be used to generate the hash if the type name seems common. Use a command -// like this to get the hash: echo -n "MyPersistentObjectType" | sha1sum -// If the structure layout changes, ALWAYS increment this number so that -// newer versions of the code don't try to interpret persistent data written -// by older versions with a different layout. -// -// kExpectedInstanceSize: This value is the hard-coded number that matches -// what sizeof(T) would return. By providing it explicitly, the allocator can -// verify that the structure is compatible between both 32-bit and 64-bit -// versions of the code. -// -// Using New manages the memory and then calls the default constructor for the -// object. Given that objects are persistent, no destructor is ever called -// automatically though a caller can explicitly call Delete to destruct it and -// change the type to something indicating it is no longer in use. -// -// Though persistent memory segments are transferrable between programs built -// for different natural word widths, they CANNOT be exchanged between CPUs -// of different endianess. Attempts to do so will simply see the existing data -// as corrupt and refuse to access any of it. -class BASE_EXPORT PersistentMemoryAllocator { - public: - typedef uint32_t Reference; - - // These states are used to indicate the overall condition of the memory - // segment irrespective of what is stored within it. Because the data is - // often persistent and thus needs to be readable by different versions of - // a program, these values are fixed and can never change. - enum MemoryState : uint8_t { - // Persistent memory starts all zeros and so shows "uninitialized". - MEMORY_UNINITIALIZED = 0, - - // The header has been written and the memory is ready for use. - MEMORY_INITIALIZED = 1, - - // The data should be considered deleted. This would be set when the - // allocator is being cleaned up. If file-backed, the file is likely - // to be deleted but since deletion can fail for a variety of reasons, - // having this extra status means a future reader can realize what - // should have happened. - MEMORY_DELETED = 2, - - // Outside code can create states starting with this number; these too - // must also never change between code versions. - MEMORY_USER_DEFINED = 100, - }; - - // Iterator for going through all iterable memory records in an allocator. - // Like the allocator itself, iterators are lock-free and thread-secure. - // That means that multiple threads can share an iterator and the same - // reference will not be returned twice. - // - // The order of the items returned by an iterator matches the order in which - // MakeIterable() was called on them. Once an allocation is made iterable, - // it is always such so the only possible difference between successive - // iterations is for more to be added to the end. - // - // Iteration, in general, is tolerant of corrupted memory. It will return - // what it can and stop only when corruption forces it to. Bad corruption - // could cause the same object to be returned many times but it will - // eventually quit. - class BASE_EXPORT Iterator { - public: - // Constructs an iterator on a given |allocator|, starting at the beginning. - // The allocator must live beyond the lifetime of the iterator. This class - // has read-only access to the allocator (hence "const") but the returned - // references can be used on a read/write version, too. - explicit Iterator(const PersistentMemoryAllocator* allocator); - - // As above but resuming from the |starting_after| reference. The first call - // to GetNext() will return the next object found after that reference. The - // reference must be to an "iterable" object; references to non-iterable - // objects (those that never had MakeIterable() called for them) will cause - // a run-time error. - Iterator(const PersistentMemoryAllocator* allocator, - Reference starting_after); - - // Resets the iterator back to the beginning. - void Reset(); - - // Resets the iterator, resuming from the |starting_after| reference. - void Reset(Reference starting_after); - - // Returns the previously retrieved reference, or kReferenceNull if none. - // If constructor or reset with a starting_after location, this will return - // that value. - Reference GetLast(); - - // Gets the next iterable, storing that type in |type_return|. The actual - // return value is a reference to the allocation inside the allocator or - // zero if there are no more. GetNext() may still be called again at a - // later time to retrieve any new allocations that have been added. - Reference GetNext(uint32_t* type_return); - - // Similar to above but gets the next iterable of a specific |type_match|. - // This should not be mixed with calls to GetNext() because any allocations - // skipped here due to a type mis-match will never be returned by later - // calls to GetNext() meaning it's possible to completely miss entries. - Reference GetNextOfType(uint32_t type_match); - - // As above but works using object type. - template - Reference GetNextOfType() { - return GetNextOfType(T::kPersistentTypeId); - } - - // As above but works using objects and returns null if not found. - template - const T* GetNextOfObject() { - return GetAsObject(GetNextOfType()); - } - - // Converts references to objects. This is a convenience method so that - // users of the iterator don't need to also have their own pointer to the - // allocator over which the iterator runs in order to retrieve objects. - // Because the iterator is not read/write, only "const" objects can be - // fetched. Non-const objects can be fetched using the reference on a - // non-const (external) pointer to the same allocator (or use const_cast - // to remove the qualifier). - template - const T* GetAsObject(Reference ref) const { - return allocator_->GetAsObject(ref); - } - - // Similar to GetAsObject() but converts references to arrays of things. - template - const T* GetAsArray(Reference ref, uint32_t type_id, size_t count) const { - return allocator_->GetAsArray(ref, type_id, count); - } - - // Convert a generic pointer back into a reference. A null reference will - // be returned if |memory| is not inside the persistent segment or does not - // point to an object of the specified |type_id|. - Reference GetAsReference(const void* memory, uint32_t type_id) const { - return allocator_->GetAsReference(memory, type_id); - } - - // As above but convert an object back into a reference. - template - Reference GetAsReference(const T* obj) const { - return allocator_->GetAsReference(obj); - } - - private: - // Weak-pointer to memory allocator being iterated over. - const PersistentMemoryAllocator* allocator_; - - // The last record that was returned. - std::atomic last_record_; - - // The number of records found; used for detecting loops. - std::atomic record_count_; - - DISALLOW_COPY_AND_ASSIGN(Iterator); - }; - - // Returned information about the internal state of the heap. - struct MemoryInfo { - size_t total; - size_t free; - }; - - enum : Reference { - // A common "null" reference value. - kReferenceNull = 0, - }; - - enum : uint32_t { - // A value that will match any type when doing lookups. - kTypeIdAny = 0x00000000, - - // A value indicating that the type is in transition. Work is being done - // on the contents to prepare it for a new type to come. - kTypeIdTransitioning = 0xFFFFFFFF, - }; - - enum : size_t { - kSizeAny = 1 // Constant indicating that any array size is acceptable. - }; - - // This is the standard file extension (suitable for being passed to the - // AddExtension() method of base::FilePath) for dumps of persistent memory. - static const base::FilePath::CharType kFileExtension[]; - - // The allocator operates on any arbitrary block of memory. Creation and - // persisting or sharing of that block with another process is the - // responsibility of the caller. The allocator needs to know only the - // block's |base| address, the total |size| of the block, and any internal - // |page| size (zero if not paged) across which allocations should not span. - // The |id| is an arbitrary value the caller can use to identify a - // particular memory segment. It will only be loaded during the initial - // creation of the segment and can be checked by the caller for consistency. - // The |name|, if provided, is used to distinguish histograms for this - // allocator. Only the primary owner of the segment should define this value; - // other processes can learn it from the shared state. If the underlying - // memory is |readonly| then no changes will be made to it. The resulting - // object should be stored as a "const" pointer. - // - // PersistentMemoryAllocator does NOT take ownership of the memory block. - // The caller must manage it and ensure it stays available throughout the - // lifetime of this object. - // - // Memory segments for sharing must have had an allocator attached to them - // before actually being shared. If the memory segment was just created, it - // should be zeroed before being passed here. If it was an existing segment, - // the values here will be compared to copies stored in the shared segment - // as a guard against corruption. - // - // Make sure that the memory segment is acceptable (see IsMemoryAcceptable() - // method below) before construction if the definition of the segment can - // vary in any way at run-time. Invalid memory segments will cause a crash. - PersistentMemoryAllocator(void* base, size_t size, size_t page_size, - uint64_t id, base::StringPiece name, - bool readonly); - virtual ~PersistentMemoryAllocator(); - - // Check if memory segment is acceptable for creation of an Allocator. This - // doesn't do any analysis of the data and so doesn't guarantee that the - // contents are valid, just that the paramaters won't cause the program to - // abort. The IsCorrupt() method will report detection of data problems - // found during construction and general operation. - static bool IsMemoryAcceptable(const void* data, size_t size, - size_t page_size, bool readonly); - - // Get the internal identifier for this persistent memory segment. - uint64_t Id() const; - - // Get the internal name of this allocator (possibly an empty string). - const char* Name() const; - - // Is this segment open only for read? - bool IsReadonly() const { return readonly_; } - - // Manage the saved state of the memory. - void SetMemoryState(uint8_t memory_state); - uint8_t GetMemoryState() const; - - // Create internal histograms for tracking memory use and allocation sizes - // for allocator of |name| (which can simply be the result of Name()). This - // is done seperately from construction for situations such as when the - // histograms will be backed by memory provided by this very allocator. - // - // IMPORTANT: Callers must update tools/metrics/histograms/histograms.xml - // with the following histograms: - // UMA.PersistentAllocator.name.Errors - // UMA.PersistentAllocator.name.UsedPct - void CreateTrackingHistograms(base::StringPiece name); - - // Flushes the persistent memory to any backing store. This typically does - // nothing but is used by the FilePersistentMemoryAllocator to inform the - // OS that all the data should be sent to the disk immediately. This is - // useful in the rare case where something has just been stored that needs - // to survive a hard shutdown of the machine like from a power failure. - // The |sync| parameter indicates if this call should block until the flush - // is complete but is only advisory and may or may not have an effect - // depending on the capabilities of the OS. Synchronous flushes are allowed - // only from theads that are allowed to do I/O but since |sync| is only - // advisory, all flushes should be done on IO-capable threads. - void Flush(bool sync); - - // Direct access to underlying memory segment. If the segment is shared - // across threads or processes, reading data through these values does - // not guarantee consistency. Use with care. Do not write. - const void* data() const { return const_cast(mem_base_); } - size_t length() const { return mem_size_; } - size_t size() const { return mem_size_; } - size_t used() const; - - // Get an object referenced by a |ref|. For safety reasons, the |type_id| - // code and size-of(|T|) are compared to ensure the reference is valid - // and cannot return an object outside of the memory segment. A |type_id| of - // kTypeIdAny (zero) will match any though the size is still checked. NULL is - // returned if any problem is detected, such as corrupted storage or incorrect - // parameters. Callers MUST check that the returned value is not-null EVERY - // TIME before accessing it or risk crashing! Once dereferenced, the pointer - // is safe to reuse forever. - // - // It is essential that the object be of a fixed size. All fields must be of - // a defined type that does not change based on the compiler or the CPU - // natural word size. Acceptable are char, float, double, and (u)intXX_t. - // Unacceptable are int, bool, and wchar_t which are implementation defined - // with regards to their size. - // - // Alignment must also be consistent. A uint64_t after a uint32_t will pad - // differently between 32 and 64 bit architectures. Either put the bigger - // elements first, group smaller elements into blocks the size of larger - // elements, or manually insert padding fields as appropriate for the - // largest architecture, including at the end. - // - // To protected against mistakes, all objects must have the attribute - // |kExpectedInstanceSize| (static constexpr size_t) that is a hard-coded - // numerical value -- NNN, not sizeof(T) -- that can be tested. If the - // instance size is not fixed, at least one build will fail. - // - // If the size of a structure changes, the type-ID used to recognize it - // should also change so later versions of the code don't try to read - // incompatible structures from earlier versions. - // - // NOTE: Though this method will guarantee that an object of the specified - // type can be accessed without going outside the bounds of the memory - // segment, it makes no guarantees of the validity of the data within the - // object itself. If it is expected that the contents of the segment could - // be compromised with malicious intent, the object must be hardened as well. - // - // Though the persistent data may be "volatile" if it is shared with - // other processes, such is not necessarily the case. The internal - // "volatile" designation is discarded so as to not propagate the viral - // nature of that keyword to the caller. It can add it back, if necessary, - // based on knowledge of how the allocator is being used. - template - T* GetAsObject(Reference ref) { - static_assert(std::is_standard_layout::value, "only standard objects"); - static_assert(!std::is_array::value, "use GetAsArray<>()"); - static_assert(T::kExpectedInstanceSize == sizeof(T), "inconsistent size"); - return const_cast(reinterpret_cast( - GetBlockData(ref, T::kPersistentTypeId, sizeof(T)))); - } - template - const T* GetAsObject(Reference ref) const { - static_assert(std::is_standard_layout::value, "only standard objects"); - static_assert(!std::is_array::value, "use GetAsArray<>()"); - static_assert(T::kExpectedInstanceSize == sizeof(T), "inconsistent size"); - return const_cast(reinterpret_cast( - GetBlockData(ref, T::kPersistentTypeId, sizeof(T)))); - } - - // Like GetAsObject but get an array of simple, fixed-size types. - // - // Use a |count| of the required number of array elements, or kSizeAny. - // GetAllocSize() can be used to calculate the upper bound but isn't reliable - // because padding can make space for extra elements that were not written. - // - // Remember that an array of char is a string but may not be NUL terminated. - // - // There are no compile-time or run-time checks to ensure 32/64-bit size - // compatibilty when using these accessors. Only use fixed-size types such - // as char, float, double, or (u)intXX_t. - template - T* GetAsArray(Reference ref, uint32_t type_id, size_t count) { - static_assert(std::is_fundamental::value, "use GetAsObject<>()"); - return const_cast(reinterpret_cast( - GetBlockData(ref, type_id, count * sizeof(T)))); - } - template - const T* GetAsArray(Reference ref, uint32_t type_id, size_t count) const { - static_assert(std::is_fundamental::value, "use GetAsObject<>()"); - return const_cast(reinterpret_cast( - GetBlockData(ref, type_id, count * sizeof(T)))); - } - - // Get the corresponding reference for an object held in persistent memory. - // If the |memory| is not valid or the type does not match, a kReferenceNull - // result will be returned. - Reference GetAsReference(const void* memory, uint32_t type_id) const; - - // Get the number of bytes allocated to a block. This is useful when storing - // arrays in order to validate the ending boundary. The returned value will - // include any padding added to achieve the required alignment and so could - // be larger than given in the original Allocate() request. - size_t GetAllocSize(Reference ref) const; - - // Access the internal "type" of an object. This generally isn't necessary - // but can be used to "clear" the type and so effectively mark it as deleted - // even though the memory stays valid and allocated. Changing the type is - // an atomic compare/exchange and so requires knowing the existing value. - // It will return false if the existing type is not what is expected. - // - // Changing the type doesn't mean the data is compatible with the new type. - // Passing true for |clear| will zero the memory after the type has been - // changed away from |from_type_id| but before it becomes |to_type_id| meaning - // that it is done in a manner that is thread-safe. Memory is guaranteed to - // be zeroed atomically by machine-word in a monotonically increasing order. - // - // It will likely be necessary to reconstruct the type before it can be used. - // Changing the type WILL NOT invalidate existing pointers to the data, either - // in this process or others, so changing the data structure could have - // unpredicatable results. USE WITH CARE! - uint32_t GetType(Reference ref) const; - bool ChangeType(Reference ref, - uint32_t to_type_id, - uint32_t from_type_id, - bool clear); - - // Allocated objects can be added to an internal list that can then be - // iterated over by other processes. If an allocated object can be found - // another way, such as by having its reference within a different object - // that will be made iterable, then this call is not necessary. This always - // succeeds unless corruption is detected; check IsCorrupted() to find out. - // Once an object is made iterable, its position in iteration can never - // change; new iterable objects will always be added after it in the series. - // Changing the type does not alter its "iterable" status. - void MakeIterable(Reference ref); - - // Get the information about the amount of free space in the allocator. The - // amount of free space should be treated as approximate due to extras from - // alignment and metadata. Concurrent allocations from other threads will - // also make the true amount less than what is reported. - void GetMemoryInfo(MemoryInfo* meminfo) const; - - // If there is some indication that the memory has become corrupted, - // calling this will attempt to prevent further damage by indicating to - // all processes that something is not as expected. - void SetCorrupt() const; - - // This can be called to determine if corruption has been detected in the - // segment, possibly my a malicious actor. Once detected, future allocations - // will fail and iteration may not locate all objects. - bool IsCorrupt() const; - - // Flag set if an allocation has failed because the memory segment was full. - bool IsFull() const; - - // Update those "tracking" histograms which do not get updates during regular - // operation, such as how much memory is currently used. This should be - // called before such information is to be displayed or uploaded. - void UpdateTrackingHistograms(); - - // While the above works much like malloc & free, these next methods provide - // an "object" interface similar to new and delete. - - // Reserve space in the memory segment of the desired |size| and |type_id|. - // A return value of zero indicates the allocation failed, otherwise the - // returned reference can be used by any process to get a real pointer via - // the GetAsObject() or GetAsArray calls. The actual allocated size may be - // larger and will always be a multiple of 8 bytes (64 bits). - Reference Allocate(size_t size, uint32_t type_id); - - // Allocate and construct an object in persistent memory. The type must have - // both (size_t) kExpectedInstanceSize and (uint32_t) kPersistentTypeId - // static constexpr fields that are used to ensure compatibility between - // software versions. An optional size parameter can be specified to force - // the allocation to be bigger than the size of the object; this is useful - // when the last field is actually variable length. - template - T* New(size_t size) { - if (size < sizeof(T)) - size = sizeof(T); - Reference ref = Allocate(size, T::kPersistentTypeId); - void* mem = - const_cast(GetBlockData(ref, T::kPersistentTypeId, size)); - if (!mem) - return nullptr; - DCHECK_EQ(0U, reinterpret_cast(mem) & (alignof(T) - 1)); - return new (mem) T(); - } - template - T* New() { - return New(sizeof(T)); - } - - // Similar to New, above, but construct the object out of an existing memory - // block and of an expected type. If |clear| is true, memory will be zeroed - // before construction. Though this is not standard object behavior, it - // is present to match with new allocations that always come from zeroed - // memory. Anything previously present simply ceases to exist; no destructor - // is called for it so explicitly Delete() the old object first if need be. - // Calling this will not invalidate existing pointers to the object, either - // in this process or others, so changing the object could have unpredictable - // results. USE WITH CARE! - template - T* New(Reference ref, uint32_t from_type_id, bool clear) { - DCHECK_LE(sizeof(T), GetAllocSize(ref)) << "alloc not big enough for obj"; - // Make sure the memory is appropriate. This won't be used until after - // the type is changed but checking first avoids the possibility of having - // to change the type back. - void* mem = const_cast(GetBlockData(ref, 0, sizeof(T))); - if (!mem) - return nullptr; - // Ensure the allocator's internal alignment is sufficient for this object. - // This protects against coding errors in the allocator. - DCHECK_EQ(0U, reinterpret_cast(mem) & (alignof(T) - 1)); - // Change the type, clearing the memory if so desired. The new type is - // "transitioning" so that there is no race condition with the construction - // of the object should another thread be simultaneously iterating over - // data. This will "acquire" the memory so no changes get reordered before - // it. - if (!ChangeType(ref, kTypeIdTransitioning, from_type_id, clear)) - return nullptr; - // Construct an object of the desired type on this memory, just as if - // New() had been called to create it. - T* obj = new (mem) T(); - // Finally change the type to the desired one. This will "release" all of - // the changes above and so provide a consistent view to other threads. - bool success = - ChangeType(ref, T::kPersistentTypeId, kTypeIdTransitioning, false); - DCHECK(success); - return obj; - } - - // Deletes an object by destructing it and then changing the type to a - // different value (default 0). - template - void Delete(T* obj, uint32_t new_type) { - // Get the reference for the object. - Reference ref = GetAsReference(obj); - // First change the type to "transitioning" so there is no race condition - // where another thread could find the object through iteration while it - // is been destructed. This will "acquire" the memory so no changes get - // reordered before it. It will fail if |ref| is invalid. - if (!ChangeType(ref, kTypeIdTransitioning, T::kPersistentTypeId, false)) - return; - // Destruct the object. - obj->~T(); - // Finally change the type to the desired value. This will "release" all - // the changes above. - bool success = ChangeType(ref, new_type, kTypeIdTransitioning, false); - DCHECK(success); - } - template - void Delete(T* obj) { - Delete(obj, 0); - } - - // As above but works with objects allocated from persistent memory. - template - Reference GetAsReference(const T* obj) const { - return GetAsReference(obj, T::kPersistentTypeId); - } - - // As above but works with an object allocated from persistent memory. - template - void MakeIterable(const T* obj) { - MakeIterable(GetAsReference(obj)); - } - - protected: - enum MemoryType { - MEM_EXTERNAL, - MEM_MALLOC, - MEM_VIRTUAL, - MEM_SHARED, - MEM_FILE, - }; - - struct Memory { - Memory(void* b, MemoryType t) : base(b), type(t) {} - - void* base; - MemoryType type; - }; - - // Constructs the allocator. Everything is the same as the public allocator - // except |memory| which is a structure with additional information besides - // the base address. - PersistentMemoryAllocator(Memory memory, size_t size, size_t page_size, - uint64_t id, base::StringPiece name, - bool readonly); - - // Implementation of Flush that accepts how much to flush. - virtual void FlushPartial(size_t length, bool sync); - - volatile char* const mem_base_; // Memory base. (char so sizeof guaranteed 1) - const MemoryType mem_type_; // Type of memory allocation. - const uint32_t mem_size_; // Size of entire memory segment. - const uint32_t mem_page_; // Page size allocations shouldn't cross. - - private: - struct SharedMetadata; - struct BlockHeader; - static const uint32_t kAllocAlignment; - static const Reference kReferenceQueue; - - // The shared metadata is always located at the top of the memory segment. - // These convenience functions eliminate constant casting of the base - // pointer within the code. - const SharedMetadata* shared_meta() const { - return reinterpret_cast( - const_cast(mem_base_)); - } - SharedMetadata* shared_meta() { - return reinterpret_cast(const_cast(mem_base_)); - } - - // Actual method for doing the allocation. - Reference AllocateImpl(size_t size, uint32_t type_id); - - // Get the block header associated with a specific reference. - const volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id, - uint32_t size, bool queue_ok, - bool free_ok) const; - volatile BlockHeader* GetBlock(Reference ref, uint32_t type_id, uint32_t size, - bool queue_ok, bool free_ok) { - return const_cast( - const_cast(this)->GetBlock( - ref, type_id, size, queue_ok, free_ok)); - } - - // Get the actual data within a block associated with a specific reference. - const volatile void* GetBlockData(Reference ref, uint32_t type_id, - uint32_t size) const; - volatile void* GetBlockData(Reference ref, uint32_t type_id, - uint32_t size) { - return const_cast( - const_cast(this)->GetBlockData( - ref, type_id, size)); - } - - // Record an error in the internal histogram. - void RecordError(int error) const; - - const size_t vm_page_size_; // The page size used by the OS. - const bool readonly_; // Indicates access to read-only memory. - mutable std::atomic corrupt_; // Local version of "corrupted" flag. - - HistogramBase* allocs_histogram_; // Histogram recording allocs. - HistogramBase* used_histogram_; // Histogram recording used space. - HistogramBase* errors_histogram_; // Histogram recording errors. - - friend class PersistentMemoryAllocatorTest; - FRIEND_TEST_ALL_PREFIXES(PersistentMemoryAllocatorTest, AllocateAndIterate); - DISALLOW_COPY_AND_ASSIGN(PersistentMemoryAllocator); -}; - - -// This allocator uses a local memory block it allocates from the general -// heap. It is generally used when some kind of "death rattle" handler will -// save the contents to persistent storage during process shutdown. It is -// also useful for testing. -class BASE_EXPORT LocalPersistentMemoryAllocator - : public PersistentMemoryAllocator { - public: - LocalPersistentMemoryAllocator(size_t size, uint64_t id, - base::StringPiece name); - ~LocalPersistentMemoryAllocator() override; - - private: - // Allocates a block of local memory of the specified |size|, ensuring that - // the memory will not be physically allocated until accessed and will read - // as zero when that happens. - static Memory AllocateLocalMemory(size_t size); - - // Deallocates a block of local |memory| of the specified |size|. - static void DeallocateLocalMemory(void* memory, size_t size, MemoryType type); - - DISALLOW_COPY_AND_ASSIGN(LocalPersistentMemoryAllocator); -}; - - -// This allocator takes a shared-memory object and performs allocation from -// it. The memory must be previously mapped via Map() or MapAt(). The allocator -// takes ownership of the memory object. -class BASE_EXPORT SharedPersistentMemoryAllocator - : public PersistentMemoryAllocator { - public: - SharedPersistentMemoryAllocator(std::unique_ptr memory, - uint64_t id, - base::StringPiece name, - bool read_only); - ~SharedPersistentMemoryAllocator() override; - - SharedMemory* shared_memory() { return shared_memory_.get(); } - - // Ensure that the memory isn't so invalid that it would crash when passing it - // to the allocator. This doesn't guarantee the data is valid, just that it - // won't cause the program to abort. The existing IsCorrupt() call will handle - // the rest. - static bool IsSharedMemoryAcceptable(const SharedMemory& memory); - - private: - std::unique_ptr shared_memory_; - - DISALLOW_COPY_AND_ASSIGN(SharedPersistentMemoryAllocator); -}; - - -#if !defined(OS_NACL) // NACL doesn't support any kind of file access in build. -// This allocator takes a memory-mapped file object and performs allocation -// from it. The allocator takes ownership of the file object. -class BASE_EXPORT FilePersistentMemoryAllocator - : public PersistentMemoryAllocator { - public: - // A |max_size| of zero will use the length of the file as the maximum - // size. The |file| object must have been already created with sufficient - // permissions (read, read/write, or read/write/extend). - FilePersistentMemoryAllocator(std::unique_ptr file, - size_t max_size, - uint64_t id, - base::StringPiece name, - bool read_only); - ~FilePersistentMemoryAllocator() override; - - // Ensure that the file isn't so invalid that it would crash when passing it - // to the allocator. This doesn't guarantee the file is valid, just that it - // won't cause the program to abort. The existing IsCorrupt() call will handle - // the rest. - static bool IsFileAcceptable(const MemoryMappedFile& file, bool read_only); - - protected: - // PersistentMemoryAllocator: - void FlushPartial(size_t length, bool sync) override; - - private: - std::unique_ptr mapped_file_; - - DISALLOW_COPY_AND_ASSIGN(FilePersistentMemoryAllocator); -}; -#endif // !defined(OS_NACL) - -// An allocation that is defined but not executed until required at a later -// time. This allows for potential users of an allocation to be decoupled -// from the logic that defines it. In addition, there can be multiple users -// of the same allocation or any region thereof that are guaranteed to always -// use the same space. It's okay to copy/move these objects. -// -// This is a top-level class instead of an inner class of the PMA so that it -// can be forward-declared in other header files without the need to include -// the full contents of this file. -class BASE_EXPORT DelayedPersistentAllocation { - public: - using Reference = PersistentMemoryAllocator::Reference; - - // Creates a delayed allocation using the specified |allocator|. When - // needed, the memory will be allocated using the specified |type| and - // |size|. If |offset| is given, the returned pointer will be at that - // offset into the segment; this allows combining allocations into a - // single persistent segment to reduce overhead and means an "all or - // nothing" request. Note that |size| is always the total memory size - // and |offset| is just indicating the start of a block within it. If - // |make_iterable| was true, the allocation will made iterable when it - // is created; already existing allocations are not changed. - // - // Once allocated, a reference to the segment will be stored at |ref|. - // This shared location must be initialized to zero (0); it is checked - // with every Get() request to see if the allocation has already been - // done. If reading |ref| outside of this object, be sure to do an - // "acquire" load. Don't write to it -- leave that to this object. - // - // For convenience, methods taking both Atomic32 and std::atomic - // are defined. - DelayedPersistentAllocation(PersistentMemoryAllocator* allocator, - subtle::Atomic32* ref, - uint32_t type, - size_t size, - bool make_iterable); - DelayedPersistentAllocation(PersistentMemoryAllocator* allocator, - subtle::Atomic32* ref, - uint32_t type, - size_t size, - size_t offset, - bool make_iterable); - DelayedPersistentAllocation(PersistentMemoryAllocator* allocator, - std::atomic* ref, - uint32_t type, - size_t size, - bool make_iterable); - DelayedPersistentAllocation(PersistentMemoryAllocator* allocator, - std::atomic* ref, - uint32_t type, - size_t size, - size_t offset, - bool make_iterable); - ~DelayedPersistentAllocation(); - - // Gets a pointer to the defined allocation. This will realize the request - // and update the reference provided during construction. The memory will - // be zeroed the first time it is returned, after that it is shared with - // all other Get() requests and so shows any changes made to it elsewhere. - // - // If the allocation fails for any reason, null will be returned. This works - // even on "const" objects because the allocation is already defined, just - // delayed. - void* Get() const; - - // Gets the internal reference value. If this returns a non-zero value then - // a subsequent call to Get() will do nothing but convert that reference into - // a memory location -- useful for accessing an existing allocation without - // creating one unnecessarily. - Reference reference() const { - return reference_->load(std::memory_order_relaxed); - } - - private: - // The underlying object that does the actual allocation of memory. Its - // lifetime must exceed that of all DelayedPersistentAllocation objects - // that use it. - PersistentMemoryAllocator* const allocator_; - - // The desired type and size of the allocated segment plus the offset - // within it for the defined request. - const uint32_t type_; - const uint32_t size_; - const uint32_t offset_; - - // Flag indicating if allocation should be made iterable when done. - const bool make_iterable_; - - // The location at which a reference to the allocated segment is to be - // stored once the allocation is complete. If multiple delayed allocations - // share the same pointer then an allocation on one will amount to an - // allocation for all. - volatile std::atomic* const reference_; - - // No DISALLOW_COPY_AND_ASSIGN as it's okay to copy/move these objects. -}; - -} // namespace base - -#endif // BASE_METRICS_PERSISTENT_MEMORY_ALLOCATOR_H_ diff --git a/metrics/persistent_memory_allocator_unittest.cc b/metrics/persistent_memory_allocator_unittest.cc deleted file mode 100644 index 75e4faa76..000000000 --- a/metrics/persistent_memory_allocator_unittest.cc +++ /dev/null @@ -1,1001 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/metrics/persistent_memory_allocator.h" - -#include - -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/files/memory_mapped_file.h" -#include "base/files/scoped_temp_dir.h" -#include "base/memory/shared_memory.h" -#include "base/metrics/histogram.h" -#include "base/rand_util.h" -#include "base/strings/safe_sprintf.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/condition_variable.h" -#include "base/synchronization/lock.h" -#include "base/threading/simple_thread.h" -#include "testing/gmock/include/gmock/gmock.h" - -namespace base { - -namespace { - -const uint32_t TEST_MEMORY_SIZE = 1 << 20; // 1 MiB -const uint32_t TEST_MEMORY_PAGE = 64 << 10; // 64 KiB -const uint32_t TEST_ID = 12345; -const char TEST_NAME[] = "TestAllocator"; - -void SetFileLength(const base::FilePath& path, size_t length) { - { - File file(path, File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE); - DCHECK(file.IsValid()); - ASSERT_TRUE(file.SetLength(static_cast(length))); - } - - int64_t actual_length; - DCHECK(GetFileSize(path, &actual_length)); - DCHECK_EQ(length, static_cast(actual_length)); -} - -} // namespace - -typedef PersistentMemoryAllocator::Reference Reference; - -class PersistentMemoryAllocatorTest : public testing::Test { - public: - // This can't be statically initialized because it's value isn't defined - // in the PersistentMemoryAllocator header file. Instead, it's simply set - // in the constructor. - uint32_t kAllocAlignment; - - struct TestObject1 { - static constexpr uint32_t kPersistentTypeId = 1; - static constexpr size_t kExpectedInstanceSize = 4 + 1 + 3; - int32_t onething; - char oranother; - }; - - struct TestObject2 { - static constexpr uint32_t kPersistentTypeId = 2; - static constexpr size_t kExpectedInstanceSize = 8 + 4 + 4 + 8 + 8; - int64_t thiis; - int32_t that; - float andthe; - double other; - char thing[8]; - }; - - PersistentMemoryAllocatorTest() { - kAllocAlignment = GetAllocAlignment(); - mem_segment_.reset(new char[TEST_MEMORY_SIZE]); - } - - void SetUp() override { - allocator_.reset(); - ::memset(mem_segment_.get(), 0, TEST_MEMORY_SIZE); - allocator_.reset(new PersistentMemoryAllocator( - mem_segment_.get(), TEST_MEMORY_SIZE, TEST_MEMORY_PAGE, - TEST_ID, TEST_NAME, false)); - } - - void TearDown() override { - allocator_.reset(); - } - - unsigned CountIterables() { - PersistentMemoryAllocator::Iterator iter(allocator_.get()); - uint32_t type; - unsigned count = 0; - while (iter.GetNext(&type) != 0) { - ++count; - } - return count; - } - - static uint32_t GetAllocAlignment() { - return PersistentMemoryAllocator::kAllocAlignment; - } - - protected: - std::unique_ptr mem_segment_; - std::unique_ptr allocator_; -}; - -TEST_F(PersistentMemoryAllocatorTest, AllocateAndIterate) { - allocator_->CreateTrackingHistograms(allocator_->Name()); - - std::string base_name(TEST_NAME); - EXPECT_EQ(TEST_ID, allocator_->Id()); - EXPECT_TRUE(allocator_->used_histogram_); - EXPECT_EQ("UMA.PersistentAllocator." + base_name + ".UsedPct", - allocator_->used_histogram_->histogram_name()); - EXPECT_EQ(PersistentMemoryAllocator::MEMORY_INITIALIZED, - allocator_->GetMemoryState()); - - // Get base memory info for later comparison. - PersistentMemoryAllocator::MemoryInfo meminfo0; - allocator_->GetMemoryInfo(&meminfo0); - EXPECT_EQ(TEST_MEMORY_SIZE, meminfo0.total); - EXPECT_GT(meminfo0.total, meminfo0.free); - - // Validate allocation of test object and make sure it can be referenced - // and all metadata looks correct. - TestObject1* obj1 = allocator_->New(); - ASSERT_TRUE(obj1); - Reference block1 = allocator_->GetAsReference(obj1); - ASSERT_NE(0U, block1); - EXPECT_NE(nullptr, allocator_->GetAsObject(block1)); - EXPECT_EQ(nullptr, allocator_->GetAsObject(block1)); - EXPECT_LE(sizeof(TestObject1), allocator_->GetAllocSize(block1)); - EXPECT_GT(sizeof(TestObject1) + kAllocAlignment, - allocator_->GetAllocSize(block1)); - PersistentMemoryAllocator::MemoryInfo meminfo1; - allocator_->GetMemoryInfo(&meminfo1); - EXPECT_EQ(meminfo0.total, meminfo1.total); - EXPECT_GT(meminfo0.free, meminfo1.free); - - // Verify that pointers can be turned back into references and that invalid - // addresses return null. - char* memory1 = allocator_->GetAsArray(block1, 1, 1); - ASSERT_TRUE(memory1); - EXPECT_EQ(block1, allocator_->GetAsReference(memory1, 0)); - EXPECT_EQ(block1, allocator_->GetAsReference(memory1, 1)); - EXPECT_EQ(0U, allocator_->GetAsReference(memory1, 2)); - EXPECT_EQ(0U, allocator_->GetAsReference(memory1 + 1, 0)); - EXPECT_EQ(0U, allocator_->GetAsReference(memory1 + 16, 0)); - EXPECT_EQ(0U, allocator_->GetAsReference(nullptr, 0)); - EXPECT_EQ(0U, allocator_->GetAsReference(&base_name, 0)); - - // Ensure that the test-object can be made iterable. - PersistentMemoryAllocator::Iterator iter1a(allocator_.get()); - EXPECT_EQ(0U, iter1a.GetLast()); - uint32_t type; - EXPECT_EQ(0U, iter1a.GetNext(&type)); - allocator_->MakeIterable(block1); - EXPECT_EQ(block1, iter1a.GetNext(&type)); - EXPECT_EQ(1U, type); - EXPECT_EQ(block1, iter1a.GetLast()); - EXPECT_EQ(0U, iter1a.GetNext(&type)); - EXPECT_EQ(block1, iter1a.GetLast()); - - // Create second test-object and ensure everything is good and it cannot - // be confused with test-object of another type. - TestObject2* obj2 = allocator_->New(); - ASSERT_TRUE(obj2); - Reference block2 = allocator_->GetAsReference(obj2); - ASSERT_NE(0U, block2); - EXPECT_NE(nullptr, allocator_->GetAsObject(block2)); - EXPECT_EQ(nullptr, allocator_->GetAsObject(block2)); - EXPECT_LE(sizeof(TestObject2), allocator_->GetAllocSize(block2)); - EXPECT_GT(sizeof(TestObject2) + kAllocAlignment, - allocator_->GetAllocSize(block2)); - PersistentMemoryAllocator::MemoryInfo meminfo2; - allocator_->GetMemoryInfo(&meminfo2); - EXPECT_EQ(meminfo1.total, meminfo2.total); - EXPECT_GT(meminfo1.free, meminfo2.free); - - // Ensure that second test-object can also be made iterable. - allocator_->MakeIterable(obj2); - EXPECT_EQ(block2, iter1a.GetNext(&type)); - EXPECT_EQ(2U, type); - EXPECT_EQ(block2, iter1a.GetLast()); - EXPECT_EQ(0U, iter1a.GetNext(&type)); - EXPECT_EQ(block2, iter1a.GetLast()); - - // Check that the iterator can be reset to the beginning. - iter1a.Reset(); - EXPECT_EQ(0U, iter1a.GetLast()); - EXPECT_EQ(block1, iter1a.GetNext(&type)); - EXPECT_EQ(block1, iter1a.GetLast()); - EXPECT_EQ(block2, iter1a.GetNext(&type)); - EXPECT_EQ(block2, iter1a.GetLast()); - EXPECT_EQ(0U, iter1a.GetNext(&type)); - - // Check that the iterator can be reset to an arbitrary location. - iter1a.Reset(block1); - EXPECT_EQ(block1, iter1a.GetLast()); - EXPECT_EQ(block2, iter1a.GetNext(&type)); - EXPECT_EQ(block2, iter1a.GetLast()); - EXPECT_EQ(0U, iter1a.GetNext(&type)); - - // Check that iteration can begin after an arbitrary location. - PersistentMemoryAllocator::Iterator iter1b(allocator_.get(), block1); - EXPECT_EQ(block2, iter1b.GetNext(&type)); - EXPECT_EQ(0U, iter1b.GetNext(&type)); - - // Ensure nothing has gone noticably wrong. - EXPECT_FALSE(allocator_->IsFull()); - EXPECT_FALSE(allocator_->IsCorrupt()); - - // Check the internal histogram record of used memory. - allocator_->UpdateTrackingHistograms(); - std::unique_ptr used_samples( - allocator_->used_histogram_->SnapshotSamples()); - EXPECT_TRUE(used_samples); - EXPECT_EQ(1, used_samples->TotalCount()); - - // Check that an object's type can be changed. - EXPECT_EQ(2U, allocator_->GetType(block2)); - allocator_->ChangeType(block2, 3, 2, false); - EXPECT_EQ(3U, allocator_->GetType(block2)); - allocator_->New(block2, 3, false); - EXPECT_EQ(2U, allocator_->GetType(block2)); - - // Create second allocator (read/write) using the same memory segment. - std::unique_ptr allocator2( - new PersistentMemoryAllocator(mem_segment_.get(), TEST_MEMORY_SIZE, - TEST_MEMORY_PAGE, 0, "", false)); - EXPECT_EQ(TEST_ID, allocator2->Id()); - EXPECT_FALSE(allocator2->used_histogram_); - - // Ensure that iteration and access through second allocator works. - PersistentMemoryAllocator::Iterator iter2(allocator2.get()); - EXPECT_EQ(block1, iter2.GetNext(&type)); - EXPECT_EQ(block2, iter2.GetNext(&type)); - EXPECT_EQ(0U, iter2.GetNext(&type)); - EXPECT_NE(nullptr, allocator2->GetAsObject(block1)); - EXPECT_NE(nullptr, allocator2->GetAsObject(block2)); - - // Create a third allocator (read-only) using the same memory segment. - std::unique_ptr allocator3( - new PersistentMemoryAllocator(mem_segment_.get(), TEST_MEMORY_SIZE, - TEST_MEMORY_PAGE, 0, "", true)); - EXPECT_EQ(TEST_ID, allocator3->Id()); - EXPECT_FALSE(allocator3->used_histogram_); - - // Ensure that iteration and access through third allocator works. - PersistentMemoryAllocator::Iterator iter3(allocator3.get()); - EXPECT_EQ(block1, iter3.GetNext(&type)); - EXPECT_EQ(block2, iter3.GetNext(&type)); - EXPECT_EQ(0U, iter3.GetNext(&type)); - EXPECT_NE(nullptr, allocator3->GetAsObject(block1)); - EXPECT_NE(nullptr, allocator3->GetAsObject(block2)); - - // Ensure that GetNextOfType works. - PersistentMemoryAllocator::Iterator iter1c(allocator_.get()); - EXPECT_EQ(block2, iter1c.GetNextOfType()); - EXPECT_EQ(0U, iter1c.GetNextOfType(2)); - - // Ensure that GetNextOfObject works. - PersistentMemoryAllocator::Iterator iter1d(allocator_.get()); - EXPECT_EQ(obj2, iter1d.GetNextOfObject()); - EXPECT_EQ(nullptr, iter1d.GetNextOfObject()); - - // Ensure that deleting an object works. - allocator_->Delete(obj2); - PersistentMemoryAllocator::Iterator iter1z(allocator_.get()); - EXPECT_EQ(nullptr, iter1z.GetNextOfObject()); - - // Ensure that the memory state can be set. - allocator_->SetMemoryState(PersistentMemoryAllocator::MEMORY_DELETED); - EXPECT_EQ(PersistentMemoryAllocator::MEMORY_DELETED, - allocator_->GetMemoryState()); -} - -TEST_F(PersistentMemoryAllocatorTest, PageTest) { - // This allocation will go into the first memory page. - Reference block1 = allocator_->Allocate(TEST_MEMORY_PAGE / 2, 1); - EXPECT_LT(0U, block1); - EXPECT_GT(TEST_MEMORY_PAGE, block1); - - // This allocation won't fit in same page as previous block. - Reference block2 = - allocator_->Allocate(TEST_MEMORY_PAGE - 2 * kAllocAlignment, 2); - EXPECT_EQ(TEST_MEMORY_PAGE, block2); - - // This allocation will also require a new page. - Reference block3 = allocator_->Allocate(2 * kAllocAlignment + 99, 3); - EXPECT_EQ(2U * TEST_MEMORY_PAGE, block3); -} - -// A simple thread that takes an allocator and repeatedly allocates random- -// sized chunks from it until no more can be done. -class AllocatorThread : public SimpleThread { - public: - AllocatorThread(const std::string& name, - void* base, - uint32_t size, - uint32_t page_size) - : SimpleThread(name, Options()), - count_(0), - iterable_(0), - allocator_(base, size, page_size, 0, std::string(), false) {} - - void Run() override { - for (;;) { - uint32_t size = RandInt(1, 99); - uint32_t type = RandInt(100, 999); - Reference block = allocator_.Allocate(size, type); - if (!block) - break; - - count_++; - if (RandInt(0, 1)) { - allocator_.MakeIterable(block); - iterable_++; - } - } - } - - unsigned iterable() { return iterable_; } - unsigned count() { return count_; } - - private: - unsigned count_; - unsigned iterable_; - PersistentMemoryAllocator allocator_; -}; - -// Test parallel allocation/iteration and ensure consistency across all -// instances. -TEST_F(PersistentMemoryAllocatorTest, ParallelismTest) { - void* memory = mem_segment_.get(); - AllocatorThread t1("t1", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE); - AllocatorThread t2("t2", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE); - AllocatorThread t3("t3", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE); - AllocatorThread t4("t4", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE); - AllocatorThread t5("t5", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE); - - t1.Start(); - t2.Start(); - t3.Start(); - t4.Start(); - t5.Start(); - - unsigned last_count = 0; - do { - unsigned count = CountIterables(); - EXPECT_LE(last_count, count); - } while (!allocator_->IsCorrupt() && !allocator_->IsFull()); - - t1.Join(); - t2.Join(); - t3.Join(); - t4.Join(); - t5.Join(); - - EXPECT_FALSE(allocator_->IsCorrupt()); - EXPECT_TRUE(allocator_->IsFull()); - EXPECT_EQ(CountIterables(), - t1.iterable() + t2.iterable() + t3.iterable() + t4.iterable() + - t5.iterable()); -} - -// A simple thread that counts objects by iterating through an allocator. -class CounterThread : public SimpleThread { - public: - CounterThread(const std::string& name, - PersistentMemoryAllocator::Iterator* iterator, - Lock* lock, - ConditionVariable* condition, - bool* wake_up) - : SimpleThread(name, Options()), - iterator_(iterator), - lock_(lock), - condition_(condition), - count_(0), - wake_up_(wake_up) {} - - void Run() override { - // Wait so all threads can start at approximately the same time. - // Best performance comes from releasing a single worker which then - // releases the next, etc., etc. - { - AutoLock autolock(*lock_); - - // Before calling Wait(), make sure that the wake up condition - // has not already passed. Also, since spurious signal events - // are possible, check the condition in a while loop to make - // sure that the wake up condition is met when this thread - // returns from the Wait(). - // See usage comments in src/base/synchronization/condition_variable.h. - while (!*wake_up_) { - condition_->Wait(); - condition_->Signal(); - } - } - - uint32_t type; - while (iterator_->GetNext(&type) != 0) { - ++count_; - } - } - - unsigned count() { return count_; } - - private: - PersistentMemoryAllocator::Iterator* iterator_; - Lock* lock_; - ConditionVariable* condition_; - unsigned count_; - bool* wake_up_; - - DISALLOW_COPY_AND_ASSIGN(CounterThread); -}; - -// Ensure that parallel iteration returns the same number of objects as -// single-threaded iteration. -TEST_F(PersistentMemoryAllocatorTest, IteratorParallelismTest) { - // Fill the memory segment with random allocations. - unsigned iterable_count = 0; - for (;;) { - uint32_t size = RandInt(1, 99); - uint32_t type = RandInt(100, 999); - Reference block = allocator_->Allocate(size, type); - if (!block) - break; - allocator_->MakeIterable(block); - ++iterable_count; - } - EXPECT_FALSE(allocator_->IsCorrupt()); - EXPECT_TRUE(allocator_->IsFull()); - EXPECT_EQ(iterable_count, CountIterables()); - - PersistentMemoryAllocator::Iterator iter(allocator_.get()); - Lock lock; - ConditionVariable condition(&lock); - bool wake_up = false; - - CounterThread t1("t1", &iter, &lock, &condition, &wake_up); - CounterThread t2("t2", &iter, &lock, &condition, &wake_up); - CounterThread t3("t3", &iter, &lock, &condition, &wake_up); - CounterThread t4("t4", &iter, &lock, &condition, &wake_up); - CounterThread t5("t5", &iter, &lock, &condition, &wake_up); - - t1.Start(); - t2.Start(); - t3.Start(); - t4.Start(); - t5.Start(); - - // Take the lock and set the wake up condition to true. This helps to - // avoid a race condition where the Signal() event is called before - // all the threads have reached the Wait() and thus never get woken up. - { - AutoLock autolock(lock); - wake_up = true; - } - - // This will release all the waiting threads. - condition.Signal(); - - t1.Join(); - t2.Join(); - t3.Join(); - t4.Join(); - t5.Join(); - - EXPECT_EQ(iterable_count, - t1.count() + t2.count() + t3.count() + t4.count() + t5.count()); - -#if 0 - // These ensure that the threads don't run sequentially. It shouldn't be - // enabled in general because it could lead to a flaky test if it happens - // simply by chance but it is useful during development to ensure that the - // test is working correctly. - EXPECT_NE(iterable_count, t1.count()); - EXPECT_NE(iterable_count, t2.count()); - EXPECT_NE(iterable_count, t3.count()); - EXPECT_NE(iterable_count, t4.count()); - EXPECT_NE(iterable_count, t5.count()); -#endif -} - -TEST_F(PersistentMemoryAllocatorTest, DelayedAllocationTest) { - std::atomic ref1, ref2; - ref1.store(0, std::memory_order_relaxed); - ref2.store(0, std::memory_order_relaxed); - DelayedPersistentAllocation da1(allocator_.get(), &ref1, 1001, 100, true); - DelayedPersistentAllocation da2a(allocator_.get(), &ref2, 2002, 200, 0, true); - DelayedPersistentAllocation da2b(allocator_.get(), &ref2, 2002, 200, 5, true); - - // Nothing should yet have been allocated. - uint32_t type; - PersistentMemoryAllocator::Iterator iter(allocator_.get()); - EXPECT_EQ(0U, iter.GetNext(&type)); - - // Do first delayed allocation and check that a new persistent object exists. - EXPECT_EQ(0U, da1.reference()); - void* mem1 = da1.Get(); - ASSERT_TRUE(mem1); - EXPECT_NE(0U, da1.reference()); - EXPECT_EQ(allocator_->GetAsReference(mem1, 1001), - ref1.load(std::memory_order_relaxed)); - EXPECT_NE(0U, iter.GetNext(&type)); - EXPECT_EQ(1001U, type); - EXPECT_EQ(0U, iter.GetNext(&type)); - - // Do second delayed allocation and check. - void* mem2a = da2a.Get(); - ASSERT_TRUE(mem2a); - EXPECT_EQ(allocator_->GetAsReference(mem2a, 2002), - ref2.load(std::memory_order_relaxed)); - EXPECT_NE(0U, iter.GetNext(&type)); - EXPECT_EQ(2002U, type); - EXPECT_EQ(0U, iter.GetNext(&type)); - - // Third allocation should just return offset into second allocation. - void* mem2b = da2b.Get(); - ASSERT_TRUE(mem2b); - EXPECT_EQ(0U, iter.GetNext(&type)); - EXPECT_EQ(reinterpret_cast(mem2a) + 5, - reinterpret_cast(mem2b)); -} - -// This test doesn't verify anything other than it doesn't crash. Its goal -// is to find coding errors that aren't otherwise tested for, much like a -// "fuzzer" would. -// This test is suppsoed to fail on TSAN bot (crbug.com/579867). -#if defined(THREAD_SANITIZER) -#define MAYBE_CorruptionTest DISABLED_CorruptionTest -#else -#define MAYBE_CorruptionTest CorruptionTest -#endif -TEST_F(PersistentMemoryAllocatorTest, MAYBE_CorruptionTest) { - char* memory = mem_segment_.get(); - AllocatorThread t1("t1", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE); - AllocatorThread t2("t2", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE); - AllocatorThread t3("t3", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE); - AllocatorThread t4("t4", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE); - AllocatorThread t5("t5", memory, TEST_MEMORY_SIZE, TEST_MEMORY_PAGE); - - t1.Start(); - t2.Start(); - t3.Start(); - t4.Start(); - t5.Start(); - - do { - size_t offset = RandInt(0, TEST_MEMORY_SIZE - 1); - char value = RandInt(0, 255); - memory[offset] = value; - } while (!allocator_->IsCorrupt() && !allocator_->IsFull()); - - t1.Join(); - t2.Join(); - t3.Join(); - t4.Join(); - t5.Join(); - - CountIterables(); -} - -// Attempt to cause crashes or loops by expressly creating dangerous conditions. -TEST_F(PersistentMemoryAllocatorTest, MaliciousTest) { - Reference block1 = allocator_->Allocate(sizeof(TestObject1), 1); - Reference block2 = allocator_->Allocate(sizeof(TestObject1), 2); - Reference block3 = allocator_->Allocate(sizeof(TestObject1), 3); - Reference block4 = allocator_->Allocate(sizeof(TestObject1), 3); - Reference block5 = allocator_->Allocate(sizeof(TestObject1), 3); - allocator_->MakeIterable(block1); - allocator_->MakeIterable(block2); - allocator_->MakeIterable(block3); - allocator_->MakeIterable(block4); - allocator_->MakeIterable(block5); - EXPECT_EQ(5U, CountIterables()); - EXPECT_FALSE(allocator_->IsCorrupt()); - - // Create loop in iterable list and ensure it doesn't hang. The return value - // from CountIterables() in these cases is unpredictable. If there is a - // failure, the call will hang and the test killed for taking too long. - uint32_t* header4 = (uint32_t*)(mem_segment_.get() + block4); - EXPECT_EQ(block5, header4[3]); - header4[3] = block4; - CountIterables(); // loop: 1-2-3-4-4 - EXPECT_TRUE(allocator_->IsCorrupt()); - - // Test where loop goes back to previous block. - header4[3] = block3; - CountIterables(); // loop: 1-2-3-4-3 - - // Test where loop goes back to the beginning. - header4[3] = block1; - CountIterables(); // loop: 1-2-3-4-1 -} - - -//----- LocalPersistentMemoryAllocator ----------------------------------------- - -TEST(LocalPersistentMemoryAllocatorTest, CreationTest) { - LocalPersistentMemoryAllocator allocator(TEST_MEMORY_SIZE, 42, ""); - EXPECT_EQ(42U, allocator.Id()); - EXPECT_NE(0U, allocator.Allocate(24, 1)); - EXPECT_FALSE(allocator.IsFull()); - EXPECT_FALSE(allocator.IsCorrupt()); -} - - -//----- SharedPersistentMemoryAllocator ---------------------------------------- - -TEST(SharedPersistentMemoryAllocatorTest, CreationTest) { - SharedMemoryHandle shared_handle_1; - SharedMemoryHandle shared_handle_2; - - PersistentMemoryAllocator::MemoryInfo meminfo1; - Reference r123, r456, r789; - { - std::unique_ptr shmem1(new SharedMemory()); - ASSERT_TRUE(shmem1->CreateAndMapAnonymous(TEST_MEMORY_SIZE)); - SharedPersistentMemoryAllocator local(std::move(shmem1), TEST_ID, "", - false); - EXPECT_FALSE(local.IsReadonly()); - r123 = local.Allocate(123, 123); - r456 = local.Allocate(456, 456); - r789 = local.Allocate(789, 789); - local.MakeIterable(r123); - local.ChangeType(r456, 654, 456, false); - local.MakeIterable(r789); - local.GetMemoryInfo(&meminfo1); - EXPECT_FALSE(local.IsFull()); - EXPECT_FALSE(local.IsCorrupt()); - - shared_handle_1 = local.shared_memory()->handle().Duplicate(); - ASSERT_TRUE(shared_handle_1.IsValid()); - shared_handle_2 = local.shared_memory()->handle().Duplicate(); - ASSERT_TRUE(shared_handle_2.IsValid()); - } - - // Read-only test. - std::unique_ptr shmem2(new SharedMemory(shared_handle_1, - /*readonly=*/true)); - ASSERT_TRUE(shmem2->Map(TEST_MEMORY_SIZE)); - - SharedPersistentMemoryAllocator shalloc2(std::move(shmem2), 0, "", true); - EXPECT_TRUE(shalloc2.IsReadonly()); - EXPECT_EQ(TEST_ID, shalloc2.Id()); - EXPECT_FALSE(shalloc2.IsFull()); - EXPECT_FALSE(shalloc2.IsCorrupt()); - - PersistentMemoryAllocator::Iterator iter2(&shalloc2); - uint32_t type; - EXPECT_EQ(r123, iter2.GetNext(&type)); - EXPECT_EQ(r789, iter2.GetNext(&type)); - EXPECT_EQ(0U, iter2.GetNext(&type)); - - EXPECT_EQ(123U, shalloc2.GetType(r123)); - EXPECT_EQ(654U, shalloc2.GetType(r456)); - EXPECT_EQ(789U, shalloc2.GetType(r789)); - - PersistentMemoryAllocator::MemoryInfo meminfo2; - shalloc2.GetMemoryInfo(&meminfo2); - EXPECT_EQ(meminfo1.total, meminfo2.total); - EXPECT_EQ(meminfo1.free, meminfo2.free); - - // Read/write test. - std::unique_ptr shmem3(new SharedMemory(shared_handle_2, - /*readonly=*/false)); - ASSERT_TRUE(shmem3->Map(TEST_MEMORY_SIZE)); - - SharedPersistentMemoryAllocator shalloc3(std::move(shmem3), 0, "", false); - EXPECT_FALSE(shalloc3.IsReadonly()); - EXPECT_EQ(TEST_ID, shalloc3.Id()); - EXPECT_FALSE(shalloc3.IsFull()); - EXPECT_FALSE(shalloc3.IsCorrupt()); - - PersistentMemoryAllocator::Iterator iter3(&shalloc3); - EXPECT_EQ(r123, iter3.GetNext(&type)); - EXPECT_EQ(r789, iter3.GetNext(&type)); - EXPECT_EQ(0U, iter3.GetNext(&type)); - - EXPECT_EQ(123U, shalloc3.GetType(r123)); - EXPECT_EQ(654U, shalloc3.GetType(r456)); - EXPECT_EQ(789U, shalloc3.GetType(r789)); - - PersistentMemoryAllocator::MemoryInfo meminfo3; - shalloc3.GetMemoryInfo(&meminfo3); - EXPECT_EQ(meminfo1.total, meminfo3.total); - EXPECT_EQ(meminfo1.free, meminfo3.free); - - // Interconnectivity test. - Reference obj = shalloc3.Allocate(42, 42); - ASSERT_TRUE(obj); - shalloc3.MakeIterable(obj); - EXPECT_EQ(obj, iter2.GetNext(&type)); - EXPECT_EQ(42U, type); - - // Clear-on-change test. - Reference data_ref = shalloc3.Allocate(sizeof(int) * 4, 911); - int* data = shalloc3.GetAsArray(data_ref, 911, 4); - ASSERT_TRUE(data); - data[0] = 0; - data[1] = 1; - data[2] = 2; - data[3] = 3; - ASSERT_TRUE(shalloc3.ChangeType(data_ref, 119, 911, false)); - EXPECT_EQ(0, data[0]); - EXPECT_EQ(1, data[1]); - EXPECT_EQ(2, data[2]); - EXPECT_EQ(3, data[3]); - ASSERT_TRUE(shalloc3.ChangeType(data_ref, 191, 119, true)); - EXPECT_EQ(0, data[0]); - EXPECT_EQ(0, data[1]); - EXPECT_EQ(0, data[2]); - EXPECT_EQ(0, data[3]); -} - - -#if !defined(OS_NACL) -//----- FilePersistentMemoryAllocator ------------------------------------------ - -TEST(FilePersistentMemoryAllocatorTest, CreationTest) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("persistent_memory"); - - PersistentMemoryAllocator::MemoryInfo meminfo1; - Reference r123, r456, r789; - { - LocalPersistentMemoryAllocator local(TEST_MEMORY_SIZE, TEST_ID, ""); - EXPECT_FALSE(local.IsReadonly()); - r123 = local.Allocate(123, 123); - r456 = local.Allocate(456, 456); - r789 = local.Allocate(789, 789); - local.MakeIterable(r123); - local.ChangeType(r456, 654, 456, false); - local.MakeIterable(r789); - local.GetMemoryInfo(&meminfo1); - EXPECT_FALSE(local.IsFull()); - EXPECT_FALSE(local.IsCorrupt()); - - File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE); - ASSERT_TRUE(writer.IsValid()); - writer.Write(0, (const char*)local.data(), local.used()); - } - - std::unique_ptr mmfile(new MemoryMappedFile()); - mmfile->Initialize(file_path); - EXPECT_TRUE(mmfile->IsValid()); - const size_t mmlength = mmfile->length(); - EXPECT_GE(meminfo1.total, mmlength); - - FilePersistentMemoryAllocator file(std::move(mmfile), 0, 0, "", false); - EXPECT_FALSE(file.IsReadonly()); - EXPECT_EQ(TEST_ID, file.Id()); - EXPECT_FALSE(file.IsFull()); - EXPECT_FALSE(file.IsCorrupt()); - - PersistentMemoryAllocator::Iterator iter(&file); - uint32_t type; - EXPECT_EQ(r123, iter.GetNext(&type)); - EXPECT_EQ(r789, iter.GetNext(&type)); - EXPECT_EQ(0U, iter.GetNext(&type)); - - EXPECT_EQ(123U, file.GetType(r123)); - EXPECT_EQ(654U, file.GetType(r456)); - EXPECT_EQ(789U, file.GetType(r789)); - - PersistentMemoryAllocator::MemoryInfo meminfo2; - file.GetMemoryInfo(&meminfo2); - EXPECT_GE(meminfo1.total, meminfo2.total); - EXPECT_GE(meminfo1.free, meminfo2.free); - EXPECT_EQ(mmlength, meminfo2.total); - EXPECT_EQ(0U, meminfo2.free); - - // There's no way of knowing if Flush actually does anything but at least - // verify that it runs without CHECK violations. - file.Flush(false); - file.Flush(true); -} - -TEST(FilePersistentMemoryAllocatorTest, ExtendTest) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("extend_test"); - MemoryMappedFile::Region region = {0, 16 << 10}; // 16KiB maximum size. - - // Start with a small but valid file of persistent data. - ASSERT_FALSE(PathExists(file_path)); - { - LocalPersistentMemoryAllocator local(TEST_MEMORY_SIZE, TEST_ID, ""); - local.Allocate(1, 1); - local.Allocate(11, 11); - - File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE); - ASSERT_TRUE(writer.IsValid()); - writer.Write(0, (const char*)local.data(), local.used()); - } - ASSERT_TRUE(PathExists(file_path)); - int64_t before_size; - ASSERT_TRUE(GetFileSize(file_path, &before_size)); - - // Map it as an extendable read/write file and append to it. - { - std::unique_ptr mmfile(new MemoryMappedFile()); - mmfile->Initialize( - File(file_path, File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE), - region, MemoryMappedFile::READ_WRITE_EXTEND); - FilePersistentMemoryAllocator allocator(std::move(mmfile), region.size, 0, - "", false); - EXPECT_EQ(static_cast(before_size), allocator.used()); - - allocator.Allocate(111, 111); - EXPECT_LT(static_cast(before_size), allocator.used()); - } - - // Validate that append worked. - int64_t after_size; - ASSERT_TRUE(GetFileSize(file_path, &after_size)); - EXPECT_LT(before_size, after_size); - - // Verify that it's still an acceptable file. - { - std::unique_ptr mmfile(new MemoryMappedFile()); - mmfile->Initialize( - File(file_path, File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE), - region, MemoryMappedFile::READ_WRITE_EXTEND); - EXPECT_TRUE(FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true)); - EXPECT_TRUE( - FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, false)); - } -} - -TEST(FilePersistentMemoryAllocatorTest, AcceptableTest) { - const uint32_t kAllocAlignment = - PersistentMemoryAllocatorTest::GetAllocAlignment(); - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - - LocalPersistentMemoryAllocator local(TEST_MEMORY_SIZE, TEST_ID, ""); - local.MakeIterable(local.Allocate(1, 1)); - local.MakeIterable(local.Allocate(11, 11)); - const size_t minsize = local.used(); - std::unique_ptr garbage(new char[minsize]); - RandBytes(garbage.get(), minsize); - - std::unique_ptr mmfile; - char filename[100]; - for (size_t filesize = minsize; filesize > 0; --filesize) { - strings::SafeSPrintf(filename, "memory_%d_A", filesize); - FilePath file_path = temp_dir.GetPath().AppendASCII(filename); - ASSERT_FALSE(PathExists(file_path)); - { - File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE); - ASSERT_TRUE(writer.IsValid()); - writer.Write(0, (const char*)local.data(), filesize); - } - ASSERT_TRUE(PathExists(file_path)); - - // Request read/write access for some sizes that are a multple of the - // allocator's alignment size. The allocator is strict about file size - // being a multiple of its internal alignment when doing read/write access. - const bool read_only = (filesize % (2 * kAllocAlignment)) != 0; - const uint32_t file_flags = - File::FLAG_OPEN | File::FLAG_READ | (read_only ? 0 : File::FLAG_WRITE); - const MemoryMappedFile::Access map_access = - read_only ? MemoryMappedFile::READ_ONLY : MemoryMappedFile::READ_WRITE; - - mmfile.reset(new MemoryMappedFile()); - mmfile->Initialize(File(file_path, file_flags), map_access); - EXPECT_EQ(filesize, mmfile->length()); - if (FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, read_only)) { - // Make sure construction doesn't crash. It will, however, cause - // error messages warning about about a corrupted memory segment. - FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0, "", - read_only); - // Also make sure that iteration doesn't crash. - PersistentMemoryAllocator::Iterator iter(&allocator); - uint32_t type_id; - Reference ref; - while ((ref = iter.GetNext(&type_id)) != 0) { - const char* data = allocator.GetAsArray( - ref, 0, PersistentMemoryAllocator::kSizeAny); - uint32_t type = allocator.GetType(ref); - size_t size = allocator.GetAllocSize(ref); - // Ensure compiler can't optimize-out above variables. - (void)data; - (void)type; - (void)size; - } - - // Ensure that short files are detected as corrupt and full files are not. - EXPECT_EQ(filesize != minsize, allocator.IsCorrupt()); - } else { - // For filesize >= minsize, the file must be acceptable. This - // else clause (file-not-acceptable) should be reached only if - // filesize < minsize. - EXPECT_LT(filesize, minsize); - } - - strings::SafeSPrintf(filename, "memory_%d_B", filesize); - file_path = temp_dir.GetPath().AppendASCII(filename); - ASSERT_FALSE(PathExists(file_path)); - { - File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE); - ASSERT_TRUE(writer.IsValid()); - writer.Write(0, (const char*)garbage.get(), filesize); - } - ASSERT_TRUE(PathExists(file_path)); - - mmfile.reset(new MemoryMappedFile()); - mmfile->Initialize(File(file_path, file_flags), map_access); - EXPECT_EQ(filesize, mmfile->length()); - if (FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, read_only)) { - // Make sure construction doesn't crash. It will, however, cause - // error messages warning about about a corrupted memory segment. - FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0, "", - read_only); - EXPECT_TRUE(allocator.IsCorrupt()); // Garbage data so it should be. - } else { - // For filesize >= minsize, the file must be acceptable. This - // else clause (file-not-acceptable) should be reached only if - // filesize < minsize. - EXPECT_GT(minsize, filesize); - } - } -} - -TEST_F(PersistentMemoryAllocatorTest, TruncateTest) { - ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - FilePath file_path = temp_dir.GetPath().AppendASCII("truncate_test"); - - // Start with a small but valid file of persistent data. Keep the "used" - // amount for both allocations. - Reference a1_ref; - Reference a2_ref; - size_t a1_used; - size_t a2_used; - ASSERT_FALSE(PathExists(file_path)); - { - LocalPersistentMemoryAllocator allocator(TEST_MEMORY_SIZE, TEST_ID, ""); - a1_ref = allocator.Allocate(100 << 10, 1); - allocator.MakeIterable(a1_ref); - a1_used = allocator.used(); - a2_ref = allocator.Allocate(200 << 10, 11); - allocator.MakeIterable(a2_ref); - a2_used = allocator.used(); - - File writer(file_path, File::FLAG_CREATE | File::FLAG_WRITE); - ASSERT_TRUE(writer.IsValid()); - writer.Write(0, static_cast(allocator.data()), - allocator.size()); - } - ASSERT_TRUE(PathExists(file_path)); - EXPECT_LE(a1_used, a2_ref); - - // Truncate the file to include everything and make sure it can be read, both - // with read-write and read-only access. - for (size_t file_length : {a2_used, a1_used, a1_used / 2}) { - SCOPED_TRACE(StringPrintf("file_length=%zu", file_length)); - SetFileLength(file_path, file_length); - - for (bool read_only : {false, true}) { - SCOPED_TRACE(StringPrintf("read_only=%s", read_only ? "true" : "false")); - - std::unique_ptr mmfile(new MemoryMappedFile()); - mmfile->Initialize( - File(file_path, File::FLAG_OPEN | - (read_only ? File::FLAG_READ - : File::FLAG_READ | File::FLAG_WRITE)), - read_only ? MemoryMappedFile::READ_ONLY - : MemoryMappedFile::READ_WRITE); - ASSERT_TRUE( - FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, read_only)); - - FilePersistentMemoryAllocator allocator(std::move(mmfile), 0, 0, "", - read_only); - - PersistentMemoryAllocator::Iterator iter(&allocator); - uint32_t type_id; - EXPECT_EQ(file_length >= a1_used ? a1_ref : 0U, iter.GetNext(&type_id)); - EXPECT_EQ(file_length >= a2_used ? a2_ref : 0U, iter.GetNext(&type_id)); - EXPECT_EQ(0U, iter.GetNext(&type_id)); - - // Ensure that short files are detected as corrupt and full files are not. - EXPECT_EQ(file_length < a2_used, allocator.IsCorrupt()); - } - - // Ensure that file length was not adjusted. - int64_t actual_length; - ASSERT_TRUE(GetFileSize(file_path, &actual_length)); - EXPECT_EQ(file_length, static_cast(actual_length)); - } -} - -#endif // !defined(OS_NACL) - -} // namespace base diff --git a/metrics/persistent_sample_map.cc b/metrics/persistent_sample_map.cc deleted file mode 100644 index f38b9d1f6..000000000 --- a/metrics/persistent_sample_map.cc +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2016 The Chromium 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/persistent_sample_map.h" - -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/histogram_macros.h" -#include "base/metrics/persistent_histogram_allocator.h" -#include "base/numerics/safe_conversions.h" -#include "base/stl_util.h" - -namespace base { - -typedef HistogramBase::Count Count; -typedef HistogramBase::Sample Sample; - -namespace { - -// An iterator for going through a PersistentSampleMap. The logic here is -// identical to that of SampleMapIterator but with different data structures. -// Changes here likely need to be duplicated there. -class PersistentSampleMapIterator : public SampleCountIterator { - public: - typedef std::map - SampleToCountMap; - - explicit PersistentSampleMapIterator(const SampleToCountMap& sample_counts); - ~PersistentSampleMapIterator() override; - - // SampleCountIterator: - bool Done() const override; - void Next() override; - void Get(HistogramBase::Sample* min, - int64_t* max, - HistogramBase::Count* count) const override; - - private: - void SkipEmptyBuckets(); - - SampleToCountMap::const_iterator iter_; - const SampleToCountMap::const_iterator end_; -}; - -PersistentSampleMapIterator::PersistentSampleMapIterator( - const SampleToCountMap& sample_counts) - : iter_(sample_counts.begin()), - end_(sample_counts.end()) { - SkipEmptyBuckets(); -} - -PersistentSampleMapIterator::~PersistentSampleMapIterator() = default; - -bool PersistentSampleMapIterator::Done() const { - return iter_ == end_; -} - -void PersistentSampleMapIterator::Next() { - DCHECK(!Done()); - ++iter_; - SkipEmptyBuckets(); -} - -void PersistentSampleMapIterator::Get(Sample* min, - int64_t* max, - Count* count) const { - DCHECK(!Done()); - if (min) - *min = iter_->first; - if (max) - *max = strict_cast(iter_->first) + 1; - if (count) - *count = *iter_->second; -} - -void PersistentSampleMapIterator::SkipEmptyBuckets() { - while (!Done() && *iter_->second == 0) { - ++iter_; - } -} - -// This structure holds an entry for a PersistentSampleMap within a persistent -// memory allocator. The "id" must be unique across all maps held by an -// allocator or they will get attached to the wrong sample map. -struct SampleRecord { - // SHA1(SampleRecord): Increment this if structure changes! - static constexpr uint32_t kPersistentTypeId = 0x8FE6A69F + 1; - - // Expected size for 32/64-bit check. - static constexpr size_t kExpectedInstanceSize = 16; - - uint64_t id; // Unique identifier of owner. - Sample value; // The value for which this record holds a count. - Count count; // The count associated with the above value. -}; - -} // namespace - -PersistentSampleMap::PersistentSampleMap( - uint64_t id, - PersistentHistogramAllocator* allocator, - Metadata* meta) - : HistogramSamples(id, meta), allocator_(allocator) {} - -PersistentSampleMap::~PersistentSampleMap() { - if (records_) - records_->Release(this); -} - -void PersistentSampleMap::Accumulate(Sample value, Count count) { -#if 0 // TODO(bcwhite) Re-enable efficient version after crbug.com/682680. - *GetOrCreateSampleCountStorage(value) += count; -#else - Count* local_count_ptr = GetOrCreateSampleCountStorage(value); - if (count < 0) { - if (*local_count_ptr < -count) - RecordNegativeSample(SAMPLES_ACCUMULATE_WENT_NEGATIVE, -count); - else - RecordNegativeSample(SAMPLES_ACCUMULATE_NEGATIVE_COUNT, -count); - *local_count_ptr += count; - } else { - Sample old_value = *local_count_ptr; - Sample new_value = old_value + count; - *local_count_ptr = new_value; - if ((new_value >= 0) != (old_value >= 0)) - RecordNegativeSample(SAMPLES_ACCUMULATE_OVERFLOW, count); - } -#endif - IncreaseSumAndCount(strict_cast(count) * value, count); -} - -Count PersistentSampleMap::GetCount(Sample value) const { - // Have to override "const" to make sure all samples have been loaded before - // being able to know what value to return. - Count* count_pointer = - const_cast(this)->GetSampleCountStorage(value); - return count_pointer ? *count_pointer : 0; -} - -Count PersistentSampleMap::TotalCount() const { - // Have to override "const" in order to make sure all samples have been - // loaded before trying to iterate over the map. - const_cast(this)->ImportSamples(-1, true); - - Count count = 0; - for (const auto& entry : sample_counts_) { - count += *entry.second; - } - return count; -} - -std::unique_ptr PersistentSampleMap::Iterator() const { - // Have to override "const" in order to make sure all samples have been - // loaded before trying to iterate over the map. - const_cast(this)->ImportSamples(-1, true); - return WrapUnique(new PersistentSampleMapIterator(sample_counts_)); -} - -// static -PersistentMemoryAllocator::Reference -PersistentSampleMap::GetNextPersistentRecord( - PersistentMemoryAllocator::Iterator& iterator, - uint64_t* sample_map_id) { - const SampleRecord* record = iterator.GetNextOfObject(); - if (!record) - return 0; - - *sample_map_id = record->id; - return iterator.GetAsReference(record); -} - -// static -PersistentMemoryAllocator::Reference -PersistentSampleMap::CreatePersistentRecord( - PersistentMemoryAllocator* allocator, - uint64_t sample_map_id, - Sample value) { - SampleRecord* record = allocator->New(); - if (!record) { - NOTREACHED() << "full=" << allocator->IsFull() - << ", corrupt=" << allocator->IsCorrupt(); - return 0; - } - - record->id = sample_map_id; - record->value = value; - record->count = 0; - - PersistentMemoryAllocator::Reference ref = allocator->GetAsReference(record); - allocator->MakeIterable(ref); - return ref; -} - -bool PersistentSampleMap::AddSubtractImpl(SampleCountIterator* iter, - Operator op) { - Sample min; - int64_t max; - Count count; - for (; !iter->Done(); iter->Next()) { - iter->Get(&min, &max, &count); - if (count == 0) - continue; - if (strict_cast(min) + 1 != max) - return false; // SparseHistogram only supports bucket with size 1. - *GetOrCreateSampleCountStorage(min) += - (op == HistogramSamples::ADD) ? count : -count; - } - return true; -} - -Count* PersistentSampleMap::GetSampleCountStorage(Sample value) { - // If |value| is already in the map, just return that. - auto it = sample_counts_.find(value); - if (it != sample_counts_.end()) - return it->second; - - // Import any new samples from persistent memory looking for the value. - return ImportSamples(value, false); -} - -Count* PersistentSampleMap::GetOrCreateSampleCountStorage(Sample value) { - // Get any existing count storage. - Count* count_pointer = GetSampleCountStorage(value); - if (count_pointer) - return count_pointer; - - // Create a new record in persistent memory for the value. |records_| will - // have been initialized by the GetSampleCountStorage() call above. - DCHECK(records_); - PersistentMemoryAllocator::Reference ref = records_->CreateNew(value); - if (!ref) { - // If a new record could not be created then the underlying allocator is - // full or corrupt. Instead, allocate the counter from the heap. This - // sample will not be persistent, will not be shared, and will leak... - // but it's better than crashing. - count_pointer = new Count(0); - sample_counts_[value] = count_pointer; - return count_pointer; - } - - // A race condition between two independent processes (i.e. two independent - // histogram objects sharing the same sample data) could cause two of the - // above records to be created. The allocator, however, forces a strict - // ordering on iterable objects so use the import method to actually add the - // just-created record. This ensures that all PersistentSampleMap objects - // will always use the same record, whichever was first made iterable. - // Thread-safety within a process where multiple threads use the same - // histogram object is delegated to the controlling histogram object which, - // for sparse histograms, is a lock object. - count_pointer = ImportSamples(value, false); - DCHECK(count_pointer); - return count_pointer; -} - -PersistentSampleMapRecords* PersistentSampleMap::GetRecords() { - // The |records_| pointer is lazily fetched from the |allocator_| only on - // first use. Sometimes duplicate histograms are created by race conditions - // and if both were to grab the records object, there would be a conflict. - // Use of a histogram, and thus a call to this method, won't occur until - // after the histogram has been de-dup'd. - if (!records_) - records_ = allocator_->UseSampleMapRecords(id(), this); - return records_; -} - -Count* PersistentSampleMap::ImportSamples(Sample until_value, - bool import_everything) { - Count* found_count = nullptr; - PersistentMemoryAllocator::Reference ref; - PersistentSampleMapRecords* records = GetRecords(); - while ((ref = records->GetNext()) != 0) { - SampleRecord* record = records->GetAsObject(ref); - if (!record) - continue; - - DCHECK_EQ(id(), record->id); - - // Check if the record's value is already known. - if (!ContainsKey(sample_counts_, record->value)) { - // No: Add it to map of known values. - sample_counts_[record->value] = &record->count; - } else { - // Yes: Ignore it; it's a duplicate caused by a race condition -- see - // code & comment in GetOrCreateSampleCountStorage() for details. - // Check that nothing ever operated on the duplicate record. - DCHECK_EQ(0, record->count); - } - - // Check if it's the value being searched for and, if so, keep a pointer - // to return later. Stop here unless everything is being imported. - // Because race conditions can cause multiple records for a single value, - // be sure to return the first one found. - if (record->value == until_value) { - if (!found_count) - found_count = &record->count; - if (!import_everything) - break; - } - } - - return found_count; -} - -} // namespace base diff --git a/metrics/persistent_sample_map.h b/metrics/persistent_sample_map.h deleted file mode 100644 index 853f86218..000000000 --- a/metrics/persistent_sample_map.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// PersistentSampleMap implements HistogramSamples interface. It is used -// by the SparseHistogram class to store samples in persistent memory which -// allows it to be shared between processes or live across restarts. - -#ifndef BASE_METRICS_PERSISTENT_SAMPLE_MAP_H_ -#define BASE_METRICS_PERSISTENT_SAMPLE_MAP_H_ - -#include - -#include -#include - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_samples.h" -#include "base/metrics/persistent_memory_allocator.h" - -namespace base { - -class PersistentHistogramAllocator; -class PersistentSampleMapRecords; - -// The logic here is similar to that of SampleMap but with different data -// structures. Changes here likely need to be duplicated there. -class BASE_EXPORT PersistentSampleMap : public HistogramSamples { - public: - // Constructs a persistent sample map using a PersistentHistogramAllocator - // as the data source for persistent records. - PersistentSampleMap(uint64_t id, - PersistentHistogramAllocator* allocator, - Metadata* meta); - - ~PersistentSampleMap() override; - - // HistogramSamples: - void Accumulate(HistogramBase::Sample value, - HistogramBase::Count count) override; - HistogramBase::Count GetCount(HistogramBase::Sample value) const override; - HistogramBase::Count TotalCount() const override; - std::unique_ptr Iterator() const override; - - // Uses a persistent-memory |iterator| to locate and return information about - // the next record holding information for a PersistentSampleMap. The record - // could be for any Map so return the |sample_map_id| as well. - static PersistentMemoryAllocator::Reference GetNextPersistentRecord( - PersistentMemoryAllocator::Iterator& iterator, - uint64_t* sample_map_id); - - // Creates a new record in an |allocator| storing count information for a - // specific sample |value| of a histogram with the given |sample_map_id|. - static PersistentMemoryAllocator::Reference CreatePersistentRecord( - PersistentMemoryAllocator* allocator, - uint64_t sample_map_id, - HistogramBase::Sample value); - - protected: - // Performs arithemetic. |op| is ADD or SUBTRACT. - bool AddSubtractImpl(SampleCountIterator* iter, Operator op) override; - - // Gets a pointer to a "count" corresponding to a given |value|. Returns NULL - // if sample does not exist. - HistogramBase::Count* GetSampleCountStorage(HistogramBase::Sample value); - - // Gets a pointer to a "count" corresponding to a given |value|, creating - // the sample (initialized to zero) if it does not already exists. - HistogramBase::Count* GetOrCreateSampleCountStorage( - HistogramBase::Sample value); - - private: - // Gets the object that manages persistent records. This returns the - // |records_| member after first initializing it if necessary. - PersistentSampleMapRecords* GetRecords(); - - // Imports samples from persistent memory by iterating over all sample - // records found therein, adding them to the sample_counts_ map. If a - // count for the sample |until_value| is found, stop the import and return - // a pointer to that counter. If that value is not found, null will be - // returned after all currently available samples have been loaded. Pass - // true for |import_everything| to force the importing of all available - // samples even if a match is found. - HistogramBase::Count* ImportSamples(HistogramBase::Sample until_value, - bool import_everything); - - // All created/loaded sample values and their associated counts. The storage - // for the actual Count numbers is owned by the |records_| object and its - // underlying allocator. - std::map sample_counts_; - - // The allocator that manages histograms inside persistent memory. This is - // owned externally and is expected to live beyond the life of this object. - PersistentHistogramAllocator* allocator_; - - // The object that manages sample records inside persistent memory. This is - // owned by the |allocator_| object (above) and so, like it, is expected to - // live beyond the life of this object. This value is lazily-initialized on - // first use via the GetRecords() accessor method. - PersistentSampleMapRecords* records_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(PersistentSampleMap); -}; - -} // namespace base - -#endif // BASE_METRICS_PERSISTENT_SAMPLE_MAP_H_ diff --git a/metrics/persistent_sample_map_unittest.cc b/metrics/persistent_sample_map_unittest.cc deleted file mode 100644 index b25f58203..000000000 --- a/metrics/persistent_sample_map_unittest.cc +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) 2016 The Chromium 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/persistent_sample_map.h" - -#include - -#include "base/memory/ptr_util.h" -#include "base/metrics/persistent_histogram_allocator.h" -#include "base/test/gtest_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -std::unique_ptr CreateHistogramAllocator( - size_t bytes) { - return std::make_unique( - std::make_unique(bytes, 0, "")); -} - -std::unique_ptr DuplicateHistogramAllocator( - PersistentHistogramAllocator* original) { - return std::make_unique( - std::make_unique( - const_cast(original->data()), original->length(), 0, - original->Id(), original->Name(), false)); -} - -TEST(PersistentSampleMapTest, AccumulateTest) { - std::unique_ptr allocator = - CreateHistogramAllocator(64 << 10); // 64 KiB - HistogramSamples::LocalMetadata meta; - PersistentSampleMap samples(1, allocator.get(), &meta); - - samples.Accumulate(1, 100); - samples.Accumulate(2, 200); - samples.Accumulate(1, -200); - EXPECT_EQ(-100, samples.GetCount(1)); - EXPECT_EQ(200, samples.GetCount(2)); - - EXPECT_EQ(300, samples.sum()); - EXPECT_EQ(100, samples.TotalCount()); - EXPECT_EQ(samples.redundant_count(), samples.TotalCount()); -} - -TEST(PersistentSampleMapTest, Accumulate_LargeValuesDontOverflow) { - std::unique_ptr allocator = - CreateHistogramAllocator(64 << 10); // 64 KiB - HistogramSamples::LocalMetadata meta; - PersistentSampleMap samples(1, allocator.get(), &meta); - - samples.Accumulate(250000000, 100); - samples.Accumulate(500000000, 200); - samples.Accumulate(250000000, -200); - EXPECT_EQ(-100, samples.GetCount(250000000)); - EXPECT_EQ(200, samples.GetCount(500000000)); - - EXPECT_EQ(75000000000LL, samples.sum()); - EXPECT_EQ(100, samples.TotalCount()); - EXPECT_EQ(samples.redundant_count(), samples.TotalCount()); -} - -TEST(PersistentSampleMapTest, AddSubtractTest) { - std::unique_ptr allocator1 = - CreateHistogramAllocator(64 << 10); // 64 KiB - HistogramSamples::LocalMetadata meta1; - PersistentSampleMap samples1(1, allocator1.get(), &meta1); - samples1.Accumulate(1, 100); - samples1.Accumulate(2, 100); - samples1.Accumulate(3, 100); - - std::unique_ptr allocator2 = - DuplicateHistogramAllocator(allocator1.get()); - HistogramSamples::LocalMetadata meta2; - PersistentSampleMap samples2(2, allocator2.get(), &meta2); - samples2.Accumulate(1, 200); - samples2.Accumulate(2, 200); - samples2.Accumulate(4, 200); - - samples1.Add(samples2); - EXPECT_EQ(300, samples1.GetCount(1)); - EXPECT_EQ(300, samples1.GetCount(2)); - EXPECT_EQ(100, samples1.GetCount(3)); - EXPECT_EQ(200, samples1.GetCount(4)); - EXPECT_EQ(2000, samples1.sum()); - EXPECT_EQ(900, samples1.TotalCount()); - EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount()); - - samples1.Subtract(samples2); - EXPECT_EQ(100, samples1.GetCount(1)); - EXPECT_EQ(100, samples1.GetCount(2)); - EXPECT_EQ(100, samples1.GetCount(3)); - EXPECT_EQ(0, samples1.GetCount(4)); - EXPECT_EQ(600, samples1.sum()); - EXPECT_EQ(300, samples1.TotalCount()); - EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount()); -} - -TEST(PersistentSampleMapTest, PersistenceTest) { - std::unique_ptr allocator1 = - CreateHistogramAllocator(64 << 10); // 64 KiB - HistogramSamples::LocalMetadata meta12; - PersistentSampleMap samples1(12, allocator1.get(), &meta12); - samples1.Accumulate(1, 100); - samples1.Accumulate(2, 200); - samples1.Accumulate(1, -200); - samples1.Accumulate(-1, 1); - EXPECT_EQ(-100, samples1.GetCount(1)); - EXPECT_EQ(200, samples1.GetCount(2)); - EXPECT_EQ(1, samples1.GetCount(-1)); - EXPECT_EQ(299, samples1.sum()); - EXPECT_EQ(101, samples1.TotalCount()); - EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount()); - - std::unique_ptr allocator2 = - DuplicateHistogramAllocator(allocator1.get()); - PersistentSampleMap samples2(12, allocator2.get(), &meta12); - EXPECT_EQ(samples1.id(), samples2.id()); - EXPECT_EQ(samples1.sum(), samples2.sum()); - EXPECT_EQ(samples1.redundant_count(), samples2.redundant_count()); - EXPECT_EQ(samples1.TotalCount(), samples2.TotalCount()); - EXPECT_EQ(-100, samples2.GetCount(1)); - EXPECT_EQ(200, samples2.GetCount(2)); - EXPECT_EQ(1, samples2.GetCount(-1)); - EXPECT_EQ(299, samples2.sum()); - EXPECT_EQ(101, samples2.TotalCount()); - EXPECT_EQ(samples2.redundant_count(), samples2.TotalCount()); - - samples1.Accumulate(-1, -1); - EXPECT_EQ(0, samples2.GetCount(3)); - EXPECT_EQ(0, samples1.GetCount(3)); - samples2.Accumulate(3, 300); - EXPECT_EQ(300, samples2.GetCount(3)); - EXPECT_EQ(300, samples1.GetCount(3)); - EXPECT_EQ(samples1.sum(), samples2.sum()); - EXPECT_EQ(samples1.redundant_count(), samples2.redundant_count()); - EXPECT_EQ(samples1.TotalCount(), samples2.TotalCount()); - - EXPECT_EQ(0, samples2.GetCount(4)); - EXPECT_EQ(0, samples1.GetCount(4)); - samples1.Accumulate(4, 400); - EXPECT_EQ(400, samples2.GetCount(4)); - EXPECT_EQ(400, samples1.GetCount(4)); - samples2.Accumulate(4, 4000); - EXPECT_EQ(4400, samples2.GetCount(4)); - EXPECT_EQ(4400, samples1.GetCount(4)); - EXPECT_EQ(samples1.sum(), samples2.sum()); - EXPECT_EQ(samples1.redundant_count(), samples2.redundant_count()); - EXPECT_EQ(samples1.TotalCount(), samples2.TotalCount()); -} - -TEST(PersistentSampleMapIteratorTest, IterateTest) { - std::unique_ptr allocator = - CreateHistogramAllocator(64 << 10); // 64 KiB - HistogramSamples::LocalMetadata meta; - PersistentSampleMap samples(1, allocator.get(), &meta); - samples.Accumulate(1, 100); - samples.Accumulate(2, 200); - samples.Accumulate(4, -300); - samples.Accumulate(5, 0); - - std::unique_ptr it = samples.Iterator(); - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - - it->Get(&min, &max, &count); - EXPECT_EQ(1, min); - EXPECT_EQ(2, max); - EXPECT_EQ(100, count); - EXPECT_FALSE(it->GetBucketIndex(nullptr)); - - it->Next(); - it->Get(&min, &max, &count); - EXPECT_EQ(2, min); - EXPECT_EQ(3, max); - EXPECT_EQ(200, count); - - it->Next(); - it->Get(&min, &max, &count); - EXPECT_EQ(4, min); - EXPECT_EQ(5, max); - EXPECT_EQ(-300, count); - - it->Next(); - EXPECT_TRUE(it->Done()); -} - -TEST(PersistentSampleMapIteratorTest, SkipEmptyRanges) { - std::unique_ptr allocator1 = - CreateHistogramAllocator(64 << 10); // 64 KiB - HistogramSamples::LocalMetadata meta1; - PersistentSampleMap samples1(1, allocator1.get(), &meta1); - samples1.Accumulate(5, 1); - samples1.Accumulate(10, 2); - samples1.Accumulate(15, 3); - samples1.Accumulate(20, 4); - samples1.Accumulate(25, 5); - - std::unique_ptr allocator2 = - DuplicateHistogramAllocator(allocator1.get()); - HistogramSamples::LocalMetadata meta2; - PersistentSampleMap samples2(2, allocator2.get(), &meta2); - samples2.Accumulate(5, 1); - samples2.Accumulate(20, 4); - samples2.Accumulate(25, 5); - - samples1.Subtract(samples2); - - std::unique_ptr it = samples1.Iterator(); - EXPECT_FALSE(it->Done()); - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - - it->Get(&min, &max, &count); - EXPECT_EQ(10, min); - EXPECT_EQ(11, max); - EXPECT_EQ(2, count); - - it->Next(); - EXPECT_FALSE(it->Done()); - - it->Get(&min, &max, &count); - EXPECT_EQ(15, min); - EXPECT_EQ(16, max); - EXPECT_EQ(3, count); - - it->Next(); - EXPECT_TRUE(it->Done()); -} - -TEST(PersistentSampleMapIteratorDeathTest, IterateDoneTest) { - std::unique_ptr allocator = - CreateHistogramAllocator(64 << 10); // 64 KiB - HistogramSamples::LocalMetadata meta; - PersistentSampleMap samples(1, allocator.get(), &meta); - - std::unique_ptr it = samples.Iterator(); - - EXPECT_TRUE(it->Done()); - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - EXPECT_DCHECK_DEATH(it->Get(&min, &max, &count)); - - EXPECT_DCHECK_DEATH(it->Next()); - - samples.Accumulate(1, 100); - it = samples.Iterator(); - EXPECT_FALSE(it->Done()); -} - -} // namespace -} // namespace base diff --git a/metrics/record_histogram_checker.h b/metrics/record_histogram_checker.h deleted file mode 100644 index 75bc336d1..000000000 --- a/metrics/record_histogram_checker.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2017 The Chromium 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_RECORD_HISTOGRAM_CHECKER_H_ -#define BASE_METRICS_RECORD_HISTOGRAM_CHECKER_H_ - -#include - -#include "base/base_export.h" - -namespace base { - -// RecordHistogramChecker provides an interface for checking whether -// the given histogram should be recorded. -class BASE_EXPORT RecordHistogramChecker { - public: - virtual ~RecordHistogramChecker() = default; - - // Returns true iff the given histogram should be recorded. - // This method may be called on any thread, so it should not mutate any state. - virtual bool ShouldRecord(uint64_t histogram_hash) const = 0; -}; - -} // namespace base - -#endif // BASE_METRICS_RECORD_HISTOGRAM_CHECKER_H_ diff --git a/metrics/sample_map.cc b/metrics/sample_map.cc deleted file mode 100644 index c6dce2932..000000000 --- a/metrics/sample_map.cc +++ /dev/null @@ -1,126 +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/metrics/sample_map.h" - -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/numerics/safe_conversions.h" -#include "base/stl_util.h" - -namespace base { - -typedef HistogramBase::Count Count; -typedef HistogramBase::Sample Sample; - -namespace { - -// An iterator for going through a SampleMap. The logic here is identical -// to that of PersistentSampleMapIterator but with different data structures. -// Changes here likely need to be duplicated there. -class SampleMapIterator : public SampleCountIterator { - public: - typedef std::map - SampleToCountMap; - - explicit SampleMapIterator(const SampleToCountMap& sample_counts); - ~SampleMapIterator() override; - - // SampleCountIterator: - bool Done() const override; - void Next() override; - void Get(HistogramBase::Sample* min, - int64_t* max, - HistogramBase::Count* count) const override; - - private: - void SkipEmptyBuckets(); - - SampleToCountMap::const_iterator iter_; - const SampleToCountMap::const_iterator end_; -}; - -SampleMapIterator::SampleMapIterator(const SampleToCountMap& sample_counts) - : iter_(sample_counts.begin()), - end_(sample_counts.end()) { - SkipEmptyBuckets(); -} - -SampleMapIterator::~SampleMapIterator() = default; - -bool SampleMapIterator::Done() const { - return iter_ == end_; -} - -void SampleMapIterator::Next() { - DCHECK(!Done()); - ++iter_; - SkipEmptyBuckets(); -} - -void SampleMapIterator::Get(Sample* min, int64_t* max, Count* count) const { - DCHECK(!Done()); - if (min) - *min = iter_->first; - if (max) - *max = strict_cast(iter_->first) + 1; - if (count) - *count = iter_->second; -} - -void SampleMapIterator::SkipEmptyBuckets() { - while (!Done() && iter_->second == 0) { - ++iter_; - } -} - -} // namespace - -SampleMap::SampleMap() : SampleMap(0) {} - -SampleMap::SampleMap(uint64_t id) : HistogramSamples(id, new LocalMetadata()) {} - -SampleMap::~SampleMap() { - delete static_cast(meta()); -} - -void SampleMap::Accumulate(Sample value, Count count) { - sample_counts_[value] += count; - IncreaseSumAndCount(strict_cast(count) * value, count); -} - -Count SampleMap::GetCount(Sample value) const { - std::map::const_iterator it = sample_counts_.find(value); - if (it == sample_counts_.end()) - return 0; - return it->second; -} - -Count SampleMap::TotalCount() const { - Count count = 0; - for (const auto& entry : sample_counts_) { - count += entry.second; - } - return count; -} - -std::unique_ptr SampleMap::Iterator() const { - return WrapUnique(new SampleMapIterator(sample_counts_)); -} - -bool SampleMap::AddSubtractImpl(SampleCountIterator* iter, Operator op) { - Sample min; - int64_t max; - Count count; - for (; !iter->Done(); iter->Next()) { - iter->Get(&min, &max, &count); - if (strict_cast(min) + 1 != max) - return false; // SparseHistogram only supports bucket with size 1. - - sample_counts_[min] += (op == HistogramSamples::ADD) ? count : -count; - } - return true; -} - -} // namespace base diff --git a/metrics/sample_map.h b/metrics/sample_map.h deleted file mode 100644 index 7458e05e9..000000000 --- a/metrics/sample_map.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// SampleMap implements HistogramSamples interface. It is used by the -// SparseHistogram class to store samples. - -#ifndef BASE_METRICS_SAMPLE_MAP_H_ -#define BASE_METRICS_SAMPLE_MAP_H_ - -#include - -#include -#include - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_samples.h" - -namespace base { - -// The logic here is similar to that of PersistentSampleMap but with different -// data structures. Changes here likely need to be duplicated there. -class BASE_EXPORT SampleMap : public HistogramSamples { - public: - SampleMap(); - explicit SampleMap(uint64_t id); - ~SampleMap() override; - - // HistogramSamples: - void Accumulate(HistogramBase::Sample value, - HistogramBase::Count count) override; - HistogramBase::Count GetCount(HistogramBase::Sample value) const override; - HistogramBase::Count TotalCount() const override; - std::unique_ptr Iterator() const override; - - protected: - // Performs arithemetic. |op| is ADD or SUBTRACT. - bool AddSubtractImpl(SampleCountIterator* iter, Operator op) override; - - private: - std::map sample_counts_; - - DISALLOW_COPY_AND_ASSIGN(SampleMap); -}; - -} // namespace base - -#endif // BASE_METRICS_SAMPLE_MAP_H_ diff --git a/metrics/sample_map_unittest.cc b/metrics/sample_map_unittest.cc deleted file mode 100644 index 83db56f50..000000000 --- a/metrics/sample_map_unittest.cc +++ /dev/null @@ -1,168 +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/metrics/sample_map.h" - -#include - -#include "base/test/gtest_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace { - -TEST(SampleMapTest, AccumulateTest) { - SampleMap samples(1); - - samples.Accumulate(1, 100); - samples.Accumulate(2, 200); - samples.Accumulate(1, -200); - EXPECT_EQ(-100, samples.GetCount(1)); - EXPECT_EQ(200, samples.GetCount(2)); - - EXPECT_EQ(300, samples.sum()); - EXPECT_EQ(100, samples.TotalCount()); - EXPECT_EQ(samples.redundant_count(), samples.TotalCount()); -} - -TEST(SampleMapTest, Accumulate_LargeValuesDontOverflow) { - SampleMap samples(1); - - samples.Accumulate(250000000, 100); - samples.Accumulate(500000000, 200); - samples.Accumulate(250000000, -200); - EXPECT_EQ(-100, samples.GetCount(250000000)); - EXPECT_EQ(200, samples.GetCount(500000000)); - - EXPECT_EQ(75000000000LL, samples.sum()); - EXPECT_EQ(100, samples.TotalCount()); - EXPECT_EQ(samples.redundant_count(), samples.TotalCount()); -} - -TEST(SampleMapTest, AddSubtractTest) { - SampleMap samples1(1); - SampleMap samples2(2); - - samples1.Accumulate(1, 100); - samples1.Accumulate(2, 100); - samples1.Accumulate(3, 100); - - samples2.Accumulate(1, 200); - samples2.Accumulate(2, 200); - samples2.Accumulate(4, 200); - - samples1.Add(samples2); - EXPECT_EQ(300, samples1.GetCount(1)); - EXPECT_EQ(300, samples1.GetCount(2)); - EXPECT_EQ(100, samples1.GetCount(3)); - EXPECT_EQ(200, samples1.GetCount(4)); - EXPECT_EQ(2000, samples1.sum()); - EXPECT_EQ(900, samples1.TotalCount()); - EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount()); - - samples1.Subtract(samples2); - EXPECT_EQ(100, samples1.GetCount(1)); - EXPECT_EQ(100, samples1.GetCount(2)); - EXPECT_EQ(100, samples1.GetCount(3)); - EXPECT_EQ(0, samples1.GetCount(4)); - EXPECT_EQ(600, samples1.sum()); - EXPECT_EQ(300, samples1.TotalCount()); - EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount()); -} - -TEST(SampleMapIteratorTest, IterateTest) { - SampleMap samples(1); - samples.Accumulate(1, 100); - samples.Accumulate(2, 200); - samples.Accumulate(4, -300); - samples.Accumulate(5, 0); - - std::unique_ptr it = samples.Iterator(); - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - - it->Get(&min, &max, &count); - EXPECT_EQ(1, min); - EXPECT_EQ(2, max); - EXPECT_EQ(100, count); - EXPECT_FALSE(it->GetBucketIndex(nullptr)); - - it->Next(); - it->Get(&min, &max, &count); - EXPECT_EQ(2, min); - EXPECT_EQ(3, max); - EXPECT_EQ(200, count); - - it->Next(); - it->Get(&min, &max, &count); - EXPECT_EQ(4, min); - EXPECT_EQ(5, max); - EXPECT_EQ(-300, count); - - it->Next(); - EXPECT_TRUE(it->Done()); -} - -TEST(SampleMapIteratorTest, SkipEmptyRanges) { - SampleMap samples(1); - samples.Accumulate(5, 1); - samples.Accumulate(10, 2); - samples.Accumulate(15, 3); - samples.Accumulate(20, 4); - samples.Accumulate(25, 5); - - SampleMap samples2(2); - samples2.Accumulate(5, 1); - samples2.Accumulate(20, 4); - samples2.Accumulate(25, 5); - - samples.Subtract(samples2); - - std::unique_ptr it = samples.Iterator(); - EXPECT_FALSE(it->Done()); - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - - it->Get(&min, &max, &count); - EXPECT_EQ(10, min); - EXPECT_EQ(11, max); - EXPECT_EQ(2, count); - - it->Next(); - EXPECT_FALSE(it->Done()); - - it->Get(&min, &max, &count); - EXPECT_EQ(15, min); - EXPECT_EQ(16, max); - EXPECT_EQ(3, count); - - it->Next(); - EXPECT_TRUE(it->Done()); -} - -TEST(SampleMapIteratorDeathTest, IterateDoneTest) { - SampleMap samples(1); - - std::unique_ptr it = samples.Iterator(); - - EXPECT_TRUE(it->Done()); - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - EXPECT_DCHECK_DEATH(it->Get(&min, &max, &count)); - - EXPECT_DCHECK_DEATH(it->Next()); - - samples.Accumulate(1, 100); - it = samples.Iterator(); - EXPECT_FALSE(it->Done()); -} - -} // namespace -} // namespace base diff --git a/metrics/sample_vector.cc b/metrics/sample_vector.cc deleted file mode 100644 index cf8634e83..000000000 --- a/metrics/sample_vector.cc +++ /dev/null @@ -1,429 +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/metrics/sample_vector.h" - -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/numerics/safe_conversions.h" -#include "base/synchronization/lock.h" -#include "base/threading/platform_thread.h" - -// This SampleVector makes use of the single-sample embedded in the base -// HistogramSamples class. If the count is non-zero then there is guaranteed -// (within the bounds of "eventual consistency") to be no allocated external -// storage. Once the full counts storage is allocated, the single-sample must -// be extracted and disabled. - -namespace base { - -typedef HistogramBase::Count Count; -typedef HistogramBase::Sample Sample; - -SampleVectorBase::SampleVectorBase(uint64_t id, - Metadata* meta, - const BucketRanges* bucket_ranges) - : HistogramSamples(id, meta), bucket_ranges_(bucket_ranges) { - CHECK_GE(bucket_ranges_->bucket_count(), 1u); -} - -SampleVectorBase::~SampleVectorBase() = default; - -void SampleVectorBase::Accumulate(Sample value, Count count) { - const size_t bucket_index = GetBucketIndex(value); - - // Handle the single-sample case. - if (!counts()) { - // Try to accumulate the parameters into the single-count entry. - if (AccumulateSingleSample(value, count, bucket_index)) { - // A race condition could lead to a new single-sample being accumulated - // above just after another thread executed the MountCountsStorage below. - // Since it is mounted, it could be mounted elsewhere and have values - // written to it. It's not allowed to have both a single-sample and - // entries in the counts array so move the single-sample. - if (counts()) - MoveSingleSampleToCounts(); - return; - } - - // Need real storage to store both what was in the single-sample plus the - // parameter information. - MountCountsStorageAndMoveSingleSample(); - } - - // Handle the multi-sample case. - Count new_value = - subtle::NoBarrier_AtomicIncrement(&counts()[bucket_index], count); - IncreaseSumAndCount(strict_cast(count) * value, count); - - // TODO(bcwhite) Remove after crbug.com/682680. - Count old_value = new_value - count; - if ((new_value >= 0) != (old_value >= 0) && count > 0) - RecordNegativeSample(SAMPLES_ACCUMULATE_OVERFLOW, count); -} - -Count SampleVectorBase::GetCount(Sample value) const { - return GetCountAtIndex(GetBucketIndex(value)); -} - -Count SampleVectorBase::TotalCount() const { - // Handle the single-sample case. - SingleSample sample = single_sample().Load(); - if (sample.count != 0) - return sample.count; - - // Handle the multi-sample case. - if (counts() || MountExistingCountsStorage()) { - Count count = 0; - size_t size = counts_size(); - const HistogramBase::AtomicCount* counts_array = counts(); - for (size_t i = 0; i < size; ++i) { - count += subtle::NoBarrier_Load(&counts_array[i]); - } - return count; - } - - // And the no-value case. - return 0; -} - -Count SampleVectorBase::GetCountAtIndex(size_t bucket_index) const { - DCHECK(bucket_index < counts_size()); - - // Handle the single-sample case. - SingleSample sample = single_sample().Load(); - if (sample.count != 0) - return sample.bucket == bucket_index ? sample.count : 0; - - // Handle the multi-sample case. - if (counts() || MountExistingCountsStorage()) - return subtle::NoBarrier_Load(&counts()[bucket_index]); - - // And the no-value case. - return 0; -} - -std::unique_ptr SampleVectorBase::Iterator() const { - // Handle the single-sample case. - SingleSample sample = single_sample().Load(); - if (sample.count != 0) { - return std::make_unique( - bucket_ranges_->range(sample.bucket), - bucket_ranges_->range(sample.bucket + 1), sample.count, sample.bucket); - } - - // Handle the multi-sample case. - if (counts() || MountExistingCountsStorage()) { - return std::make_unique(counts(), counts_size(), - bucket_ranges_); - } - - // And the no-value case. - return std::make_unique(nullptr, 0, bucket_ranges_); -} - -bool SampleVectorBase::AddSubtractImpl(SampleCountIterator* iter, - HistogramSamples::Operator op) { - // Stop now if there's nothing to do. - if (iter->Done()) - return true; - - // Get the first value and its index. - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - iter->Get(&min, &max, &count); - size_t dest_index = GetBucketIndex(min); - - // The destination must be a superset of the source meaning that though the - // incoming ranges will find an exact match, the incoming bucket-index, if - // it exists, may be offset from the destination bucket-index. Calculate - // that offset of the passed iterator; there are are no overflow checks - // because 2's compliment math will work it out in the end. - // - // Because GetBucketIndex() always returns the same true or false result for - // a given iterator object, |index_offset| is either set here and used below, - // or never set and never used. The compiler doesn't know this, though, which - // is why it's necessary to initialize it to something. - size_t index_offset = 0; - size_t iter_index; - if (iter->GetBucketIndex(&iter_index)) - index_offset = dest_index - iter_index; - if (dest_index >= counts_size()) - return false; - - // Post-increment. Information about the current sample is not available - // after this point. - iter->Next(); - - // Single-value storage is possible if there is no counts storage and the - // retrieved entry is the only one in the iterator. - if (!counts()) { - if (iter->Done()) { - // Don't call AccumulateSingleSample because that updates sum and count - // which was already done by the caller of this method. - if (single_sample().Accumulate( - dest_index, op == HistogramSamples::ADD ? count : -count)) { - // Handle race-condition that mounted counts storage between above and - // here. - if (counts()) - MoveSingleSampleToCounts(); - return true; - } - } - - // The counts storage will be needed to hold the multiple incoming values. - MountCountsStorageAndMoveSingleSample(); - } - - // Go through the iterator and add the counts into correct bucket. - while (true) { - // Ensure that the sample's min/max match the ranges min/max. - if (min != bucket_ranges_->range(dest_index) || - max != bucket_ranges_->range(dest_index + 1)) { - NOTREACHED() << "sample=" << min << "," << max - << "; range=" << bucket_ranges_->range(dest_index) << "," - << bucket_ranges_->range(dest_index + 1); - return false; - } - - // Sample's bucket matches exactly. Adjust count. - subtle::NoBarrier_AtomicIncrement( - &counts()[dest_index], op == HistogramSamples::ADD ? count : -count); - - // Advance to the next iterable sample. See comments above for how - // everything works. - if (iter->Done()) - return true; - iter->Get(&min, &max, &count); - if (iter->GetBucketIndex(&iter_index)) { - // Destination bucket is a known offset from the source bucket. - dest_index = iter_index + index_offset; - } else { - // Destination bucket has to be determined anew each time. - dest_index = GetBucketIndex(min); - } - if (dest_index >= counts_size()) - return false; - iter->Next(); - } -} - -// Use simple binary search. This is very general, but there are better -// approaches if we knew that the buckets were linearly distributed. -size_t SampleVectorBase::GetBucketIndex(Sample value) const { - size_t bucket_count = bucket_ranges_->bucket_count(); - CHECK_GE(bucket_count, 1u); - CHECK_GE(value, bucket_ranges_->range(0)); - CHECK_LT(value, bucket_ranges_->range(bucket_count)); - - size_t under = 0; - size_t over = bucket_count; - size_t mid; - do { - DCHECK_GE(over, under); - mid = under + (over - under)/2; - if (mid == under) - break; - if (bucket_ranges_->range(mid) <= value) - under = mid; - else - over = mid; - } while (true); - - DCHECK_LE(bucket_ranges_->range(mid), value); - CHECK_GT(bucket_ranges_->range(mid + 1), value); - return mid; -} - -void SampleVectorBase::MoveSingleSampleToCounts() { - DCHECK(counts()); - - // Disable the single-sample since there is now counts storage for the data. - SingleSample sample = single_sample().Extract(/*disable=*/true); - - // Stop here if there is no "count" as trying to find the bucket index of - // an invalid (including zero) "value" will crash. - if (sample.count == 0) - return; - - // Move the value into storage. Sum and redundant-count already account - // for this entry so no need to call IncreaseSumAndCount(). - subtle::NoBarrier_AtomicIncrement(&counts()[sample.bucket], sample.count); -} - -void SampleVectorBase::MountCountsStorageAndMoveSingleSample() { - // There are many SampleVector objects and the lock is needed very - // infrequently (just when advancing from single-sample to multi-sample) so - // define a single, global lock that all can use. This lock only prevents - // concurrent entry into the code below; access and updates to |counts_| - // still requires atomic operations. - static LazyInstance::Leaky counts_lock = LAZY_INSTANCE_INITIALIZER; - if (subtle::NoBarrier_Load(&counts_) == 0) { - AutoLock lock(counts_lock.Get()); - if (subtle::NoBarrier_Load(&counts_) == 0) { - // Create the actual counts storage while the above lock is acquired. - HistogramBase::Count* counts = CreateCountsStorageWhileLocked(); - DCHECK(counts); - - // Point |counts_| to the newly created storage. This is done while - // locked to prevent possible concurrent calls to CreateCountsStorage - // but, between that call and here, other threads could notice the - // existence of the storage and race with this to set_counts(). That's - // okay because (a) it's atomic and (b) it always writes the same value. - set_counts(counts); - } - } - - // Move any single-sample into the newly mounted storage. - MoveSingleSampleToCounts(); -} - -SampleVector::SampleVector(const BucketRanges* bucket_ranges) - : SampleVector(0, bucket_ranges) {} - -SampleVector::SampleVector(uint64_t id, const BucketRanges* bucket_ranges) - : SampleVectorBase(id, new LocalMetadata(), bucket_ranges) {} - -SampleVector::~SampleVector() { - delete static_cast(meta()); -} - -bool SampleVector::MountExistingCountsStorage() const { - // There is never any existing storage other than what is already in use. - return counts() != nullptr; -} - -HistogramBase::AtomicCount* SampleVector::CreateCountsStorageWhileLocked() { - local_counts_.resize(counts_size()); - return &local_counts_[0]; -} - -PersistentSampleVector::PersistentSampleVector( - uint64_t id, - const BucketRanges* bucket_ranges, - Metadata* meta, - const DelayedPersistentAllocation& counts) - : SampleVectorBase(id, meta, bucket_ranges), persistent_counts_(counts) { - // Only mount the full storage if the single-sample has been disabled. - // Otherwise, it is possible for this object instance to start using (empty) - // storage that was created incidentally while another instance continues to - // update to the single sample. This "incidental creation" can happen because - // the memory is a DelayedPersistentAllocation which allows multiple memory - // blocks within it and applies an all-or-nothing approach to the allocation. - // Thus, a request elsewhere for one of the _other_ blocks would make _this_ - // block available even though nothing has explicitly requested it. - // - // Note that it's not possible for the ctor to mount existing storage and - // move any single-sample to it because sometimes the persistent memory is - // read-only. Only non-const methods (which assume that memory is read/write) - // can do that. - if (single_sample().IsDisabled()) { - bool success = MountExistingCountsStorage(); - DCHECK(success); - } -} - -PersistentSampleVector::~PersistentSampleVector() = default; - -bool PersistentSampleVector::MountExistingCountsStorage() const { - // There is no early exit if counts is not yet mounted because, given that - // this is a virtual function, it's more efficient to do that at the call- - // site. There is no danger, however, should this get called anyway (perhaps - // because of a race condition) because at worst the |counts_| value would - // be over-written (in an atomic manner) with the exact same address. - - if (!persistent_counts_.reference()) - return false; // Nothing to mount. - - // Mount the counts array in position. - set_counts( - static_cast(persistent_counts_.Get())); - - // The above shouldn't fail but can if the data is corrupt or incomplete. - return counts() != nullptr; -} - -HistogramBase::AtomicCount* -PersistentSampleVector::CreateCountsStorageWhileLocked() { - void* mem = persistent_counts_.Get(); - if (!mem) { - // The above shouldn't fail but can if Bad Things(tm) are occurring in the - // persistent allocator. Crashing isn't a good option so instead just - // allocate something from the heap and return that. There will be no - // sharing or persistence but worse things are already happening. - return new HistogramBase::AtomicCount[counts_size()]; - } - - return static_cast(mem); -} - -SampleVectorIterator::SampleVectorIterator( - const std::vector* counts, - const BucketRanges* bucket_ranges) - : counts_(&(*counts)[0]), - counts_size_(counts->size()), - bucket_ranges_(bucket_ranges), - index_(0) { - DCHECK_GE(bucket_ranges_->bucket_count(), counts_size_); - SkipEmptyBuckets(); -} - -SampleVectorIterator::SampleVectorIterator( - const HistogramBase::AtomicCount* counts, - size_t counts_size, - const BucketRanges* bucket_ranges) - : counts_(counts), - counts_size_(counts_size), - bucket_ranges_(bucket_ranges), - index_(0) { - DCHECK_GE(bucket_ranges_->bucket_count(), counts_size_); - SkipEmptyBuckets(); -} - -SampleVectorIterator::~SampleVectorIterator() = default; - -bool SampleVectorIterator::Done() const { - return index_ >= counts_size_; -} - -void SampleVectorIterator::Next() { - DCHECK(!Done()); - index_++; - SkipEmptyBuckets(); -} - -void SampleVectorIterator::Get(HistogramBase::Sample* min, - int64_t* max, - HistogramBase::Count* count) const { - DCHECK(!Done()); - if (min != nullptr) - *min = bucket_ranges_->range(index_); - if (max != nullptr) - *max = strict_cast(bucket_ranges_->range(index_ + 1)); - if (count != nullptr) - *count = subtle::NoBarrier_Load(&counts_[index_]); -} - -bool SampleVectorIterator::GetBucketIndex(size_t* index) const { - DCHECK(!Done()); - if (index != nullptr) - *index = index_; - return true; -} - -void SampleVectorIterator::SkipEmptyBuckets() { - if (Done()) - return; - - while (index_ < counts_size_) { - if (subtle::NoBarrier_Load(&counts_[index_]) != 0) - return; - index_++; - } -} - -} // namespace base diff --git a/metrics/sample_vector.h b/metrics/sample_vector.h deleted file mode 100644 index 278272da1..000000000 --- a/metrics/sample_vector.h +++ /dev/null @@ -1,185 +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. - -// SampleVector implements HistogramSamples interface. It is used by all -// Histogram based classes to store samples. - -#ifndef BASE_METRICS_SAMPLE_VECTOR_H_ -#define BASE_METRICS_SAMPLE_VECTOR_H_ - -#include -#include - -#include -#include - -#include "base/atomicops.h" -#include "base/compiler_specific.h" -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/metrics/bucket_ranges.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_samples.h" -#include "base/metrics/persistent_memory_allocator.h" - -namespace base { - -class BucketRanges; - -class BASE_EXPORT SampleVectorBase : public HistogramSamples { - public: - SampleVectorBase(uint64_t id, - Metadata* meta, - const BucketRanges* bucket_ranges); - ~SampleVectorBase() override; - - // HistogramSamples: - void Accumulate(HistogramBase::Sample value, - HistogramBase::Count count) override; - HistogramBase::Count GetCount(HistogramBase::Sample value) const override; - HistogramBase::Count TotalCount() const override; - std::unique_ptr Iterator() const override; - - // Get count of a specific bucket. - HistogramBase::Count GetCountAtIndex(size_t bucket_index) const; - - // Access the bucket ranges held externally. - const BucketRanges* bucket_ranges() const { return bucket_ranges_; } - - protected: - bool AddSubtractImpl( - SampleCountIterator* iter, - HistogramSamples::Operator op) override; // |op| is ADD or SUBTRACT. - - virtual size_t GetBucketIndex(HistogramBase::Sample value) const; - - // Moves the single-sample value to a mounted "counts" array. - void MoveSingleSampleToCounts(); - - // Mounts (creating if necessary) an array of "counts" for multi-value - // storage. - void MountCountsStorageAndMoveSingleSample(); - - // Mounts "counts" storage that already exists. This does not attempt to move - // any single-sample information to that storage as that would violate the - // "const" restriction that is often used to indicate read-only memory. - virtual bool MountExistingCountsStorage() const = 0; - - // Creates "counts" storage and returns a pointer to it. Ownership of the - // array remains with the called method but will never change. This must be - // called while some sort of lock is held to prevent reentry. - virtual HistogramBase::Count* CreateCountsStorageWhileLocked() = 0; - - HistogramBase::AtomicCount* counts() { - return reinterpret_cast( - subtle::Acquire_Load(&counts_)); - } - - const HistogramBase::AtomicCount* counts() const { - return reinterpret_cast( - subtle::Acquire_Load(&counts_)); - } - - void set_counts(const HistogramBase::AtomicCount* counts) const { - subtle::Release_Store(&counts_, reinterpret_cast(counts)); - } - - size_t counts_size() const { return bucket_ranges_->bucket_count(); } - - private: - friend class SampleVectorTest; - FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts); - FRIEND_TEST_ALL_PREFIXES(SharedHistogramTest, CorruptSampleCounts); - - // |counts_| is actually a pointer to a HistogramBase::AtomicCount array but - // is held as an AtomicWord for concurrency reasons. When combined with the - // single_sample held in the metadata, there are four possible states: - // 1) single_sample == zero, counts_ == null - // 2) single_sample != zero, counts_ == null - // 3) single_sample != zero, counts_ != null BUT IS EMPTY - // 4) single_sample == zero, counts_ != null and may have data - // Once |counts_| is set, it can never revert and any existing single-sample - // must be moved to this storage. It is mutable because changing it doesn't - // change the (const) data but must adapt if a non-const object causes the - // storage to be allocated and updated. - mutable subtle::AtomicWord counts_ = 0; - - // Shares the same BucketRanges with Histogram object. - const BucketRanges* const bucket_ranges_; - - DISALLOW_COPY_AND_ASSIGN(SampleVectorBase); -}; - -// A sample vector that uses local memory for the counts array. -class BASE_EXPORT SampleVector : public SampleVectorBase { - public: - explicit SampleVector(const BucketRanges* bucket_ranges); - SampleVector(uint64_t id, const BucketRanges* bucket_ranges); - ~SampleVector() override; - - private: - // SampleVectorBase: - bool MountExistingCountsStorage() const override; - HistogramBase::Count* CreateCountsStorageWhileLocked() override; - - // Simple local storage for counts. - mutable std::vector local_counts_; - - DISALLOW_COPY_AND_ASSIGN(SampleVector); -}; - -// A sample vector that uses persistent memory for the counts array. -class BASE_EXPORT PersistentSampleVector : public SampleVectorBase { - public: - PersistentSampleVector(uint64_t id, - const BucketRanges* bucket_ranges, - Metadata* meta, - const DelayedPersistentAllocation& counts); - ~PersistentSampleVector() override; - - private: - // SampleVectorBase: - bool MountExistingCountsStorage() const override; - HistogramBase::Count* CreateCountsStorageWhileLocked() override; - - // Persistent storage for counts. - DelayedPersistentAllocation persistent_counts_; - - DISALLOW_COPY_AND_ASSIGN(PersistentSampleVector); -}; - -// An iterator for sample vectors. This could be defined privately in the .cc -// file but is here for easy testing. -class BASE_EXPORT SampleVectorIterator : public SampleCountIterator { - public: - SampleVectorIterator(const std::vector* counts, - const BucketRanges* bucket_ranges); - SampleVectorIterator(const HistogramBase::AtomicCount* counts, - size_t counts_size, - const BucketRanges* bucket_ranges); - ~SampleVectorIterator() override; - - // SampleCountIterator implementation: - bool Done() const override; - void Next() override; - void Get(HistogramBase::Sample* min, - int64_t* max, - HistogramBase::Count* count) const override; - - // SampleVector uses predefined buckets, so iterator can return bucket index. - bool GetBucketIndex(size_t* index) const override; - - private: - void SkipEmptyBuckets(); - - const HistogramBase::AtomicCount* counts_; - size_t counts_size_; - const BucketRanges* bucket_ranges_; - - size_t index_; -}; - -} // namespace base - -#endif // BASE_METRICS_SAMPLE_VECTOR_H_ diff --git a/metrics/sample_vector_unittest.cc b/metrics/sample_vector_unittest.cc deleted file mode 100644 index 49218023d..000000000 --- a/metrics/sample_vector_unittest.cc +++ /dev/null @@ -1,545 +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/metrics/sample_vector.h" - -#include -#include - -#include -#include -#include - -#include "base/metrics/bucket_ranges.h" -#include "base/metrics/histogram.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/test/gtest_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -// This framework class has "friend" access to the SampleVector for accessing -// non-public methods and fields. -class SampleVectorTest : public testing::Test { - public: - const HistogramBase::AtomicCount* GetSamplesCounts( - const SampleVectorBase& samples) { - return samples.counts(); - } -}; - -TEST_F(SampleVectorTest, Accumulate) { - // Custom buckets: [1, 5) [5, 10) - BucketRanges ranges(3); - ranges.set_range(0, 1); - ranges.set_range(1, 5); - ranges.set_range(2, 10); - SampleVector samples(1, &ranges); - - samples.Accumulate(1, 200); - samples.Accumulate(2, -300); - EXPECT_EQ(-100, samples.GetCountAtIndex(0)); - - samples.Accumulate(5, 200); - EXPECT_EQ(200, samples.GetCountAtIndex(1)); - - EXPECT_EQ(600, samples.sum()); - EXPECT_EQ(100, samples.redundant_count()); - EXPECT_EQ(samples.TotalCount(), samples.redundant_count()); - - samples.Accumulate(5, -100); - EXPECT_EQ(100, samples.GetCountAtIndex(1)); - - EXPECT_EQ(100, samples.sum()); - EXPECT_EQ(0, samples.redundant_count()); - EXPECT_EQ(samples.TotalCount(), samples.redundant_count()); -} - -TEST_F(SampleVectorTest, Accumulate_LargeValuesDontOverflow) { - // Custom buckets: [1, 250000000) [250000000, 500000000) - BucketRanges ranges(3); - ranges.set_range(0, 1); - ranges.set_range(1, 250000000); - ranges.set_range(2, 500000000); - SampleVector samples(1, &ranges); - - samples.Accumulate(240000000, 200); - samples.Accumulate(249999999, -300); - EXPECT_EQ(-100, samples.GetCountAtIndex(0)); - - samples.Accumulate(250000000, 200); - EXPECT_EQ(200, samples.GetCountAtIndex(1)); - - EXPECT_EQ(23000000300LL, samples.sum()); - EXPECT_EQ(100, samples.redundant_count()); - EXPECT_EQ(samples.TotalCount(), samples.redundant_count()); - - samples.Accumulate(250000000, -100); - EXPECT_EQ(100, samples.GetCountAtIndex(1)); - - EXPECT_EQ(-1999999700LL, samples.sum()); - EXPECT_EQ(0, samples.redundant_count()); - EXPECT_EQ(samples.TotalCount(), samples.redundant_count()); -} - -TEST_F(SampleVectorTest, AddSubtract) { - // Custom buckets: [0, 1) [1, 2) [2, 3) [3, INT_MAX) - BucketRanges ranges(5); - ranges.set_range(0, 0); - ranges.set_range(1, 1); - ranges.set_range(2, 2); - ranges.set_range(3, 3); - ranges.set_range(4, INT_MAX); - - SampleVector samples1(1, &ranges); - samples1.Accumulate(0, 100); - samples1.Accumulate(2, 100); - samples1.Accumulate(4, 100); - EXPECT_EQ(600, samples1.sum()); - EXPECT_EQ(300, samples1.TotalCount()); - EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount()); - - SampleVector samples2(2, &ranges); - samples2.Accumulate(1, 200); - samples2.Accumulate(2, 200); - samples2.Accumulate(4, 200); - EXPECT_EQ(1400, samples2.sum()); - EXPECT_EQ(600, samples2.TotalCount()); - EXPECT_EQ(samples2.redundant_count(), samples2.TotalCount()); - - samples1.Add(samples2); - EXPECT_EQ(100, samples1.GetCountAtIndex(0)); - EXPECT_EQ(200, samples1.GetCountAtIndex(1)); - EXPECT_EQ(300, samples1.GetCountAtIndex(2)); - EXPECT_EQ(300, samples1.GetCountAtIndex(3)); - EXPECT_EQ(2000, samples1.sum()); - EXPECT_EQ(900, samples1.TotalCount()); - EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount()); - - samples1.Subtract(samples2); - EXPECT_EQ(100, samples1.GetCountAtIndex(0)); - EXPECT_EQ(0, samples1.GetCountAtIndex(1)); - EXPECT_EQ(100, samples1.GetCountAtIndex(2)); - EXPECT_EQ(100, samples1.GetCountAtIndex(3)); - EXPECT_EQ(600, samples1.sum()); - EXPECT_EQ(300, samples1.TotalCount()); - EXPECT_EQ(samples1.redundant_count(), samples1.TotalCount()); -} - -TEST_F(SampleVectorTest, BucketIndexDeath) { - // 8 buckets with exponential layout: - // [0, 1) [1, 2) [2, 4) [4, 8) [8, 16) [16, 32) [32, 64) [64, INT_MAX) - BucketRanges ranges(9); - Histogram::InitializeBucketRanges(1, 64, &ranges); - SampleVector samples(1, &ranges); - - // Normal case - samples.Accumulate(0, 1); - samples.Accumulate(3, 2); - samples.Accumulate(64, 3); - EXPECT_EQ(1, samples.GetCount(0)); - EXPECT_EQ(2, samples.GetCount(2)); - EXPECT_EQ(3, samples.GetCount(65)); - - // Extreme case. - EXPECT_DEATH_IF_SUPPORTED(samples.Accumulate(INT_MIN, 100), ""); - EXPECT_DEATH_IF_SUPPORTED(samples.Accumulate(-1, 100), ""); - EXPECT_DEATH_IF_SUPPORTED(samples.Accumulate(INT_MAX, 100), ""); - - // Custom buckets: [1, 5) [5, 10) - // Note, this is not a valid BucketRanges for Histogram because it does not - // have overflow buckets. - BucketRanges ranges2(3); - ranges2.set_range(0, 1); - ranges2.set_range(1, 5); - ranges2.set_range(2, 10); - SampleVector samples2(2, &ranges2); - - // Normal case. - samples2.Accumulate(1, 1); - samples2.Accumulate(4, 1); - samples2.Accumulate(5, 2); - samples2.Accumulate(9, 2); - EXPECT_EQ(2, samples2.GetCount(1)); - EXPECT_EQ(4, samples2.GetCount(5)); - - // Extreme case. - EXPECT_DEATH_IF_SUPPORTED(samples2.Accumulate(0, 100), ""); - EXPECT_DEATH_IF_SUPPORTED(samples2.Accumulate(10, 100), ""); -} - -TEST_F(SampleVectorTest, AddSubtractBucketNotMatchDeath) { - // Custom buckets 1: [1, 3) [3, 5) - BucketRanges ranges1(3); - ranges1.set_range(0, 1); - ranges1.set_range(1, 3); - ranges1.set_range(2, 5); - SampleVector samples1(1, &ranges1); - - // Custom buckets 2: [0, 1) [1, 3) [3, 6) [6, 7) - BucketRanges ranges2(5); - ranges2.set_range(0, 0); - ranges2.set_range(1, 1); - ranges2.set_range(2, 3); - ranges2.set_range(3, 6); - ranges2.set_range(4, 7); - SampleVector samples2(2, &ranges2); - - samples2.Accumulate(1, 100); - samples1.Add(samples2); - EXPECT_EQ(100, samples1.GetCountAtIndex(0)); - - // Extra bucket in the beginning. These should CHECK in GetBucketIndex. - samples2.Accumulate(0, 100); - EXPECT_DEATH_IF_SUPPORTED(samples1.Add(samples2), ""); - EXPECT_DEATH_IF_SUPPORTED(samples1.Subtract(samples2), ""); - - // Extra bucket in the end. These should cause AddSubtractImpl to fail, and - // Add to DCHECK as a result. - samples2.Accumulate(0, -100); - samples2.Accumulate(6, 100); - EXPECT_DCHECK_DEATH(samples1.Add(samples2)); - EXPECT_DCHECK_DEATH(samples1.Subtract(samples2)); - - // Bucket not match: [3, 5) VS [3, 6). These should cause AddSubtractImpl to - // DCHECK. - samples2.Accumulate(6, -100); - samples2.Accumulate(3, 100); - EXPECT_DCHECK_DEATH(samples1.Add(samples2)); - EXPECT_DCHECK_DEATH(samples1.Subtract(samples2)); -} - -TEST_F(SampleVectorTest, Iterate) { - BucketRanges ranges(5); - ranges.set_range(0, 0); - ranges.set_range(1, 1); - ranges.set_range(2, 2); - ranges.set_range(3, 3); - ranges.set_range(4, 4); - - std::vector counts(3); - counts[0] = 1; - counts[1] = 0; // Iterator will bypass this empty bucket. - counts[2] = 2; - - // BucketRanges can have larger size than counts. - SampleVectorIterator it(&counts, &ranges); - size_t index; - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - it.Get(&min, &max, &count); - EXPECT_EQ(0, min); - EXPECT_EQ(1, max); - EXPECT_EQ(1, count); - EXPECT_TRUE(it.GetBucketIndex(&index)); - EXPECT_EQ(0u, index); - - it.Next(); - it.Get(&min, &max, &count); - EXPECT_EQ(2, min); - EXPECT_EQ(3, max); - EXPECT_EQ(2, count); - EXPECT_TRUE(it.GetBucketIndex(&index)); - EXPECT_EQ(2u, index); - - it.Next(); - EXPECT_TRUE(it.Done()); - - // Create iterator from SampleVector. - SampleVector samples(1, &ranges); - samples.Accumulate(0, 0); - samples.Accumulate(1, 1); - samples.Accumulate(2, 2); - samples.Accumulate(3, 3); - std::unique_ptr it2 = samples.Iterator(); - - int i; - for (i = 1; !it2->Done(); i++, it2->Next()) { - it2->Get(&min, &max, &count); - EXPECT_EQ(i, min); - EXPECT_EQ(i + 1, max); - EXPECT_EQ(i, count); - - size_t index; - EXPECT_TRUE(it2->GetBucketIndex(&index)); - EXPECT_EQ(static_cast(i), index); - } - EXPECT_EQ(4, i); -} - -TEST_F(SampleVectorTest, IterateDoneDeath) { - BucketRanges ranges(5); - ranges.set_range(0, 0); - ranges.set_range(1, 1); - ranges.set_range(2, 2); - ranges.set_range(3, 3); - ranges.set_range(4, INT_MAX); - SampleVector samples(1, &ranges); - - std::unique_ptr it = samples.Iterator(); - - EXPECT_TRUE(it->Done()); - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - EXPECT_DCHECK_DEATH(it->Get(&min, &max, &count)); - - EXPECT_DCHECK_DEATH(it->Next()); - - samples.Accumulate(2, 100); - it = samples.Iterator(); - EXPECT_FALSE(it->Done()); -} - -TEST_F(SampleVectorTest, SingleSample) { - // Custom buckets: [1, 5) [5, 10) - BucketRanges ranges(3); - ranges.set_range(0, 1); - ranges.set_range(1, 5); - ranges.set_range(2, 10); - SampleVector samples(&ranges); - - // Ensure that a single value accumulates correctly. - EXPECT_FALSE(GetSamplesCounts(samples)); - samples.Accumulate(3, 200); - EXPECT_EQ(200, samples.GetCount(3)); - EXPECT_FALSE(GetSamplesCounts(samples)); - samples.Accumulate(3, 400); - EXPECT_EQ(600, samples.GetCount(3)); - EXPECT_FALSE(GetSamplesCounts(samples)); - EXPECT_EQ(3 * 600, samples.sum()); - EXPECT_EQ(600, samples.TotalCount()); - EXPECT_EQ(600, samples.redundant_count()); - - // Ensure that the iterator returns only one value. - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - std::unique_ptr it = samples.Iterator(); - ASSERT_FALSE(it->Done()); - it->Get(&min, &max, &count); - EXPECT_EQ(1, min); - EXPECT_EQ(5, max); - EXPECT_EQ(600, count); - it->Next(); - EXPECT_TRUE(it->Done()); - - // Ensure that it can be merged to another single-sample vector. - SampleVector samples_copy(&ranges); - samples_copy.Add(samples); - EXPECT_FALSE(GetSamplesCounts(samples_copy)); - EXPECT_EQ(3 * 600, samples_copy.sum()); - EXPECT_EQ(600, samples_copy.TotalCount()); - EXPECT_EQ(600, samples_copy.redundant_count()); - - // A different value should cause creation of the counts array. - samples.Accumulate(8, 100); - EXPECT_TRUE(GetSamplesCounts(samples)); - EXPECT_EQ(600, samples.GetCount(3)); - EXPECT_EQ(100, samples.GetCount(8)); - EXPECT_EQ(3 * 600 + 8 * 100, samples.sum()); - EXPECT_EQ(600 + 100, samples.TotalCount()); - EXPECT_EQ(600 + 100, samples.redundant_count()); - - // The iterator should now return both values. - it = samples.Iterator(); - ASSERT_FALSE(it->Done()); - it->Get(&min, &max, &count); - EXPECT_EQ(1, min); - EXPECT_EQ(5, max); - EXPECT_EQ(600, count); - it->Next(); - ASSERT_FALSE(it->Done()); - it->Get(&min, &max, &count); - EXPECT_EQ(5, min); - EXPECT_EQ(10, max); - EXPECT_EQ(100, count); - it->Next(); - EXPECT_TRUE(it->Done()); - - // Ensure that it can merged to a single-sample vector. - samples_copy.Add(samples); - EXPECT_TRUE(GetSamplesCounts(samples_copy)); - EXPECT_EQ(3 * 1200 + 8 * 100, samples_copy.sum()); - EXPECT_EQ(1200 + 100, samples_copy.TotalCount()); - EXPECT_EQ(1200 + 100, samples_copy.redundant_count()); -} - -TEST_F(SampleVectorTest, PersistentSampleVector) { - LocalPersistentMemoryAllocator allocator(64 << 10, 0, ""); - std::atomic samples_ref; - samples_ref.store(0, std::memory_order_relaxed); - HistogramSamples::Metadata samples_meta; - memset(&samples_meta, 0, sizeof(samples_meta)); - - // Custom buckets: [1, 5) [5, 10) - BucketRanges ranges(3); - ranges.set_range(0, 1); - ranges.set_range(1, 5); - ranges.set_range(2, 10); - - // Persistent allocation. - const size_t counts_bytes = - sizeof(HistogramBase::AtomicCount) * ranges.bucket_count(); - const DelayedPersistentAllocation allocation(&allocator, &samples_ref, 1, - counts_bytes, false); - - PersistentSampleVector samples1(0, &ranges, &samples_meta, allocation); - EXPECT_FALSE(GetSamplesCounts(samples1)); - samples1.Accumulate(3, 200); - EXPECT_EQ(200, samples1.GetCount(3)); - EXPECT_FALSE(GetSamplesCounts(samples1)); - EXPECT_EQ(0, samples1.GetCount(8)); - EXPECT_FALSE(GetSamplesCounts(samples1)); - - PersistentSampleVector samples2(0, &ranges, &samples_meta, allocation); - EXPECT_EQ(200, samples2.GetCount(3)); - EXPECT_FALSE(GetSamplesCounts(samples2)); - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - std::unique_ptr it = samples2.Iterator(); - ASSERT_FALSE(it->Done()); - it->Get(&min, &max, &count); - EXPECT_EQ(1, min); - EXPECT_EQ(5, max); - EXPECT_EQ(200, count); - it->Next(); - EXPECT_TRUE(it->Done()); - - samples1.Accumulate(8, 100); - EXPECT_TRUE(GetSamplesCounts(samples1)); - - EXPECT_FALSE(GetSamplesCounts(samples2)); - EXPECT_EQ(200, samples2.GetCount(3)); - EXPECT_EQ(100, samples2.GetCount(8)); - EXPECT_TRUE(GetSamplesCounts(samples2)); - EXPECT_EQ(3 * 200 + 8 * 100, samples2.sum()); - EXPECT_EQ(300, samples2.TotalCount()); - EXPECT_EQ(300, samples2.redundant_count()); - - it = samples2.Iterator(); - ASSERT_FALSE(it->Done()); - it->Get(&min, &max, &count); - EXPECT_EQ(1, min); - EXPECT_EQ(5, max); - EXPECT_EQ(200, count); - it->Next(); - ASSERT_FALSE(it->Done()); - it->Get(&min, &max, &count); - EXPECT_EQ(5, min); - EXPECT_EQ(10, max); - EXPECT_EQ(100, count); - it->Next(); - EXPECT_TRUE(it->Done()); - - PersistentSampleVector samples3(0, &ranges, &samples_meta, allocation); - EXPECT_TRUE(GetSamplesCounts(samples2)); - EXPECT_EQ(200, samples3.GetCount(3)); - EXPECT_EQ(100, samples3.GetCount(8)); - EXPECT_EQ(3 * 200 + 8 * 100, samples3.sum()); - EXPECT_EQ(300, samples3.TotalCount()); - EXPECT_EQ(300, samples3.redundant_count()); - - it = samples3.Iterator(); - ASSERT_FALSE(it->Done()); - it->Get(&min, &max, &count); - EXPECT_EQ(1, min); - EXPECT_EQ(5, max); - EXPECT_EQ(200, count); - it->Next(); - ASSERT_FALSE(it->Done()); - it->Get(&min, &max, &count); - EXPECT_EQ(5, min); - EXPECT_EQ(10, max); - EXPECT_EQ(100, count); - it->Next(); - EXPECT_TRUE(it->Done()); -} - -TEST_F(SampleVectorTest, PersistentSampleVectorTestWithOutsideAlloc) { - LocalPersistentMemoryAllocator allocator(64 << 10, 0, ""); - std::atomic samples_ref; - samples_ref.store(0, std::memory_order_relaxed); - HistogramSamples::Metadata samples_meta; - memset(&samples_meta, 0, sizeof(samples_meta)); - - // Custom buckets: [1, 5) [5, 10) - BucketRanges ranges(3); - ranges.set_range(0, 1); - ranges.set_range(1, 5); - ranges.set_range(2, 10); - - // Persistent allocation. - const size_t counts_bytes = - sizeof(HistogramBase::AtomicCount) * ranges.bucket_count(); - const DelayedPersistentAllocation allocation(&allocator, &samples_ref, 1, - counts_bytes, false); - - PersistentSampleVector samples1(0, &ranges, &samples_meta, allocation); - EXPECT_FALSE(GetSamplesCounts(samples1)); - samples1.Accumulate(3, 200); - EXPECT_EQ(200, samples1.GetCount(3)); - EXPECT_FALSE(GetSamplesCounts(samples1)); - - // Because the delayed allocation can be shared with other objects (the - // |offset| parameter allows concatinating multiple data blocks into the - // same allocation), it's possible that the allocation gets realized from - // the outside even though the data block being accessed is all zero. - allocation.Get(); - EXPECT_EQ(200, samples1.GetCount(3)); - EXPECT_FALSE(GetSamplesCounts(samples1)); - - HistogramBase::Sample min; - int64_t max; - HistogramBase::Count count; - std::unique_ptr it = samples1.Iterator(); - ASSERT_FALSE(it->Done()); - it->Get(&min, &max, &count); - EXPECT_EQ(1, min); - EXPECT_EQ(5, max); - EXPECT_EQ(200, count); - it->Next(); - EXPECT_TRUE(it->Done()); - - // A duplicate samples object should still see the single-sample entry even - // when storage is available. - PersistentSampleVector samples2(0, &ranges, &samples_meta, allocation); - EXPECT_EQ(200, samples2.GetCount(3)); - - // New accumulations, in both directions, of the existing value should work. - samples1.Accumulate(3, 50); - EXPECT_EQ(250, samples1.GetCount(3)); - EXPECT_EQ(250, samples2.GetCount(3)); - samples2.Accumulate(3, 50); - EXPECT_EQ(300, samples1.GetCount(3)); - EXPECT_EQ(300, samples2.GetCount(3)); - - it = samples1.Iterator(); - ASSERT_FALSE(it->Done()); - it->Get(&min, &max, &count); - EXPECT_EQ(1, min); - EXPECT_EQ(5, max); - EXPECT_EQ(300, count); - it->Next(); - EXPECT_TRUE(it->Done()); - - samples1.Accumulate(8, 100); - EXPECT_TRUE(GetSamplesCounts(samples1)); - EXPECT_EQ(300, samples1.GetCount(3)); - EXPECT_EQ(300, samples2.GetCount(3)); - EXPECT_EQ(100, samples1.GetCount(8)); - EXPECT_EQ(100, samples2.GetCount(8)); - samples2.Accumulate(8, 100); - EXPECT_EQ(300, samples1.GetCount(3)); - EXPECT_EQ(300, samples2.GetCount(3)); - EXPECT_EQ(200, samples1.GetCount(8)); - EXPECT_EQ(200, samples2.GetCount(8)); -} - -} // namespace base diff --git a/metrics/single_sample_metrics.cc b/metrics/single_sample_metrics.cc deleted file mode 100644 index 57c1c8f16..000000000 --- a/metrics/single_sample_metrics.cc +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2017 The Chromium 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/single_sample_metrics.h" - -#include "base/memory/ptr_util.h" -#include "base/metrics/histogram.h" - -namespace base { - -static SingleSampleMetricsFactory* g_factory = nullptr; - -// static -SingleSampleMetricsFactory* SingleSampleMetricsFactory::Get() { - if (!g_factory) - g_factory = new DefaultSingleSampleMetricsFactory(); - - return g_factory; -} - -// static -void SingleSampleMetricsFactory::SetFactory( - std::unique_ptr factory) { - DCHECK(!g_factory); - g_factory = factory.release(); -} - -// static -void SingleSampleMetricsFactory::DeleteFactoryForTesting() { - DCHECK(g_factory); - delete g_factory; - g_factory = nullptr; -} - -std::unique_ptr -DefaultSingleSampleMetricsFactory::CreateCustomCountsMetric( - const std::string& histogram_name, - HistogramBase::Sample min, - HistogramBase::Sample max, - uint32_t bucket_count) { - return std::make_unique( - histogram_name, min, max, bucket_count, - HistogramBase::kUmaTargetedHistogramFlag); -} - -DefaultSingleSampleMetric::DefaultSingleSampleMetric( - const std::string& histogram_name, - HistogramBase::Sample min, - HistogramBase::Sample max, - uint32_t bucket_count, - int32_t flags) - : histogram_(Histogram::FactoryGet(histogram_name, - min, - max, - bucket_count, - flags)) { - // Bad construction parameters may lead to |histogram_| being null; DCHECK to - // find accidental errors in production. We must still handle the nullptr in - // destruction though since this construction may come from another untrusted - // process. - DCHECK(histogram_); -} - -DefaultSingleSampleMetric::~DefaultSingleSampleMetric() { - // |histogram_| may be nullptr if bad construction parameters are given. - if (sample_ < 0 || !histogram_) - return; - histogram_->Add(sample_); -} - -void DefaultSingleSampleMetric::SetSample(HistogramBase::Sample sample) { - DCHECK_GE(sample, 0); - sample_ = sample; -} - -} // namespace base diff --git a/metrics/single_sample_metrics.h b/metrics/single_sample_metrics.h deleted file mode 100644 index b966cb1ac..000000000 --- a/metrics/single_sample_metrics.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2017 The Chromium 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_SINGLE_SAMPLE_METRICS_H_ -#define BASE_METRICS_SINGLE_SAMPLE_METRICS_H_ - -#include - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/metrics/histogram_base.h" - -namespace base { - -// See base/metrics/histograms.h for parameter definitions. Must only be used -// and destroyed from the same thread as construction. -class BASE_EXPORT SingleSampleMetric { - public: - virtual ~SingleSampleMetric() = default; - - virtual void SetSample(HistogramBase::Sample sample) = 0; -}; - -// Factory for creating single sample metrics. A single sample metric only -// reports its sample once at destruction time. The sample may be changed prior -// to destruction using the SetSample() method as many times as desired. -// -// The metric creation methods are safe to call from any thread, however the -// returned class must only be used and destroyed from the same thread as -// construction. -// -// See base/metrics/histogram_macros.h for usage recommendations and -// base/metrics/histogram.h for full parameter definitions. -class BASE_EXPORT SingleSampleMetricsFactory { - public: - virtual ~SingleSampleMetricsFactory() = default; - - // Returns the factory provided by SetFactory(), or if no factory has been set - // a default factory will be provided (future calls to SetFactory() will fail - // if the default factory is ever vended). - static SingleSampleMetricsFactory* Get(); - static void SetFactory(std::unique_ptr factory); - - // The factory normally persists until process shutdown, but in testing we - // should avoid leaking it since it sets a global. - static void DeleteFactoryForTesting(); - - // The methods below return a single sample metric for counts histograms; see - // method comments for the corresponding histogram macro. - - // UMA_HISTOGRAM_CUSTOM_COUNTS() - virtual std::unique_ptr CreateCustomCountsMetric( - const std::string& histogram_name, - HistogramBase::Sample min, - HistogramBase::Sample max, - uint32_t bucket_count) = 0; -}; - -// Default implementation for when no factory has been provided to the process. -// Samples are only recorded within the current process in this case, so samples -// will be lost in the event of sudden process termination. -class BASE_EXPORT DefaultSingleSampleMetricsFactory - : public SingleSampleMetricsFactory { - public: - DefaultSingleSampleMetricsFactory() = default; - ~DefaultSingleSampleMetricsFactory() override = default; - - // SingleSampleMetricsFactory: - std::unique_ptr CreateCustomCountsMetric( - const std::string& histogram_name, - HistogramBase::Sample min, - HistogramBase::Sample max, - uint32_t bucket_count) override; - - private: - DISALLOW_COPY_AND_ASSIGN(DefaultSingleSampleMetricsFactory); -}; - -class BASE_EXPORT DefaultSingleSampleMetric : public SingleSampleMetric { - public: - DefaultSingleSampleMetric(const std::string& histogram_name, - HistogramBase::Sample min, - HistogramBase::Sample max, - uint32_t bucket_count, - int32_t flags); - ~DefaultSingleSampleMetric() override; - - // SingleSampleMetric: - void SetSample(HistogramBase::Sample sample) override; - - private: - HistogramBase* const histogram_; - - // The last sample provided to SetSample(). We use -1 as a sentinel value to - // indicate no sample has been set. - HistogramBase::Sample sample_ = -1; - - DISALLOW_COPY_AND_ASSIGN(DefaultSingleSampleMetric); -}; - -} // namespace base - -#endif // BASE_METRICS_SINGLE_SAMPLE_METRICS_H_ diff --git a/metrics/single_sample_metrics_unittest.cc b/metrics/single_sample_metrics_unittest.cc deleted file mode 100644 index d4d5913d6..000000000 --- a/metrics/single_sample_metrics_unittest.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2017 The Chromium 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/single_sample_metrics.h" - -#include "base/memory/ptr_util.h" -#include "base/metrics/dummy_histogram.h" -#include "base/test/gtest_util.h" -#include "base/test/metrics/histogram_tester.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -const HistogramBase::Sample kMin = 1; -const HistogramBase::Sample kMax = 10; -const uint32_t kBucketCount = 10; -const char kMetricName[] = "Single.Sample.Metric"; - -class SingleSampleMetricsTest : public testing::Test { - public: - SingleSampleMetricsTest() = default; - - ~SingleSampleMetricsTest() override { - // Ensure we cleanup after ourselves. - SingleSampleMetricsFactory::DeleteFactoryForTesting(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(SingleSampleMetricsTest); -}; - -} // namespace - -TEST_F(SingleSampleMetricsTest, DefaultFactoryGetSet) { - SingleSampleMetricsFactory* factory = SingleSampleMetricsFactory::Get(); - ASSERT_TRUE(factory); - - // Same factory should be returned evermore. - EXPECT_EQ(factory, SingleSampleMetricsFactory::Get()); - - // Setting a factory after the default has been instantiated should fail. - EXPECT_DCHECK_DEATH(SingleSampleMetricsFactory::SetFactory( - WrapUnique(nullptr))); -} - -TEST_F(SingleSampleMetricsTest, CustomFactoryGetSet) { - SingleSampleMetricsFactory* factory = new DefaultSingleSampleMetricsFactory(); - SingleSampleMetricsFactory::SetFactory(WrapUnique(factory)); - EXPECT_EQ(factory, SingleSampleMetricsFactory::Get()); -} - -TEST_F(SingleSampleMetricsTest, DefaultSingleSampleMetricNoValue) { - SingleSampleMetricsFactory* factory = SingleSampleMetricsFactory::Get(); - - HistogramTester tester; - std::unique_ptr metric = - factory->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount); - metric.reset(); - - // Verify that no sample is recorded if SetSample() is never called. - tester.ExpectTotalCount(kMetricName, 0); -} - -TEST_F(SingleSampleMetricsTest, DefaultSingleSampleMetricWithValue) { - SingleSampleMetricsFactory* factory = SingleSampleMetricsFactory::Get(); - - HistogramTester tester; - std::unique_ptr metric = - factory->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount); - - const HistogramBase::Sample kLastSample = 9; - metric->SetSample(1); - metric->SetSample(3); - metric->SetSample(5); - metric->SetSample(kLastSample); - metric.reset(); - - // Verify only the last sample sent to SetSample() is recorded. - tester.ExpectUniqueSample(kMetricName, kLastSample, 1); - - // Verify construction implicitly by requesting a histogram with the same - // parameters; this test relies on the fact that histogram objects are unique - // per name. Different parameters will result in a Dummy histogram returned. - EXPECT_EQ( - DummyHistogram::GetInstance(), - Histogram::FactoryGet(kMetricName, 1, 3, 3, HistogramBase::kNoFlags)); - EXPECT_NE(DummyHistogram::GetInstance(), - Histogram::FactoryGet(kMetricName, kMin, kMax, kBucketCount, - HistogramBase::kUmaTargetedHistogramFlag)); -} - -TEST_F(SingleSampleMetricsTest, MultipleMetricsAreDistinct) { - SingleSampleMetricsFactory* factory = SingleSampleMetricsFactory::Get(); - - HistogramTester tester; - std::unique_ptr metric = - factory->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount); - std::unique_ptr metric2 = - factory->CreateCustomCountsMetric(kMetricName, kMin, kMax, kBucketCount); - const char kMetricName2[] = "Single.Sample.Metric.2"; - std::unique_ptr metric3 = - factory->CreateCustomCountsMetric(kMetricName2, kMin, kMax, kBucketCount); - - const HistogramBase::Sample kSample1 = 5; - metric->SetSample(kSample1); - metric2->SetSample(kSample1); - - const HistogramBase::Sample kSample2 = 7; - metric3->SetSample(kSample2); - - metric.reset(); - tester.ExpectUniqueSample(kMetricName, kSample1, 1); - - metric2.reset(); - tester.ExpectUniqueSample(kMetricName, kSample1, 2); - - metric3.reset(); - tester.ExpectUniqueSample(kMetricName2, kSample2, 1); -} - -} // namespace base diff --git a/metrics/sparse_histogram.cc b/metrics/sparse_histogram.cc deleted file mode 100644 index 30175a078..000000000 --- a/metrics/sparse_histogram.cc +++ /dev/null @@ -1,290 +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/metrics/sparse_histogram.h" - -#include - -#include "base/memory/ptr_util.h" -#include "base/metrics/dummy_histogram.h" -#include "base/metrics/metrics_hashes.h" -#include "base/metrics/persistent_histogram_allocator.h" -#include "base/metrics/persistent_sample_map.h" -#include "base/metrics/sample_map.h" -#include "base/metrics/statistics_recorder.h" -#include "base/pickle.h" -#include "base/strings/stringprintf.h" -#include "base/synchronization/lock.h" - -namespace base { - -typedef HistogramBase::Count Count; -typedef HistogramBase::Sample Sample; - -// static -HistogramBase* SparseHistogram::FactoryGet(const std::string& name, - int32_t flags) { - HistogramBase* histogram = StatisticsRecorder::FindHistogram(name); - if (!histogram) { - // TODO(gayane): |HashMetricName| is called again in Histogram constructor. - // Refactor code to avoid the additional call. - bool should_record = - StatisticsRecorder::ShouldRecordHistogram(HashMetricName(name)); - if (!should_record) - return DummyHistogram::GetInstance(); - // Try to create the histogram using a "persistent" allocator. As of - // 2016-02-25, the availability of such is controlled by a base::Feature - // that is off by default. If the allocator doesn't exist or if - // allocating from it fails, code below will allocate the histogram from - // the process heap. - PersistentMemoryAllocator::Reference histogram_ref = 0; - std::unique_ptr tentative_histogram; - PersistentHistogramAllocator* allocator = GlobalHistogramAllocator::Get(); - if (allocator) { - tentative_histogram = allocator->AllocateHistogram( - SPARSE_HISTOGRAM, name, 0, 0, nullptr, flags, &histogram_ref); - } - - // Handle the case where no persistent allocator is present or the - // persistent allocation fails (perhaps because it is full). - if (!tentative_histogram) { - DCHECK(!histogram_ref); // Should never have been set. - DCHECK(!allocator); // Shouldn't have failed. - flags &= ~HistogramBase::kIsPersistent; - tentative_histogram.reset(new SparseHistogram(GetPermanentName(name))); - tentative_histogram->SetFlags(flags); - } - - // Register this histogram with the StatisticsRecorder. Keep a copy of - // the pointer value to tell later whether the locally created histogram - // was registered or deleted. The type is "void" because it could point - // to released memory after the following line. - const void* tentative_histogram_ptr = tentative_histogram.get(); - histogram = StatisticsRecorder::RegisterOrDeleteDuplicate( - tentative_histogram.release()); - - // Persistent histograms need some follow-up processing. - if (histogram_ref) { - allocator->FinalizeHistogram(histogram_ref, - histogram == tentative_histogram_ptr); - } - } - - CHECK_EQ(SPARSE_HISTOGRAM, histogram->GetHistogramType()); - return histogram; -} - -// static -std::unique_ptr SparseHistogram::PersistentCreate( - PersistentHistogramAllocator* allocator, - const char* name, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta) { - return WrapUnique( - new SparseHistogram(allocator, name, meta, logged_meta)); -} - -SparseHistogram::~SparseHistogram() = default; - -uint64_t SparseHistogram::name_hash() const { - return unlogged_samples_->id(); -} - -HistogramType SparseHistogram::GetHistogramType() const { - return SPARSE_HISTOGRAM; -} - -bool SparseHistogram::HasConstructionArguments( - Sample expected_minimum, - Sample expected_maximum, - uint32_t expected_bucket_count) const { - // SparseHistogram never has min/max/bucket_count limit. - return false; -} - -void SparseHistogram::Add(Sample value) { - AddCount(value, 1); -} - -void SparseHistogram::AddCount(Sample value, int count) { - if (count <= 0) { - NOTREACHED(); - return; - } - { - base::AutoLock auto_lock(lock_); - unlogged_samples_->Accumulate(value, count); - } - - FindAndRunCallback(value); -} - -std::unique_ptr SparseHistogram::SnapshotSamples() const { - std::unique_ptr snapshot(new SampleMap(name_hash())); - - base::AutoLock auto_lock(lock_); - snapshot->Add(*unlogged_samples_); - snapshot->Add(*logged_samples_); - return std::move(snapshot); -} - -std::unique_ptr SparseHistogram::SnapshotDelta() { - DCHECK(!final_delta_created_); - - std::unique_ptr snapshot(new SampleMap(name_hash())); - base::AutoLock auto_lock(lock_); - snapshot->Add(*unlogged_samples_); - - unlogged_samples_->Subtract(*snapshot); - logged_samples_->Add(*snapshot); - return std::move(snapshot); -} - -std::unique_ptr SparseHistogram::SnapshotFinalDelta() const { - DCHECK(!final_delta_created_); - final_delta_created_ = true; - - std::unique_ptr snapshot(new SampleMap(name_hash())); - base::AutoLock auto_lock(lock_); - snapshot->Add(*unlogged_samples_); - - return std::move(snapshot); -} - -void SparseHistogram::AddSamples(const HistogramSamples& samples) { - base::AutoLock auto_lock(lock_); - unlogged_samples_->Add(samples); -} - -bool SparseHistogram::AddSamplesFromPickle(PickleIterator* iter) { - base::AutoLock auto_lock(lock_); - return unlogged_samples_->AddFromPickle(iter); -} - -void SparseHistogram::WriteHTMLGraph(std::string* output) const { - output->append("
");
-  WriteAsciiImpl(true, "
", output); - output->append("
"); -} - -void SparseHistogram::WriteAscii(std::string* output) const { - WriteAsciiImpl(true, "\n", output); -} - -void SparseHistogram::SerializeInfoImpl(Pickle* pickle) const { - pickle->WriteString(histogram_name()); - pickle->WriteInt(flags()); -} - -SparseHistogram::SparseHistogram(const char* name) - : HistogramBase(name), - unlogged_samples_(new SampleMap(HashMetricName(name))), - logged_samples_(new SampleMap(unlogged_samples_->id())) {} - -SparseHistogram::SparseHistogram(PersistentHistogramAllocator* allocator, - const char* name, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta) - : HistogramBase(name), - // While other histogram types maintain a static vector of values with - // sufficient space for both "active" and "logged" samples, with each - // SampleVector being given the appropriate half, sparse histograms - // have no such initial allocation. Each sample has its own record - // attached to a single PersistentSampleMap by a common 64-bit identifier. - // Since a sparse histogram has two sample maps (active and logged), - // there must be two sets of sample records with diffent IDs. The - // "active" samples use, for convenience purposes, an ID matching - // that of the histogram while the "logged" samples use that number - // plus 1. - unlogged_samples_( - new PersistentSampleMap(HashMetricName(name), allocator, meta)), - logged_samples_(new PersistentSampleMap(unlogged_samples_->id() + 1, - allocator, - logged_meta)) {} - -HistogramBase* SparseHistogram::DeserializeInfoImpl(PickleIterator* iter) { - std::string histogram_name; - int flags; - if (!iter->ReadString(&histogram_name) || !iter->ReadInt(&flags)) { - DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; - return nullptr; - } - - flags &= ~HistogramBase::kIPCSerializationSourceFlag; - - return SparseHistogram::FactoryGet(histogram_name, flags); -} - -void SparseHistogram::GetParameters(DictionaryValue* params) const { - // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.) -} - -void SparseHistogram::GetCountAndBucketData(Count* count, - int64_t* sum, - ListValue* buckets) const { - // TODO(kaiwang): Implement. (See HistogramBase::WriteJSON.) -} - -void SparseHistogram::WriteAsciiImpl(bool graph_it, - const std::string& newline, - std::string* output) const { - // Get a local copy of the data so we are consistent. - std::unique_ptr snapshot = SnapshotSamples(); - Count total_count = snapshot->TotalCount(); - double scaled_total_count = total_count / 100.0; - - WriteAsciiHeader(total_count, output); - output->append(newline); - - // Determine how wide the largest bucket range is (how many digits to print), - // so that we'll be able to right-align starts for the graphical bars. - // Determine which bucket has the largest sample count so that we can - // normalize the graphical bar-width relative to that sample count. - Count largest_count = 0; - Sample largest_sample = 0; - std::unique_ptr it = snapshot->Iterator(); - while (!it->Done()) { - Sample min; - int64_t max; - Count count; - it->Get(&min, &max, &count); - if (min > largest_sample) - largest_sample = min; - if (count > largest_count) - largest_count = count; - it->Next(); - } - size_t print_width = GetSimpleAsciiBucketRange(largest_sample).size() + 1; - - // iterate over each item and display them - it = snapshot->Iterator(); - while (!it->Done()) { - Sample min; - int64_t max; - Count count; - it->Get(&min, &max, &count); - - // value is min, so display it - std::string range = GetSimpleAsciiBucketRange(min); - output->append(range); - for (size_t j = 0; range.size() + j < print_width + 1; ++j) - output->push_back(' '); - - if (graph_it) - WriteAsciiBucketGraph(count, largest_count, output); - WriteAsciiBucketValue(count, scaled_total_count, output); - output->append(newline); - it->Next(); - } -} - -void SparseHistogram::WriteAsciiHeader(const Count total_count, - std::string* output) const { - StringAppendF(output, "Histogram: %s recorded %d samples", histogram_name(), - total_count); - if (flags()) - StringAppendF(output, " (flags = 0x%x)", flags()); -} - -} // namespace base diff --git a/metrics/sparse_histogram.h b/metrics/sparse_histogram.h deleted file mode 100644 index 913762c95..000000000 --- a/metrics/sparse_histogram.h +++ /dev/null @@ -1,108 +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_METRICS_SPARSE_HISTOGRAM_H_ -#define BASE_METRICS_SPARSE_HISTOGRAM_H_ - -#include -#include - -#include -#include -#include - -#include "base/base_export.h" -#include "base/macros.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_samples.h" -#include "base/synchronization/lock.h" - -namespace base { - -class HistogramSamples; -class PersistentHistogramAllocator; -class Pickle; -class PickleIterator; - -class BASE_EXPORT SparseHistogram : public HistogramBase { - public: - // If there's one with same name, return the existing one. If not, create a - // new one. - static HistogramBase* FactoryGet(const std::string& name, int32_t flags); - - // Create a histogram using data in persistent storage. The allocator must - // live longer than the created sparse histogram. - static std::unique_ptr PersistentCreate( - PersistentHistogramAllocator* allocator, - const char* name, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta); - - ~SparseHistogram() override; - - // HistogramBase implementation: - uint64_t name_hash() const override; - HistogramType GetHistogramType() const override; - bool HasConstructionArguments(Sample expected_minimum, - Sample expected_maximum, - uint32_t expected_bucket_count) const override; - void Add(Sample value) override; - void AddCount(Sample value, int count) override; - void AddSamples(const HistogramSamples& samples) override; - bool AddSamplesFromPickle(base::PickleIterator* iter) override; - std::unique_ptr SnapshotSamples() const override; - std::unique_ptr SnapshotDelta() override; - std::unique_ptr SnapshotFinalDelta() const override; - void WriteHTMLGraph(std::string* output) const override; - void WriteAscii(std::string* output) const override; - - protected: - // HistogramBase implementation: - void SerializeInfoImpl(base::Pickle* pickle) const override; - - private: - // Clients should always use FactoryGet to create SparseHistogram. - explicit SparseHistogram(const char* name); - - SparseHistogram(PersistentHistogramAllocator* allocator, - const char* name, - HistogramSamples::Metadata* meta, - HistogramSamples::Metadata* logged_meta); - - friend BASE_EXPORT HistogramBase* DeserializeHistogramInfo( - base::PickleIterator* iter); - static HistogramBase* DeserializeInfoImpl(base::PickleIterator* iter); - - void GetParameters(DictionaryValue* params) const override; - void GetCountAndBucketData(Count* count, - int64_t* sum, - ListValue* buckets) const override; - - // Helpers for emitting Ascii graphic. Each method appends data to output. - void WriteAsciiImpl(bool graph_it, - const std::string& newline, - std::string* output) const; - - // Write a common header message describing this histogram. - void WriteAsciiHeader(const Count total_count, - std::string* output) const; - - // For constuctor calling. - friend class SparseHistogramTest; - - // Protects access to |samples_|. - mutable base::Lock lock_; - - // Flag to indicate if PrepareFinalDelta has been previously called. - mutable bool final_delta_created_ = false; - - std::unique_ptr unlogged_samples_; - std::unique_ptr logged_samples_; - - DISALLOW_COPY_AND_ASSIGN(SparseHistogram); -}; - -} // namespace base - -#endif // BASE_METRICS_SPARSE_HISTOGRAM_H_ diff --git a/metrics/sparse_histogram_unittest.cc b/metrics/sparse_histogram_unittest.cc deleted file mode 100644 index 72dd90544..000000000 --- a/metrics/sparse_histogram_unittest.cc +++ /dev/null @@ -1,388 +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/metrics/sparse_histogram.h" - -#include -#include - -#include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_samples.h" -#include "base/metrics/metrics_hashes.h" -#include "base/metrics/persistent_histogram_allocator.h" -#include "base/metrics/persistent_memory_allocator.h" -#include "base/metrics/sample_map.h" -#include "base/metrics/statistics_recorder.h" -#include "base/pickle.h" -#include "base/strings/stringprintf.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -// Test parameter indicates if a persistent memory allocator should be used -// for histogram allocation. False will allocate histograms from the process -// heap. -class SparseHistogramTest : public testing::TestWithParam { - protected: - const int32_t kAllocatorMemorySize = 8 << 20; // 8 MiB - - SparseHistogramTest() : use_persistent_histogram_allocator_(GetParam()) {} - - void SetUp() override { - if (use_persistent_histogram_allocator_) - CreatePersistentMemoryAllocator(); - - // Each test will have a clean state (no Histogram / BucketRanges - // registered). - InitializeStatisticsRecorder(); - } - - void TearDown() override { - if (allocator_) { - ASSERT_FALSE(allocator_->IsFull()); - ASSERT_FALSE(allocator_->IsCorrupt()); - } - UninitializeStatisticsRecorder(); - DestroyPersistentMemoryAllocator(); - } - - void InitializeStatisticsRecorder() { - DCHECK(!statistics_recorder_); - statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting(); - } - - void UninitializeStatisticsRecorder() { - statistics_recorder_.reset(); - } - - void CreatePersistentMemoryAllocator() { - GlobalHistogramAllocator::CreateWithLocalMemory( - kAllocatorMemorySize, 0, "SparseHistogramAllocatorTest"); - allocator_ = GlobalHistogramAllocator::Get()->memory_allocator(); - } - - void DestroyPersistentMemoryAllocator() { - allocator_ = nullptr; - GlobalHistogramAllocator::ReleaseForTesting(); - } - - std::unique_ptr NewSparseHistogram(const char* name) { - // std::make_unique can't access protected ctor so do it manually. This - // test class is a friend so can access it. - return std::unique_ptr(new SparseHistogram(name)); - } - - const bool use_persistent_histogram_allocator_; - - std::unique_ptr statistics_recorder_; - PersistentMemoryAllocator* allocator_ = nullptr; - - private: - DISALLOW_COPY_AND_ASSIGN(SparseHistogramTest); -}; - -// Run all HistogramTest cases with both heap and persistent memory. -INSTANTIATE_TEST_CASE_P(HeapAndPersistent, - SparseHistogramTest, - testing::Bool()); - - -TEST_P(SparseHistogramTest, BasicTest) { - std::unique_ptr histogram(NewSparseHistogram("Sparse")); - std::unique_ptr snapshot(histogram->SnapshotSamples()); - EXPECT_EQ(0, snapshot->TotalCount()); - EXPECT_EQ(0, snapshot->sum()); - - histogram->Add(100); - std::unique_ptr snapshot1(histogram->SnapshotSamples()); - EXPECT_EQ(1, snapshot1->TotalCount()); - EXPECT_EQ(1, snapshot1->GetCount(100)); - - histogram->Add(100); - histogram->Add(101); - std::unique_ptr snapshot2(histogram->SnapshotSamples()); - EXPECT_EQ(3, snapshot2->TotalCount()); - EXPECT_EQ(2, snapshot2->GetCount(100)); - EXPECT_EQ(1, snapshot2->GetCount(101)); -} - -TEST_P(SparseHistogramTest, BasicTestAddCount) { - std::unique_ptr histogram(NewSparseHistogram("Sparse")); - std::unique_ptr snapshot(histogram->SnapshotSamples()); - EXPECT_EQ(0, snapshot->TotalCount()); - EXPECT_EQ(0, snapshot->sum()); - - histogram->AddCount(100, 15); - std::unique_ptr snapshot1(histogram->SnapshotSamples()); - EXPECT_EQ(15, snapshot1->TotalCount()); - EXPECT_EQ(15, snapshot1->GetCount(100)); - - histogram->AddCount(100, 15); - histogram->AddCount(101, 25); - std::unique_ptr snapshot2(histogram->SnapshotSamples()); - EXPECT_EQ(55, snapshot2->TotalCount()); - EXPECT_EQ(30, snapshot2->GetCount(100)); - EXPECT_EQ(25, snapshot2->GetCount(101)); -} - -TEST_P(SparseHistogramTest, AddCount_LargeValuesDontOverflow) { - std::unique_ptr histogram(NewSparseHistogram("Sparse")); - std::unique_ptr snapshot(histogram->SnapshotSamples()); - EXPECT_EQ(0, snapshot->TotalCount()); - EXPECT_EQ(0, snapshot->sum()); - - histogram->AddCount(1000000000, 15); - std::unique_ptr snapshot1(histogram->SnapshotSamples()); - EXPECT_EQ(15, snapshot1->TotalCount()); - EXPECT_EQ(15, snapshot1->GetCount(1000000000)); - - histogram->AddCount(1000000000, 15); - histogram->AddCount(1010000000, 25); - std::unique_ptr snapshot2(histogram->SnapshotSamples()); - EXPECT_EQ(55, snapshot2->TotalCount()); - EXPECT_EQ(30, snapshot2->GetCount(1000000000)); - EXPECT_EQ(25, snapshot2->GetCount(1010000000)); - EXPECT_EQ(55250000000LL, snapshot2->sum()); -} - -// Make sure that counts returned by Histogram::SnapshotDelta do not overflow -// even when a total count (returned by Histogram::SnapshotSample) does. -TEST_P(SparseHistogramTest, AddCount_LargeCountsDontOverflow) { - std::unique_ptr histogram(NewSparseHistogram("Sparse")); - std::unique_ptr snapshot(histogram->SnapshotSamples()); - EXPECT_EQ(0, snapshot->TotalCount()); - EXPECT_EQ(0, snapshot->sum()); - - const int count = (1 << 30) - 1; - - // Repeat N times to make sure that there is no internal value overflow. - for (int i = 0; i < 10; ++i) { - histogram->AddCount(42, count); - std::unique_ptr samples = histogram->SnapshotDelta(); - EXPECT_EQ(count, samples->TotalCount()); - EXPECT_EQ(count, samples->GetCount(42)); - } -} - -TEST_P(SparseHistogramTest, MacroBasicTest) { - UmaHistogramSparse("Sparse", 100); - UmaHistogramSparse("Sparse", 200); - UmaHistogramSparse("Sparse", 100); - - const StatisticsRecorder::Histograms histograms = - StatisticsRecorder::GetHistograms(); - - ASSERT_THAT(histograms, testing::SizeIs(1)); - const HistogramBase* const sparse_histogram = histograms[0]; - - EXPECT_EQ(SPARSE_HISTOGRAM, sparse_histogram->GetHistogramType()); - EXPECT_EQ("Sparse", StringPiece(sparse_histogram->histogram_name())); - EXPECT_EQ( - HistogramBase::kUmaTargetedHistogramFlag | - (use_persistent_histogram_allocator_ ? HistogramBase::kIsPersistent - : 0), - sparse_histogram->flags()); - - std::unique_ptr samples = - sparse_histogram->SnapshotSamples(); - EXPECT_EQ(3, samples->TotalCount()); - EXPECT_EQ(2, samples->GetCount(100)); - EXPECT_EQ(1, samples->GetCount(200)); -} - -TEST_P(SparseHistogramTest, MacroInLoopTest) { - // Unlike the macros in histogram.h, SparseHistogram macros can have a - // variable as histogram name. - for (int i = 0; i < 2; i++) { - UmaHistogramSparse(StringPrintf("Sparse%d", i), 100); - } - - const StatisticsRecorder::Histograms histograms = - StatisticsRecorder::Sort(StatisticsRecorder::GetHistograms()); - ASSERT_THAT(histograms, testing::SizeIs(2)); - EXPECT_STREQ(histograms[0]->histogram_name(), "Sparse0"); - EXPECT_STREQ(histograms[1]->histogram_name(), "Sparse1"); -} - -TEST_P(SparseHistogramTest, Serialize) { - std::unique_ptr histogram(NewSparseHistogram("Sparse")); - histogram->SetFlags(HistogramBase::kIPCSerializationSourceFlag); - - Pickle pickle; - histogram->SerializeInfo(&pickle); - - PickleIterator iter(pickle); - - int type; - EXPECT_TRUE(iter.ReadInt(&type)); - EXPECT_EQ(SPARSE_HISTOGRAM, type); - - std::string name; - EXPECT_TRUE(iter.ReadString(&name)); - EXPECT_EQ("Sparse", name); - - int flag; - EXPECT_TRUE(iter.ReadInt(&flag)); - EXPECT_EQ(HistogramBase::kIPCSerializationSourceFlag, flag); - - // No more data in the pickle. - EXPECT_FALSE(iter.SkipBytes(1)); -} - -// Ensure that race conditions that cause multiple, identical sparse histograms -// to be created will safely resolve to a single one. -TEST_P(SparseHistogramTest, DuplicationSafety) { - const char histogram_name[] = "Duplicated"; - size_t histogram_count = StatisticsRecorder::GetHistogramCount(); - - // Create a histogram that we will later duplicate. - HistogramBase* original = - SparseHistogram::FactoryGet(histogram_name, HistogramBase::kNoFlags); - ++histogram_count; - DCHECK_EQ(histogram_count, StatisticsRecorder::GetHistogramCount()); - original->Add(1); - - // Create a duplicate. This has to happen differently depending on where the - // memory is taken from. - if (use_persistent_histogram_allocator_) { - // To allocate from persistent memory, clear the last_created reference in - // the GlobalHistogramAllocator. This will cause an Import to recreate - // the just-created histogram which will then be released as a duplicate. - GlobalHistogramAllocator::Get()->ClearLastCreatedReferenceForTesting(); - // Creating a different histogram will first do an Import to ensure it - // hasn't been created elsewhere, triggering the duplication and release. - SparseHistogram::FactoryGet("something.new", HistogramBase::kNoFlags); - ++histogram_count; - } else { - // To allocate from the heap, just call the (private) constructor directly. - // Delete it immediately like would have happened within FactoryGet(); - std::unique_ptr something = - NewSparseHistogram(histogram_name); - DCHECK_NE(original, something.get()); - } - DCHECK_EQ(histogram_count, StatisticsRecorder::GetHistogramCount()); - - // Re-creating the histogram via FactoryGet() will return the same one. - HistogramBase* duplicate = - SparseHistogram::FactoryGet(histogram_name, HistogramBase::kNoFlags); - DCHECK_EQ(original, duplicate); - DCHECK_EQ(histogram_count, StatisticsRecorder::GetHistogramCount()); - duplicate->Add(2); - - // Ensure that original histograms are still cross-functional. - original->Add(2); - duplicate->Add(1); - std::unique_ptr snapshot_orig = original->SnapshotSamples(); - std::unique_ptr snapshot_dup = duplicate->SnapshotSamples(); - DCHECK_EQ(2, snapshot_orig->GetCount(2)); - DCHECK_EQ(2, snapshot_dup->GetCount(1)); -} - -TEST_P(SparseHistogramTest, FactoryTime) { - const int kTestCreateCount = 1 << 10; // Must be power-of-2. - const int kTestLookupCount = 100000; - const int kTestAddCount = 100000; - - // Create all histogram names in advance for accurate timing below. - std::vector histogram_names; - for (int i = 0; i < kTestCreateCount; ++i) { - histogram_names.push_back( - StringPrintf("TestHistogram.%d", i % kTestCreateCount)); - } - - // Calculate cost of creating histograms. - TimeTicks create_start = TimeTicks::Now(); - for (int i = 0; i < kTestCreateCount; ++i) - SparseHistogram::FactoryGet(histogram_names[i], HistogramBase::kNoFlags); - TimeDelta create_ticks = TimeTicks::Now() - create_start; - int64_t create_ms = create_ticks.InMilliseconds(); - - VLOG(1) << kTestCreateCount << " histogram creations took " << create_ms - << "ms or about " - << (create_ms * 1000000) / kTestCreateCount - << "ns each."; - - // Calculate cost of looking up existing histograms. - TimeTicks lookup_start = TimeTicks::Now(); - for (int i = 0; i < kTestLookupCount; ++i) { - // 6007 is co-prime with kTestCreateCount and so will do lookups in an - // order less likely to be cacheable (but still hit them all) should the - // underlying storage use the exact histogram name as the key. - const int i_mult = 6007; - static_assert(i_mult < INT_MAX / kTestCreateCount, "Multiplier too big"); - int index = (i * i_mult) & (kTestCreateCount - 1); - SparseHistogram::FactoryGet(histogram_names[index], - HistogramBase::kNoFlags); - } - TimeDelta lookup_ticks = TimeTicks::Now() - lookup_start; - int64_t lookup_ms = lookup_ticks.InMilliseconds(); - - VLOG(1) << kTestLookupCount << " histogram lookups took " << lookup_ms - << "ms or about " - << (lookup_ms * 1000000) / kTestLookupCount - << "ns each."; - - // Calculate cost of accessing histograms. - HistogramBase* histogram = - SparseHistogram::FactoryGet(histogram_names[0], HistogramBase::kNoFlags); - ASSERT_TRUE(histogram); - TimeTicks add_start = TimeTicks::Now(); - for (int i = 0; i < kTestAddCount; ++i) - histogram->Add(i & 127); - TimeDelta add_ticks = TimeTicks::Now() - add_start; - int64_t add_ms = add_ticks.InMilliseconds(); - - VLOG(1) << kTestAddCount << " histogram adds took " << add_ms - << "ms or about " - << (add_ms * 1000000) / kTestAddCount - << "ns each."; -} - -TEST_P(SparseHistogramTest, ExtremeValues) { - static const struct { - Histogram::Sample sample; - int64_t expected_max; - } cases[] = { - // Note: We use -2147483647 - 1 rather than -2147483648 because the later - // is interpreted as - operator applied to 2147483648 and the latter can't - // be represented as an int32 and causes a warning. - {-2147483647 - 1, -2147483647LL}, - {0, 1}, - {2147483647, 2147483648LL}, - }; - - for (size_t i = 0; i < arraysize(cases); ++i) { - HistogramBase* histogram = - SparseHistogram::FactoryGet(StringPrintf("ExtremeValues_%zu", i), - HistogramBase::kUmaTargetedHistogramFlag); - histogram->Add(cases[i].sample); - - std::unique_ptr snapshot = histogram->SnapshotSamples(); - std::unique_ptr it = snapshot->Iterator(); - ASSERT_FALSE(it->Done()); - - base::Histogram::Sample min; - int64_t max; - base::Histogram::Count count; - it->Get(&min, &max, &count); - - EXPECT_EQ(1, count); - EXPECT_EQ(cases[i].sample, min); - EXPECT_EQ(cases[i].expected_max, max); - - it->Next(); - EXPECT_TRUE(it->Done()); - } -} - -TEST_P(SparseHistogramTest, HistogramNameHash) { - const char kName[] = "TestName"; - HistogramBase* histogram = SparseHistogram::FactoryGet( - kName, HistogramBase::kUmaTargetedHistogramFlag); - EXPECT_EQ(histogram->name_hash(), HashMetricName(kName)); -} - -} // namespace base diff --git a/metrics/statistics_recorder.cc b/metrics/statistics_recorder.cc deleted file mode 100644 index 28773a13d..000000000 --- a/metrics/statistics_recorder.cc +++ /dev/null @@ -1,416 +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/metrics/statistics_recorder.h" - -#include - -#include "base/at_exit.h" -#include "base/debug/leak_annotations.h" -#include "base/json/string_escape.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_snapshot_manager.h" -#include "base/metrics/metrics_hashes.h" -#include "base/metrics/persistent_histogram_allocator.h" -#include "base/metrics/record_histogram_checker.h" -#include "base/stl_util.h" -#include "base/strings/stringprintf.h" -#include "base/values.h" - -namespace base { -namespace { - -bool HistogramNameLesser(const base::HistogramBase* a, - const base::HistogramBase* b) { - return strcmp(a->histogram_name(), b->histogram_name()) < 0; -} - -} // namespace - -// static -LazyInstance::Leaky StatisticsRecorder::lock_; - -// static -StatisticsRecorder* StatisticsRecorder::top_ = nullptr; - -// static -bool StatisticsRecorder::is_vlog_initialized_ = false; - -size_t StatisticsRecorder::BucketRangesHash::operator()( - const BucketRanges* const a) const { - return a->checksum(); -} - -bool StatisticsRecorder::BucketRangesEqual::operator()( - const BucketRanges* const a, - const BucketRanges* const b) const { - return a->Equals(b); -} - -StatisticsRecorder::~StatisticsRecorder() { - const AutoLock auto_lock(lock_.Get()); - DCHECK_EQ(this, top_); - top_ = previous_; -} - -// static -void StatisticsRecorder::EnsureGlobalRecorderWhileLocked() { - lock_.Get().AssertAcquired(); - if (top_) - return; - - const StatisticsRecorder* const p = new StatisticsRecorder; - // The global recorder is never deleted. - ANNOTATE_LEAKING_OBJECT_PTR(p); - DCHECK_EQ(p, top_); -} - -// static -void StatisticsRecorder::RegisterHistogramProvider( - const WeakPtr& provider) { - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - top_->providers_.push_back(provider); -} - -// static -HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate( - HistogramBase* histogram) { - // Declared before |auto_lock| to ensure correct destruction order. - std::unique_ptr histogram_deleter; - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - - const char* const name = histogram->histogram_name(); - HistogramBase*& registered = top_->histograms_[name]; - - if (!registered) { - // |name| is guaranteed to never change or be deallocated so long - // as the histogram is alive (which is forever). - registered = histogram; - ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 - // If there are callbacks for this histogram, we set the kCallbackExists - // flag. - const auto callback_iterator = top_->callbacks_.find(name); - if (callback_iterator != top_->callbacks_.end()) { - if (!callback_iterator->second.is_null()) - histogram->SetFlags(HistogramBase::kCallbackExists); - else - histogram->ClearFlags(HistogramBase::kCallbackExists); - } - return histogram; - } - - if (histogram == registered) { - // The histogram was registered before. - return histogram; - } - - // We already have one histogram with this name. - histogram_deleter.reset(histogram); - return registered; -} - -// static -const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges( - const BucketRanges* ranges) { - DCHECK(ranges->HasValidChecksum()); - - // Declared before |auto_lock| to ensure correct destruction order. - std::unique_ptr ranges_deleter; - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - - const BucketRanges* const registered = *top_->ranges_.insert(ranges).first; - if (registered == ranges) { - ANNOTATE_LEAKING_OBJECT_PTR(ranges); - } else { - ranges_deleter.reset(ranges); - } - - return registered; -} - -// static -void StatisticsRecorder::WriteHTMLGraph(const std::string& query, - std::string* output) { - for (const HistogramBase* const histogram : - Sort(WithName(GetHistograms(), query))) { - histogram->WriteHTMLGraph(output); - *output += "


"; - } -} - -// static -void StatisticsRecorder::WriteGraph(const std::string& query, - std::string* output) { - if (query.length()) - StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); - else - output->append("Collections of all histograms\n"); - - for (const HistogramBase* const histogram : - Sort(WithName(GetHistograms(), query))) { - histogram->WriteAscii(output); - output->append("\n"); - } -} - -// static -std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) { - std::string output = "{\"histograms\":["; - const char* sep = ""; - for (const HistogramBase* const histogram : Sort(GetHistograms())) { - output += sep; - sep = ","; - std::string json; - histogram->WriteJSON(&json, verbosity_level); - output += json; - } - output += "]}"; - return output; -} - -// static -std::vector StatisticsRecorder::GetBucketRanges() { - std::vector out; - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - out.reserve(top_->ranges_.size()); - out.assign(top_->ranges_.begin(), top_->ranges_.end()); - return out; -} - -// static -HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) { - // This must be called *before* the lock is acquired below because it will - // call back into this object to register histograms. Those called methods - // will acquire the lock at that time. - ImportGlobalPersistentHistograms(); - - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - - const HistogramMap::const_iterator it = top_->histograms_.find(name); - return it != top_->histograms_.end() ? it->second : nullptr; -} - -// static -StatisticsRecorder::HistogramProviders -StatisticsRecorder::GetHistogramProviders() { - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - return top_->providers_; -} - -// static -void StatisticsRecorder::ImportProvidedHistograms() { - // Merge histogram data from each provider in turn. - for (const WeakPtr& provider : GetHistogramProviders()) { - // Weak-pointer may be invalid if the provider was destructed, though they - // generally never are. - if (provider) - provider->MergeHistogramDeltas(); - } -} - -// static -void StatisticsRecorder::PrepareDeltas( - bool include_persistent, - HistogramBase::Flags flags_to_set, - HistogramBase::Flags required_flags, - HistogramSnapshotManager* snapshot_manager) { - Histograms histograms = GetHistograms(); - if (!include_persistent) - histograms = NonPersistent(std::move(histograms)); - snapshot_manager->PrepareDeltas(Sort(std::move(histograms)), flags_to_set, - required_flags); -} - -// static -void StatisticsRecorder::InitLogOnShutdown() { - const AutoLock auto_lock(lock_.Get()); - InitLogOnShutdownWhileLocked(); -} - -// static -bool StatisticsRecorder::SetCallback( - const std::string& name, - const StatisticsRecorder::OnSampleCallback& cb) { - DCHECK(!cb.is_null()); - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - - if (!top_->callbacks_.insert({name, cb}).second) - return false; - - const HistogramMap::const_iterator it = top_->histograms_.find(name); - if (it != top_->histograms_.end()) - it->second->SetFlags(HistogramBase::kCallbackExists); - - return true; -} - -// static -void StatisticsRecorder::ClearCallback(const std::string& name) { - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - - top_->callbacks_.erase(name); - - // We also clear the flag from the histogram (if it exists). - const HistogramMap::const_iterator it = top_->histograms_.find(name); - if (it != top_->histograms_.end()) - it->second->ClearFlags(HistogramBase::kCallbackExists); -} - -// static -StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback( - const std::string& name) { - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - const auto it = top_->callbacks_.find(name); - return it != top_->callbacks_.end() ? it->second : OnSampleCallback(); -} - -// static -size_t StatisticsRecorder::GetHistogramCount() { - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - return top_->histograms_.size(); -} - -// static -void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) { - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - - const HistogramMap::iterator found = top_->histograms_.find(name); - if (found == top_->histograms_.end()) - return; - - HistogramBase* const base = found->second; - if (base->GetHistogramType() != SPARSE_HISTOGRAM) { - // When forgetting a histogram, it's likely that other information is - // also becoming invalid. Clear the persistent reference that may no - // longer be valid. There's no danger in this as, at worst, duplicates - // will be created in persistent memory. - static_cast(base)->bucket_ranges()->set_persistent_reference(0); - } - - top_->histograms_.erase(found); -} - -// static -std::unique_ptr -StatisticsRecorder::CreateTemporaryForTesting() { - const AutoLock auto_lock(lock_.Get()); - return WrapUnique(new StatisticsRecorder()); -} - -// static -void StatisticsRecorder::SetRecordChecker( - std::unique_ptr record_checker) { - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - top_->record_checker_ = std::move(record_checker); -} - -// static -bool StatisticsRecorder::ShouldRecordHistogram(uint64_t histogram_hash) { - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - return !top_->record_checker_ || - top_->record_checker_->ShouldRecord(histogram_hash); -} - -// static -StatisticsRecorder::Histograms StatisticsRecorder::GetHistograms() { - // This must be called *before* the lock is acquired below because it will - // call back into this object to register histograms. Those called methods - // will acquire the lock at that time. - ImportGlobalPersistentHistograms(); - - Histograms out; - - const AutoLock auto_lock(lock_.Get()); - EnsureGlobalRecorderWhileLocked(); - - out.reserve(top_->histograms_.size()); - for (const auto& entry : top_->histograms_) - out.push_back(entry.second); - - return out; -} - -// static -StatisticsRecorder::Histograms StatisticsRecorder::Sort(Histograms histograms) { - std::sort(histograms.begin(), histograms.end(), &HistogramNameLesser); - return histograms; -} - -// static -StatisticsRecorder::Histograms StatisticsRecorder::WithName( - Histograms histograms, - const std::string& query) { - // Need a C-string query for comparisons against C-string histogram name. - const char* const query_string = query.c_str(); - histograms.erase(std::remove_if(histograms.begin(), histograms.end(), - [query_string](const HistogramBase* const h) { - return !strstr(h->histogram_name(), - query_string); - }), - histograms.end()); - return histograms; -} - -// static -StatisticsRecorder::Histograms StatisticsRecorder::NonPersistent( - Histograms histograms) { - histograms.erase( - std::remove_if(histograms.begin(), histograms.end(), - [](const HistogramBase* const h) { - return (h->flags() & HistogramBase::kIsPersistent) != 0; - }), - histograms.end()); - return histograms; -} - -// static -void StatisticsRecorder::ImportGlobalPersistentHistograms() { - // Import histograms from known persistent storage. Histograms could have been - // added by other processes and they must be fetched and recognized locally. - // If the persistent memory segment is not shared between processes, this call - // does nothing. - if (GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get()) - allocator->ImportHistogramsToStatisticsRecorder(); -} - -// This singleton instance should be started during the single threaded portion -// of main(), and hence it is not thread safe. It initializes globals to provide -// support for all future calls. -StatisticsRecorder::StatisticsRecorder() { - lock_.Get().AssertAcquired(); - previous_ = top_; - top_ = this; - InitLogOnShutdownWhileLocked(); -} - -// static -void StatisticsRecorder::InitLogOnShutdownWhileLocked() { - lock_.Get().AssertAcquired(); - if (!is_vlog_initialized_ && VLOG_IS_ON(1)) { - is_vlog_initialized_ = true; - const auto dump_to_vlog = [](void*) { - std::string output; - WriteGraph("", &output); - VLOG(1) << output; - }; - AtExitManager::RegisterCallback(dump_to_vlog, nullptr); - } -} - -} // namespace base diff --git a/metrics/statistics_recorder.h b/metrics/statistics_recorder.h deleted file mode 100644 index 87a93110e..000000000 --- a/metrics/statistics_recorder.h +++ /dev/null @@ -1,302 +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. - -// StatisticsRecorder holds all Histograms and BucketRanges that are used by -// Histograms in the system. It provides a general place for -// Histograms/BucketRanges to register, and supports a global API for accessing -// (i.e., dumping, or graphing) the data. - -#ifndef BASE_METRICS_STATISTICS_RECORDER_H_ -#define BASE_METRICS_STATISTICS_RECORDER_H_ - -#include - -#include -#include -#include -#include -#include - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/gtest_prod_util.h" -#include "base/lazy_instance.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/record_histogram_checker.h" -#include "base/strings/string_piece.h" -#include "base/synchronization/lock.h" - -namespace base { - -class BucketRanges; -class HistogramSnapshotManager; - -// In-memory recorder of usage statistics (aka metrics, aka histograms). -// -// All the public methods are static and act on a global recorder. This global -// recorder is internally synchronized and all the static methods are thread -// safe. -// -// StatisticsRecorder doesn't have any public constructor. For testing purpose, -// you can create a temporary recorder using the factory method -// CreateTemporaryForTesting(). This temporary recorder becomes the global one -// until deleted. When this temporary recorder is deleted, it restores the -// previous global one. -class BASE_EXPORT StatisticsRecorder { - public: - // An interface class that allows the StatisticsRecorder to forcibly merge - // histograms from providers when necessary. - class HistogramProvider { - public: - // Merges all histogram information into the global versions. - virtual void MergeHistogramDeltas() = 0; - }; - - typedef std::vector Histograms; - - // Restores the previous global recorder. - // - // When several temporary recorders are created using - // CreateTemporaryForTesting(), these recorders must be deleted in reverse - // order of creation. - // - // This method is thread safe. - // - // Precondition: The recorder being deleted is the current global recorder. - ~StatisticsRecorder(); - - // Registers a provider of histograms that can be called to merge those into - // the global recorder. Calls to ImportProvidedHistograms() will fetch from - // registered providers. - // - // This method is thread safe. - static void RegisterHistogramProvider( - const WeakPtr& provider); - - // Registers or adds a new histogram to the collection of statistics. If an - // identically named histogram is already registered, then the argument - // |histogram| will be deleted. The returned value is always the registered - // histogram (either the argument, or the pre-existing registered histogram). - // - // This method is thread safe. - static HistogramBase* RegisterOrDeleteDuplicate(HistogramBase* histogram); - - // Registers or adds a new BucketRanges. If an equivalent BucketRanges is - // already registered, then the argument |ranges| will be deleted. The - // returned value is always the registered BucketRanges (either the argument, - // or the pre-existing one). - // - // This method is thread safe. - static const BucketRanges* RegisterOrDeleteDuplicateRanges( - const BucketRanges* ranges); - - // Methods for appending histogram data to a string. Only histograms which - // have |query| as a substring are written to |output| (an empty string will - // process all registered histograms). - // - // These methods are thread safe. - static void WriteHTMLGraph(const std::string& query, std::string* output); - static void WriteGraph(const std::string& query, std::string* output); - - // Returns the histograms with |verbosity_level| as the serialization - // verbosity. - // - // This method is thread safe. - static std::string ToJSON(JSONVerbosityLevel verbosity_level); - - // Gets existing histograms. - // - // The order of returned histograms is not guaranteed. - // - // Ownership of the individual histograms remains with the StatisticsRecorder. - // - // This method is thread safe. - static Histograms GetHistograms(); - - // Gets BucketRanges used by all histograms registered. The order of returned - // BucketRanges is not guaranteed. - // - // This method is thread safe. - static std::vector GetBucketRanges(); - - // Finds a histogram by name. Matches the exact name. Returns a null pointer - // if a matching histogram is not found. - // - // This method is thread safe. - static HistogramBase* FindHistogram(base::StringPiece name); - - // Imports histograms from providers. - // - // This method must be called on the UI thread. - static void ImportProvidedHistograms(); - - // Snapshots all histograms via |snapshot_manager|. |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|. - static void PrepareDeltas(bool include_persistent, - HistogramBase::Flags flags_to_set, - HistogramBase::Flags required_flags, - HistogramSnapshotManager* snapshot_manager); - - typedef base::Callback OnSampleCallback; - - // Sets the callback to notify when a new sample is recorded on the histogram - // referred to by |histogram_name|. Can be called before or after the - // histogram is created. Returns whether the callback was successfully set. - // - // This method is thread safe. - static bool SetCallback(const std::string& histogram_name, - const OnSampleCallback& callback); - - // Clears any callback set on the histogram referred to by |histogram_name|. - // - // This method is thread safe. - static void ClearCallback(const std::string& histogram_name); - - // Retrieves the callback for the histogram referred to by |histogram_name|, - // or a null callback if no callback exists for this histogram. - // - // This method is thread safe. - static OnSampleCallback FindCallback(const std::string& histogram_name); - - // Returns the number of known histograms. - // - // This method is thread safe. - static size_t GetHistogramCount(); - - // Initializes logging histograms with --v=1. Safe to call multiple times. - // Is called from ctor but for browser it seems that it is more useful to - // start logging after statistics recorder, so we need to init log-on-shutdown - // later. - // - // This method is thread safe. - static void InitLogOnShutdown(); - - // Removes a histogram from the internal set of known ones. This can be - // necessary during testing persistent histograms where the underlying - // memory is being released. - // - // This method is thread safe. - static void ForgetHistogramForTesting(base::StringPiece name); - - // Creates a temporary StatisticsRecorder object for testing purposes. All new - // histograms will be registered in it until it is destructed or pushed aside - // for the lifetime of yet another StatisticsRecorder object. The destruction - // of the returned object will re-activate the previous one. - // StatisticsRecorder objects must be deleted in the opposite order to which - // they're created. - // - // This method is thread safe. - static std::unique_ptr CreateTemporaryForTesting() - WARN_UNUSED_RESULT; - - // Sets the record checker for determining if a histogram should be recorded. - // Record checker doesn't affect any already recorded histograms, so this - // method must be called very early, before any threads have started. - // Record checker methods can be called on any thread, so they shouldn't - // mutate any state. - static void SetRecordChecker( - std::unique_ptr record_checker); - - // Checks if the given histogram should be recorded based on the - // ShouldRecord() method of the record checker. If the record checker is not - // set, returns true. - // - // This method is thread safe. - static bool ShouldRecordHistogram(uint64_t histogram_hash); - - // Sorts histograms by name. - static Histograms Sort(Histograms histograms); - - // Filters histograms by name. Only histograms which have |query| as a - // substring in their name are kept. An empty query keeps all histograms. - static Histograms WithName(Histograms histograms, const std::string& query); - - // Filters histograms by persistency. Only non-persistent histograms are kept. - static Histograms NonPersistent(Histograms histograms); - - private: - typedef std::vector> HistogramProviders; - - typedef std::unordered_map - HistogramMap; - - // We keep a map of callbacks to histograms, so that as histograms are - // created, we can set the callback properly. - typedef std::unordered_map CallbackMap; - - struct BucketRangesHash { - size_t operator()(const BucketRanges* a) const; - }; - - struct BucketRangesEqual { - bool operator()(const BucketRanges* a, const BucketRanges* b) const; - }; - - typedef std:: - unordered_set - RangesMap; - - friend class StatisticsRecorderTest; - FRIEND_TEST_ALL_PREFIXES(StatisticsRecorderTest, IterationTest); - - // Initializes the global recorder if it doesn't already exist. Safe to call - // multiple times. - // - // Precondition: The global lock is already acquired. - static void EnsureGlobalRecorderWhileLocked(); - - // Gets histogram providers. - // - // This method is thread safe. - static HistogramProviders GetHistogramProviders(); - - // Imports histograms from global persistent memory. - // - // Precondition: The global lock must not be held during this call. - static void ImportGlobalPersistentHistograms(); - - // Constructs a new StatisticsRecorder and sets it as the current global - // recorder. - // - // Precondition: The global lock is already acquired. - StatisticsRecorder(); - - // Initialize implementation but without lock. Caller should guard - // StatisticsRecorder by itself if needed (it isn't in unit tests). - // - // Precondition: The global lock is already acquired. - static void InitLogOnShutdownWhileLocked(); - - HistogramMap histograms_; - CallbackMap callbacks_; - RangesMap ranges_; - HistogramProviders providers_; - std::unique_ptr record_checker_; - - // Previous global recorder that existed when this one was created. - StatisticsRecorder* previous_ = nullptr; - - // Global lock for internal synchronization. - static LazyInstance::Leaky lock_; - - // Current global recorder. This recorder is used by static methods. When a - // new global recorder is created by CreateTemporaryForTesting(), then the - // previous global recorder is referenced by top_->previous_. - static StatisticsRecorder* top_; - - // Tracks whether InitLogOnShutdownWhileLocked() has registered a logging - // function that will be called when the program finishes. - static bool is_vlog_initialized_; - - DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder); -}; - -} // namespace base - -#endif // BASE_METRICS_STATISTICS_RECORDER_H_ diff --git a/metrics/statistics_recorder_unittest.cc b/metrics/statistics_recorder_unittest.cc deleted file mode 100644 index a65283cfa..000000000 --- a/metrics/statistics_recorder_unittest.cc +++ /dev/null @@ -1,720 +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/metrics/statistics_recorder.h" - -#include - -#include -#include -#include - -#include "base/bind.h" -#include "base/json/json_reader.h" -#include "base/logging.h" -#include "base/memory/weak_ptr.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_macros.h" -#include "base/metrics/persistent_histogram_allocator.h" -#include "base/metrics/record_histogram_checker.h" -#include "base/metrics/sparse_histogram.h" -#include "base/values.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -// Class to make sure any manipulations we do to the min log level are -// contained (i.e., do not affect other unit tests). -class LogStateSaver { - public: - LogStateSaver() : old_min_log_level_(logging::GetMinLogLevel()) {} - - ~LogStateSaver() { logging::SetMinLogLevel(old_min_log_level_); } - - private: - int old_min_log_level_; - - DISALLOW_COPY_AND_ASSIGN(LogStateSaver); -}; - -// Test implementation of RecordHistogramChecker interface. -class OddRecordHistogramChecker : public base::RecordHistogramChecker { - public: - ~OddRecordHistogramChecker() override = default; - - // base::RecordHistogramChecker: - bool ShouldRecord(uint64_t histogram_hash) const override { - return histogram_hash % 2; - } -}; - -} // namespace - -namespace base { - -using testing::IsEmpty; -using testing::SizeIs; -using testing::UnorderedElementsAre; - -class StatisticsRecorderTest : public testing::TestWithParam { - protected: - const int32_t kAllocatorMemorySize = 64 << 10; // 64 KiB - - StatisticsRecorderTest() : use_persistent_histogram_allocator_(GetParam()) { - // Each test will have a clean state (no Histogram / BucketRanges - // registered). - InitializeStatisticsRecorder(); - - // Use persistent memory for histograms if so indicated by test parameter. - if (use_persistent_histogram_allocator_) { - GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, - "StatisticsRecorderTest"); - } - } - - ~StatisticsRecorderTest() override { - GlobalHistogramAllocator::ReleaseForTesting(); - UninitializeStatisticsRecorder(); - } - - void InitializeStatisticsRecorder() { - DCHECK(!statistics_recorder_); - statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting(); - } - - // Deletes the global recorder if there is any. This is used by test - // NotInitialized to ensure a clean global state. - void UninitializeStatisticsRecorder() { - statistics_recorder_.reset(); - delete StatisticsRecorder::top_; - DCHECK(!StatisticsRecorder::top_); - } - - bool HasGlobalRecorder() { return StatisticsRecorder::top_ != nullptr; } - - Histogram* CreateHistogram(const char* name, - HistogramBase::Sample min, - HistogramBase::Sample max, - size_t bucket_count) { - BucketRanges* ranges = new BucketRanges(bucket_count + 1); - Histogram::InitializeBucketRanges(min, max, ranges); - const BucketRanges* registered_ranges = - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges); - return new Histogram(name, min, max, registered_ranges); - } - - void InitLogOnShutdown() { StatisticsRecorder::InitLogOnShutdown(); } - - bool IsVLogInitialized() { return StatisticsRecorder::is_vlog_initialized_; } - - void ResetVLogInitialized() { - UninitializeStatisticsRecorder(); - StatisticsRecorder::is_vlog_initialized_ = false; - } - - const bool use_persistent_histogram_allocator_; - - std::unique_ptr statistics_recorder_; - std::unique_ptr old_global_allocator_; - - private: - LogStateSaver log_state_saver_; - - DISALLOW_COPY_AND_ASSIGN(StatisticsRecorderTest); -}; - -// Run all HistogramTest cases with both heap and persistent memory. -INSTANTIATE_TEST_CASE_P(Allocator, StatisticsRecorderTest, testing::Bool()); - -TEST_P(StatisticsRecorderTest, NotInitialized) { - UninitializeStatisticsRecorder(); - EXPECT_FALSE(HasGlobalRecorder()); - - HistogramBase* const histogram = - CreateHistogram("TestHistogram", 1, 1000, 10); - EXPECT_EQ(StatisticsRecorder::RegisterOrDeleteDuplicate(histogram), - histogram); - EXPECT_TRUE(HasGlobalRecorder()); - EXPECT_THAT(StatisticsRecorder::GetHistograms(), - UnorderedElementsAre(histogram)); - - UninitializeStatisticsRecorder(); - EXPECT_FALSE(HasGlobalRecorder()); - - BucketRanges* const ranges = new BucketRanges(3); - ranges->ResetChecksum(); - EXPECT_EQ(StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges), - ranges); - EXPECT_TRUE(HasGlobalRecorder()); - EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), - UnorderedElementsAre(ranges)); -} - -TEST_P(StatisticsRecorderTest, RegisterBucketRanges) { - std::vector registered_ranges; - - BucketRanges* ranges1 = new BucketRanges(3); - ranges1->ResetChecksum(); - BucketRanges* ranges2 = new BucketRanges(4); - ranges2->ResetChecksum(); - - // Register new ranges. - EXPECT_EQ(ranges1, - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges1)); - EXPECT_EQ(ranges2, - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges2)); - EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), - UnorderedElementsAre(ranges1, ranges2)); - - // Register some ranges again. - EXPECT_EQ(ranges1, - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges1)); - EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), - UnorderedElementsAre(ranges1, ranges2)); - - // Make sure the ranges is still the one we know. - ASSERT_EQ(3u, ranges1->size()); - EXPECT_EQ(0, ranges1->range(0)); - EXPECT_EQ(0, ranges1->range(1)); - EXPECT_EQ(0, ranges1->range(2)); - - // Register ranges with same values. - BucketRanges* ranges3 = new BucketRanges(3); - ranges3->ResetChecksum(); - EXPECT_EQ(ranges1, // returning ranges1 - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges3)); - EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), - UnorderedElementsAre(ranges1, ranges2)); -} - -TEST_P(StatisticsRecorderTest, RegisterHistogram) { - // Create a Histogram that was not registered. - Histogram* const histogram1 = CreateHistogram("TestHistogram1", 1, 1000, 10); - - EXPECT_THAT(StatisticsRecorder::GetHistograms(), IsEmpty()); - - // Register the Histogram. - EXPECT_EQ(histogram1, - StatisticsRecorder::RegisterOrDeleteDuplicate(histogram1)); - EXPECT_THAT(StatisticsRecorder::GetHistograms(), - UnorderedElementsAre(histogram1)); - - // Register the same Histogram again. - EXPECT_EQ(histogram1, - StatisticsRecorder::RegisterOrDeleteDuplicate(histogram1)); - EXPECT_THAT(StatisticsRecorder::GetHistograms(), - UnorderedElementsAre(histogram1)); - - // Register another Histogram with the same name. - Histogram* const histogram2 = CreateHistogram("TestHistogram1", 1, 1000, 10); - EXPECT_NE(histogram1, histogram2); - EXPECT_EQ(histogram1, - StatisticsRecorder::RegisterOrDeleteDuplicate(histogram2)); - EXPECT_THAT(StatisticsRecorder::GetHistograms(), - UnorderedElementsAre(histogram1)); - - // Register another Histogram with a different name. - Histogram* const histogram3 = CreateHistogram("TestHistogram0", 1, 1000, 10); - EXPECT_NE(histogram1, histogram3); - EXPECT_EQ(histogram3, - StatisticsRecorder::RegisterOrDeleteDuplicate(histogram3)); - EXPECT_THAT(StatisticsRecorder::GetHistograms(), - UnorderedElementsAre(histogram1, histogram3)); -} - -TEST_P(StatisticsRecorderTest, FindHistogram) { - HistogramBase* histogram1 = Histogram::FactoryGet( - "TestHistogram1", 1, 1000, 10, HistogramBase::kNoFlags); - HistogramBase* histogram2 = Histogram::FactoryGet( - "TestHistogram2", 1, 1000, 10, HistogramBase::kNoFlags); - - EXPECT_EQ(histogram1, StatisticsRecorder::FindHistogram("TestHistogram1")); - EXPECT_EQ(histogram2, StatisticsRecorder::FindHistogram("TestHistogram2")); - EXPECT_FALSE(StatisticsRecorder::FindHistogram("TestHistogram")); - - // Create a new global allocator using the same memory as the old one. Any - // old one is kept around so the memory doesn't get released. - old_global_allocator_ = GlobalHistogramAllocator::ReleaseForTesting(); - if (use_persistent_histogram_allocator_) { - GlobalHistogramAllocator::CreateWithPersistentMemory( - const_cast(old_global_allocator_->data()), - old_global_allocator_->length(), 0, old_global_allocator_->Id(), - old_global_allocator_->Name()); - } - - // Reset statistics-recorder to validate operation from a clean start. - UninitializeStatisticsRecorder(); - InitializeStatisticsRecorder(); - - if (use_persistent_histogram_allocator_) { - EXPECT_TRUE(StatisticsRecorder::FindHistogram("TestHistogram1")); - EXPECT_TRUE(StatisticsRecorder::FindHistogram("TestHistogram2")); - } else { - EXPECT_FALSE(StatisticsRecorder::FindHistogram("TestHistogram1")); - EXPECT_FALSE(StatisticsRecorder::FindHistogram("TestHistogram2")); - } - EXPECT_FALSE(StatisticsRecorder::FindHistogram("TestHistogram")); -} - -TEST_P(StatisticsRecorderTest, WithName) { - Histogram::FactoryGet("TestHistogram1", 1, 1000, 10, Histogram::kNoFlags); - Histogram::FactoryGet("TestHistogram2", 1, 1000, 10, Histogram::kNoFlags); - Histogram::FactoryGet("TestHistogram3", 1, 1000, 10, Histogram::kNoFlags); - - const auto histograms = StatisticsRecorder::GetHistograms(); - EXPECT_THAT(histograms, SizeIs(3)); - EXPECT_THAT(StatisticsRecorder::WithName(histograms, ""), SizeIs(3)); - EXPECT_THAT(StatisticsRecorder::WithName(histograms, "Test"), SizeIs(3)); - EXPECT_THAT(StatisticsRecorder::WithName(histograms, "1"), SizeIs(1)); - EXPECT_THAT(StatisticsRecorder::WithName(histograms, "hello"), IsEmpty()); -} - -TEST_P(StatisticsRecorderTest, RegisterHistogramWithFactoryGet) { - EXPECT_THAT(StatisticsRecorder::GetHistograms(), IsEmpty()); - - // Create a histogram. - HistogramBase* const histogram1 = Histogram::FactoryGet( - "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - EXPECT_THAT(StatisticsRecorder::GetHistograms(), - UnorderedElementsAre(histogram1)); - - // Get an existing histogram. - HistogramBase* const histogram2 = Histogram::FactoryGet( - "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - EXPECT_EQ(histogram1, histogram2); - EXPECT_THAT(StatisticsRecorder::GetHistograms(), - UnorderedElementsAre(histogram1)); - - // Create a LinearHistogram. - HistogramBase* const histogram3 = LinearHistogram::FactoryGet( - "TestLinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - EXPECT_THAT(StatisticsRecorder::GetHistograms(), - UnorderedElementsAre(histogram1, histogram3)); - - // Create a BooleanHistogram. - HistogramBase* const histogram4 = BooleanHistogram::FactoryGet( - "TestBooleanHistogram", HistogramBase::kNoFlags); - EXPECT_THAT(StatisticsRecorder::GetHistograms(), - UnorderedElementsAre(histogram1, histogram3, histogram4)); - - // Create a CustomHistogram. - std::vector custom_ranges; - custom_ranges.push_back(1); - custom_ranges.push_back(5); - HistogramBase* const histogram5 = CustomHistogram::FactoryGet( - "TestCustomHistogram", custom_ranges, HistogramBase::kNoFlags); - EXPECT_THAT( - StatisticsRecorder::GetHistograms(), - UnorderedElementsAre(histogram1, histogram3, histogram4, histogram5)); -} - -TEST_P(StatisticsRecorderTest, RegisterHistogramWithMacros) { - // Macros cache pointers and so tests that use them can only be run once. - // Stop immediately if this test has run previously. - static bool already_run = false; - if (already_run) - return; - already_run = true; - - StatisticsRecorder::Histograms registered_histograms; - - HistogramBase* histogram = Histogram::FactoryGet( - "TestHistogramCounts", 1, 1000000, 50, HistogramBase::kNoFlags); - - // The histogram we got from macro is the same as from FactoryGet. - LOCAL_HISTOGRAM_COUNTS("TestHistogramCounts", 30); - registered_histograms = StatisticsRecorder::GetHistograms(); - ASSERT_EQ(1u, registered_histograms.size()); - EXPECT_EQ(histogram, registered_histograms[0]); - - LOCAL_HISTOGRAM_TIMES("TestHistogramTimes", TimeDelta::FromDays(1)); - LOCAL_HISTOGRAM_ENUMERATION("TestHistogramEnumeration", 20, 200); - - EXPECT_THAT(StatisticsRecorder::GetHistograms(), SizeIs(3)); -} - -TEST_P(StatisticsRecorderTest, BucketRangesSharing) { - EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), IsEmpty()); - - Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags); - Histogram::FactoryGet("Histogram2", 1, 64, 8, HistogramBase::kNoFlags); - EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), SizeIs(1)); - - Histogram::FactoryGet("Histogram3", 1, 64, 16, HistogramBase::kNoFlags); - EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), SizeIs(2)); -} - -TEST_P(StatisticsRecorderTest, ToJSON) { - Histogram::FactoryGet("TestHistogram1", 1, 1000, 50, HistogramBase::kNoFlags) - ->Add(30); - Histogram::FactoryGet("TestHistogram1", 1, 1000, 50, HistogramBase::kNoFlags) - ->Add(40); - Histogram::FactoryGet("TestHistogram2", 1, 1000, 50, HistogramBase::kNoFlags) - ->Add(30); - Histogram::FactoryGet("TestHistogram2", 1, 1000, 50, HistogramBase::kNoFlags) - ->Add(40); - - std::string json(StatisticsRecorder::ToJSON(JSON_VERBOSITY_LEVEL_FULL)); - - // Check for valid JSON. - std::unique_ptr root = JSONReader::Read(json); - ASSERT_TRUE(root.get()); - - DictionaryValue* root_dict = nullptr; - ASSERT_TRUE(root->GetAsDictionary(&root_dict)); - - // No query should be set. - ASSERT_FALSE(root_dict->HasKey("query")); - - ListValue* histogram_list = nullptr; - ASSERT_TRUE(root_dict->GetList("histograms", &histogram_list)); - ASSERT_EQ(2u, histogram_list->GetSize()); - - // Examine the first histogram. - DictionaryValue* histogram_dict = nullptr; - ASSERT_TRUE(histogram_list->GetDictionary(0, &histogram_dict)); - - int sample_count; - ASSERT_TRUE(histogram_dict->GetInteger("count", &sample_count)); - EXPECT_EQ(2, sample_count); - - ListValue* buckets_list = nullptr; - ASSERT_TRUE(histogram_dict->GetList("buckets", &buckets_list)); - EXPECT_EQ(2u, buckets_list->GetList().size()); - - // Check the serialized JSON with a different verbosity level. - json = StatisticsRecorder::ToJSON(JSON_VERBOSITY_LEVEL_OMIT_BUCKETS); - root = JSONReader::Read(json); - ASSERT_TRUE(root.get()); - root_dict = nullptr; - ASSERT_TRUE(root->GetAsDictionary(&root_dict)); - histogram_list = nullptr; - ASSERT_TRUE(root_dict->GetList("histograms", &histogram_list)); - ASSERT_EQ(2u, histogram_list->GetSize()); - histogram_dict = nullptr; - ASSERT_TRUE(histogram_list->GetDictionary(0, &histogram_dict)); - sample_count = 0; - ASSERT_TRUE(histogram_dict->GetInteger("count", &sample_count)); - EXPECT_EQ(2, sample_count); - buckets_list = nullptr; - // Bucket information should be omitted. - ASSERT_FALSE(histogram_dict->GetList("buckets", &buckets_list)); -} - -TEST_P(StatisticsRecorderTest, IterationTest) { - Histogram::FactoryGet("IterationTest1", 1, 64, 16, HistogramBase::kNoFlags); - Histogram::FactoryGet("IterationTest2", 1, 64, 16, HistogramBase::kNoFlags); - - auto histograms = StatisticsRecorder::GetHistograms(); - EXPECT_THAT(histograms, SizeIs(2)); - histograms = StatisticsRecorder::NonPersistent(std::move(histograms)); - EXPECT_THAT(histograms, SizeIs(use_persistent_histogram_allocator_ ? 0 : 2)); - - // Create a new global allocator using the same memory as the old one. Any - // old one is kept around so the memory doesn't get released. - old_global_allocator_ = GlobalHistogramAllocator::ReleaseForTesting(); - if (use_persistent_histogram_allocator_) { - GlobalHistogramAllocator::CreateWithPersistentMemory( - const_cast(old_global_allocator_->data()), - old_global_allocator_->length(), 0, old_global_allocator_->Id(), - old_global_allocator_->Name()); - } - - // Reset statistics-recorder to validate operation from a clean start. - UninitializeStatisticsRecorder(); - InitializeStatisticsRecorder(); - - histograms = StatisticsRecorder::GetHistograms(); - EXPECT_THAT(histograms, SizeIs(use_persistent_histogram_allocator_ ? 2 : 0)); - histograms = StatisticsRecorder::NonPersistent(std::move(histograms)); - EXPECT_THAT(histograms, IsEmpty()); -} - -namespace { - -// CallbackCheckWrapper is simply a convenient way to check and store that -// a callback was actually run. -struct CallbackCheckWrapper { - CallbackCheckWrapper() : called(false), last_histogram_value(0) {} - - void OnHistogramChanged(base::HistogramBase::Sample histogram_value) { - called = true; - last_histogram_value = histogram_value; - } - - bool called; - base::HistogramBase::Sample last_histogram_value; -}; - -} // namespace - -// Check that you can't overwrite the callback with another. -TEST_P(StatisticsRecorderTest, SetCallbackFailsWithoutHistogramTest) { - CallbackCheckWrapper callback_wrapper; - - bool result = base::StatisticsRecorder::SetCallback( - "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged, - base::Unretained(&callback_wrapper))); - EXPECT_TRUE(result); - - result = base::StatisticsRecorder::SetCallback( - "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged, - base::Unretained(&callback_wrapper))); - EXPECT_FALSE(result); -} - -// Check that you can't overwrite the callback with another. -TEST_P(StatisticsRecorderTest, SetCallbackFailsWithHistogramTest) { - HistogramBase* histogram = Histogram::FactoryGet("TestHistogram", 1, 1000, 10, - HistogramBase::kNoFlags); - EXPECT_TRUE(histogram); - - CallbackCheckWrapper callback_wrapper; - - bool result = base::StatisticsRecorder::SetCallback( - "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged, - base::Unretained(&callback_wrapper))); - EXPECT_TRUE(result); - EXPECT_EQ(histogram->flags() & base::HistogramBase::kCallbackExists, - base::HistogramBase::kCallbackExists); - - result = base::StatisticsRecorder::SetCallback( - "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged, - base::Unretained(&callback_wrapper))); - EXPECT_FALSE(result); - EXPECT_EQ(histogram->flags() & base::HistogramBase::kCallbackExists, - base::HistogramBase::kCallbackExists); - - histogram->Add(1); - - EXPECT_TRUE(callback_wrapper.called); -} - -// Check that you can't overwrite the callback with another. -TEST_P(StatisticsRecorderTest, ClearCallbackSuceedsWithHistogramTest) { - HistogramBase* histogram = Histogram::FactoryGet("TestHistogram", 1, 1000, 10, - HistogramBase::kNoFlags); - EXPECT_TRUE(histogram); - - CallbackCheckWrapper callback_wrapper; - - bool result = base::StatisticsRecorder::SetCallback( - "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged, - base::Unretained(&callback_wrapper))); - EXPECT_TRUE(result); - EXPECT_EQ(histogram->flags() & base::HistogramBase::kCallbackExists, - base::HistogramBase::kCallbackExists); - - base::StatisticsRecorder::ClearCallback("TestHistogram"); - EXPECT_EQ(histogram->flags() & base::HistogramBase::kCallbackExists, 0); - - histogram->Add(1); - - EXPECT_FALSE(callback_wrapper.called); -} - -// Check that callback is used. -TEST_P(StatisticsRecorderTest, CallbackUsedTest) { - { - HistogramBase* histogram = Histogram::FactoryGet( - "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - EXPECT_TRUE(histogram); - - CallbackCheckWrapper callback_wrapper; - - base::StatisticsRecorder::SetCallback( - "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged, - base::Unretained(&callback_wrapper))); - - histogram->Add(1); - - EXPECT_TRUE(callback_wrapper.called); - EXPECT_EQ(callback_wrapper.last_histogram_value, 1); - } - - { - HistogramBase* linear_histogram = LinearHistogram::FactoryGet( - "TestLinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - - CallbackCheckWrapper callback_wrapper; - - base::StatisticsRecorder::SetCallback( - "TestLinearHistogram", - base::Bind(&CallbackCheckWrapper::OnHistogramChanged, - base::Unretained(&callback_wrapper))); - - linear_histogram->Add(1); - - EXPECT_TRUE(callback_wrapper.called); - EXPECT_EQ(callback_wrapper.last_histogram_value, 1); - } - - { - std::vector custom_ranges; - custom_ranges.push_back(1); - custom_ranges.push_back(5); - HistogramBase* custom_histogram = CustomHistogram::FactoryGet( - "TestCustomHistogram", custom_ranges, HistogramBase::kNoFlags); - - CallbackCheckWrapper callback_wrapper; - - base::StatisticsRecorder::SetCallback( - "TestCustomHistogram", - base::Bind(&CallbackCheckWrapper::OnHistogramChanged, - base::Unretained(&callback_wrapper))); - - custom_histogram->Add(1); - - EXPECT_TRUE(callback_wrapper.called); - EXPECT_EQ(callback_wrapper.last_histogram_value, 1); - } - - { - HistogramBase* custom_histogram = SparseHistogram::FactoryGet( - "TestSparseHistogram", HistogramBase::kNoFlags); - - CallbackCheckWrapper callback_wrapper; - - base::StatisticsRecorder::SetCallback( - "TestSparseHistogram", - base::Bind(&CallbackCheckWrapper::OnHistogramChanged, - base::Unretained(&callback_wrapper))); - - custom_histogram->Add(1); - - EXPECT_TRUE(callback_wrapper.called); - EXPECT_EQ(callback_wrapper.last_histogram_value, 1); - } -} - -// Check that setting a callback before the histogram exists works. -TEST_P(StatisticsRecorderTest, CallbackUsedBeforeHistogramCreatedTest) { - CallbackCheckWrapper callback_wrapper; - - base::StatisticsRecorder::SetCallback( - "TestHistogram", base::Bind(&CallbackCheckWrapper::OnHistogramChanged, - base::Unretained(&callback_wrapper))); - - HistogramBase* histogram = Histogram::FactoryGet("TestHistogram", 1, 1000, 10, - HistogramBase::kNoFlags); - EXPECT_TRUE(histogram); - histogram->Add(1); - - EXPECT_TRUE(callback_wrapper.called); - EXPECT_EQ(callback_wrapper.last_histogram_value, 1); -} - -TEST_P(StatisticsRecorderTest, LogOnShutdownNotInitialized) { - ResetVLogInitialized(); - logging::SetMinLogLevel(logging::LOG_WARNING); - InitializeStatisticsRecorder(); - EXPECT_FALSE(VLOG_IS_ON(1)); - EXPECT_FALSE(IsVLogInitialized()); - InitLogOnShutdown(); - EXPECT_FALSE(IsVLogInitialized()); -} - -TEST_P(StatisticsRecorderTest, LogOnShutdownInitializedExplicitly) { - ResetVLogInitialized(); - logging::SetMinLogLevel(logging::LOG_WARNING); - InitializeStatisticsRecorder(); - EXPECT_FALSE(VLOG_IS_ON(1)); - EXPECT_FALSE(IsVLogInitialized()); - logging::SetMinLogLevel(logging::LOG_VERBOSE); - EXPECT_TRUE(VLOG_IS_ON(1)); - InitLogOnShutdown(); - EXPECT_TRUE(IsVLogInitialized()); -} - -TEST_P(StatisticsRecorderTest, LogOnShutdownInitialized) { - ResetVLogInitialized(); - logging::SetMinLogLevel(logging::LOG_VERBOSE); - InitializeStatisticsRecorder(); - EXPECT_TRUE(VLOG_IS_ON(1)); - EXPECT_TRUE(IsVLogInitialized()); -} - -class TestHistogramProvider : public StatisticsRecorder::HistogramProvider { - public: - TestHistogramProvider(std::unique_ptr allocator) - : allocator_(std::move(allocator)), weak_factory_(this) { - StatisticsRecorder::RegisterHistogramProvider(weak_factory_.GetWeakPtr()); - } - - void MergeHistogramDeltas() override { - PersistentHistogramAllocator::Iterator hist_iter(allocator_.get()); - while (true) { - std::unique_ptr histogram = hist_iter.GetNext(); - if (!histogram) - break; - allocator_->MergeHistogramDeltaToStatisticsRecorder(histogram.get()); - } - } - - private: - std::unique_ptr allocator_; - WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(TestHistogramProvider); -}; - -TEST_P(StatisticsRecorderTest, ImportHistogramsTest) { - // Create a second SR to create some histograms for later import. - std::unique_ptr temp_sr = - StatisticsRecorder::CreateTemporaryForTesting(); - - // Extract any existing global allocator so a new one can be created. - std::unique_ptr old_allocator = - GlobalHistogramAllocator::ReleaseForTesting(); - - // Create a histogram inside a new allocator for testing. - GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, ""); - HistogramBase* histogram = LinearHistogram::FactoryGet("Foo", 1, 10, 11, 0); - histogram->Add(3); - - // Undo back to the starting point. - std::unique_ptr new_allocator = - GlobalHistogramAllocator::ReleaseForTesting(); - GlobalHistogramAllocator::Set(std::move(old_allocator)); - temp_sr.reset(); - - // Create a provider that can supply histograms to the current SR. - TestHistogramProvider provider(std::move(new_allocator)); - - // Verify that the created histogram is no longer known. - ASSERT_FALSE(StatisticsRecorder::FindHistogram(histogram->histogram_name())); - - // Now test that it merges. - StatisticsRecorder::ImportProvidedHistograms(); - HistogramBase* found = - StatisticsRecorder::FindHistogram(histogram->histogram_name()); - ASSERT_TRUE(found); - EXPECT_NE(histogram, found); - std::unique_ptr snapshot = found->SnapshotSamples(); - EXPECT_EQ(1, snapshot->TotalCount()); - EXPECT_EQ(1, snapshot->GetCount(3)); - - // Finally, verify that updates can also be merged. - histogram->Add(3); - histogram->Add(5); - StatisticsRecorder::ImportProvidedHistograms(); - snapshot = found->SnapshotSamples(); - EXPECT_EQ(3, snapshot->TotalCount()); - EXPECT_EQ(2, snapshot->GetCount(3)); - EXPECT_EQ(1, snapshot->GetCount(5)); -} - -TEST_P(StatisticsRecorderTest, RecordHistogramChecker) { - // Before record checker is set all histograms should be recorded. - EXPECT_TRUE(StatisticsRecorder::ShouldRecordHistogram(1)); - EXPECT_TRUE(StatisticsRecorder::ShouldRecordHistogram(2)); - - auto record_checker = std::make_unique(); - StatisticsRecorder::SetRecordChecker(std::move(record_checker)); - EXPECT_TRUE(StatisticsRecorder::ShouldRecordHistogram(1)); - EXPECT_FALSE(StatisticsRecorder::ShouldRecordHistogram(2)); -} - -} // namespace base diff --git a/metrics/user_metrics.cc b/metrics/user_metrics.cc deleted file mode 100644 index 9fcc9e8a1..000000000 --- a/metrics/user_metrics.cc +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/metrics/user_metrics.h" - -#include - -#include - -#include "base/bind.h" -#include "base/lazy_instance.h" -#include "base/location.h" -#include "base/macros.h" -#include "base/threading/thread_checker.h" - -namespace base { -namespace { - -LazyInstance>::DestructorAtExit g_callbacks = - LAZY_INSTANCE_INITIALIZER; -LazyInstance>::DestructorAtExit - g_task_runner = LAZY_INSTANCE_INITIALIZER; - -} // namespace - -void RecordAction(const UserMetricsAction& action) { - RecordComputedAction(action.str_); -} - -void RecordComputedAction(const std::string& action) { - if (!g_task_runner.Get()) { - DCHECK(g_callbacks.Get().empty()); - return; - } - - if (!g_task_runner.Get()->BelongsToCurrentThread()) { - g_task_runner.Get()->PostTask(FROM_HERE, - BindOnce(&RecordComputedAction, action)); - return; - } - - for (const ActionCallback& callback : g_callbacks.Get()) { - callback.Run(action); - } -} - -void AddActionCallback(const ActionCallback& callback) { - // Only allow adding a callback if the task runner is set. - DCHECK(g_task_runner.Get()); - DCHECK(g_task_runner.Get()->BelongsToCurrentThread()); - g_callbacks.Get().push_back(callback); -} - -void RemoveActionCallback(const ActionCallback& callback) { - DCHECK(g_task_runner.Get()); - DCHECK(g_task_runner.Get()->BelongsToCurrentThread()); - std::vector* callbacks = g_callbacks.Pointer(); - for (size_t i = 0; i < callbacks->size(); ++i) { - if ((*callbacks)[i].Equals(callback)) { - callbacks->erase(callbacks->begin() + i); - return; - } - } -} - -void SetRecordActionTaskRunner( - scoped_refptr task_runner) { - DCHECK(task_runner->BelongsToCurrentThread()); - DCHECK(!g_task_runner.Get() || g_task_runner.Get()->BelongsToCurrentThread()); - g_task_runner.Get() = task_runner; -} - -} // namespace base diff --git a/metrics/user_metrics.h b/metrics/user_metrics.h deleted file mode 100644 index 87fbd9cac..000000000 --- a/metrics/user_metrics.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_METRICS_USER_METRICS_H_ -#define BASE_METRICS_USER_METRICS_H_ - -#include - -#include "base/base_export.h" -#include "base/callback.h" -#include "base/metrics/user_metrics_action.h" -#include "base/single_thread_task_runner.h" - -namespace base { - -// This module provides some helper functions for logging actions tracked by -// the user metrics system. - -// For best practices on deciding when to emit a user action, see -// https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/actions/README.md - -// Record that the user performed an action. -// This function must be called after the task runner has been set with -// SetRecordActionTaskRunner(). -// -// "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 should be followed by a -// string literal parameter and not a variable e.g. -// RecordAction(UserMetricsAction("my 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 s and -// 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. -// This function must be called after the task runner has been set with -// SetRecordActionTaskRunner(). -BASE_EXPORT void RecordComputedAction(const std::string& action); - -// Called with the action string. -typedef Callback ActionCallback; - -// Add/remove action callbacks (see above). -// These functions must be called after the task runner has been set with -// SetRecordActionTaskRunner(). -BASE_EXPORT void AddActionCallback(const ActionCallback& callback); -BASE_EXPORT void RemoveActionCallback(const ActionCallback& callback); - -// Set the task runner on which to record actions. -BASE_EXPORT void SetRecordActionTaskRunner( - scoped_refptr task_runner); - -} // namespace base - -#endif // BASE_METRICS_USER_METRICS_H_ diff --git a/metrics/user_metrics_action.h b/metrics/user_metrics_action.h deleted file mode 100644 index 454ed8384..000000000 --- a/metrics/user_metrics_action.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_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 you should use a string literal -// parameter e.g. -// RecordAction(UserMetricsAction("my 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. -// Please see tools/metrics/actions/extract_actions.py for details. -struct UserMetricsAction { - const char* str_; - explicit constexpr UserMetricsAction(const char* str) noexcept : str_(str) {} -}; - -} // namespace base - -#endif // BASE_METRICS_USER_METRICS_ACTION_H_ diff --git a/native_library.cc b/native_library.cc deleted file mode 100644 index 72012a333..000000000 --- a/native_library.cc +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/native_library.h" - -namespace base { - -NativeLibrary LoadNativeLibrary(const FilePath& library_path, - NativeLibraryLoadError* error) { - return LoadNativeLibraryWithOptions( - library_path, NativeLibraryOptions(), error); -} - -} // namespace base diff --git a/native_library.h b/native_library.h deleted file mode 100644 index 04356d968..000000000 --- a/native_library.h +++ /dev/null @@ -1,118 +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_NATIVE_LIBRARY_H_ -#define BASE_NATIVE_LIBRARY_H_ - -// This file defines a cross-platform "NativeLibrary" type which represents -// a loadable module. - -#include - -#include "base/base_export.h" -#include "base/strings/string_piece.h" -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#elif defined(OS_MACOSX) -#import -#endif // OS_* - -namespace base { - -class FilePath; - -#if defined(OS_WIN) -using NativeLibrary = HMODULE; -#elif defined(OS_MACOSX) -enum NativeLibraryType { - BUNDLE, - DYNAMIC_LIB -}; -enum NativeLibraryObjCStatus { - OBJC_UNKNOWN, - OBJC_PRESENT, - OBJC_NOT_PRESENT, -}; -struct NativeLibraryStruct { - NativeLibraryType type; - CFBundleRefNum bundle_resource_ref; - NativeLibraryObjCStatus objc_status; - union { - CFBundleRef bundle; - void* dylib; - }; -}; -using NativeLibrary = NativeLibraryStruct*; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) -using NativeLibrary = void*; -#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; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - std::string message; -#endif // OS_WIN -}; - -struct BASE_EXPORT NativeLibraryOptions { - NativeLibraryOptions() = default; - NativeLibraryOptions(const NativeLibraryOptions& options) = default; - - // If |true|, a loaded library is required to prefer local symbol resolution - // before considering global symbols. Note that this is already the default - // behavior on most systems. Setting this to |false| does not guarantee the - // inverse, i.e., it does not force a preference for global symbols over local - // ones. - bool prefer_own_symbols = false; -}; - -// Loads a native library from disk. Release it with UnloadNativeLibrary when -// you're done. Returns NULL on failure. -// If |error| is not NULL, it may be filled in on load error. -BASE_EXPORT NativeLibrary LoadNativeLibrary(const FilePath& library_path, - NativeLibraryLoadError* error); - -// Loads a native library from disk. Release it with UnloadNativeLibrary when -// you're done. Returns NULL on failure. -// If |error| is not NULL, it may be filled in on load error. -BASE_EXPORT NativeLibrary LoadNativeLibraryWithOptions( - const FilePath& library_path, - const NativeLibraryOptions& options, - NativeLibraryLoadError* error); - -// Unloads a native library. -BASE_EXPORT void UnloadNativeLibrary(NativeLibrary library); - -// Gets a function pointer from a native library. -BASE_EXPORT void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, - StringPiece name); - -// Returns the full platform-specific name for a native library. |name| must be -// ASCII. This is also the default name for the output of a gn |shared_library| -// target. See tools/gn/docs/reference.md#shared_library. -// For example for "mylib", it returns: -// - "mylib.dll" on Windows -// - "libmylib.so" on Linux -// - "libmylib.dylib" on Mac -BASE_EXPORT std::string GetNativeLibraryName(StringPiece name); - -// Returns the full platform-specific name for a gn |loadable_module| target. -// See tools/gn/docs/reference.md#loadable_module -// The returned name is the same as GetNativeLibraryName() on all platforms -// except for Mac where for "mylib" it returns "mylib.so". -BASE_EXPORT std::string GetLoadableModuleName(StringPiece name); - -} // namespace base - -#endif // BASE_NATIVE_LIBRARY_H_ diff --git a/native_library_fuchsia.cc b/native_library_fuchsia.cc deleted file mode 100644 index c50650c98..000000000 --- a/native_library_fuchsia.cc +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/native_library.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "base/base_paths_fuchsia.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/fuchsia/fuchsia_logging.h" -#include "base/logging.h" -#include "base/path_service.h" -#include "base/posix/safe_strerror.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_restrictions.h" - -namespace base { - -std::string NativeLibraryLoadError::ToString() const { - return message; -} - -NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path, - const NativeLibraryOptions& options, - NativeLibraryLoadError* error) { - std::vector components; - library_path.GetComponents(&components); - if (components.size() != 1u) { - NOTREACHED() << "library_path is a path, should be a filename: " - << library_path.MaybeAsASCII(); - return nullptr; - } - - FilePath computed_path = base::GetPackageRoot(); - computed_path = computed_path.AppendASCII("lib").Append(components[0]); - base::File library(computed_path, - base::File::FLAG_OPEN | base::File::FLAG_READ); - if (!library.IsValid()) { - if (error) { - error->message = base::StringPrintf( - "open library: %s", - base::File::ErrorToString(library.error_details()).c_str()); - } - return nullptr; - } - - zx::vmo vmo; - zx_status_t status = fdio_get_vmo_clone(library.GetPlatformFile(), - vmo.reset_and_get_address()); - if (status != ZX_OK) { - if (error) { - error->message = base::StringPrintf("fdio_get_vmo_clone: %s", - zx_status_get_string(status)); - } - return nullptr; - } - NativeLibrary result = dlopen_vmo(vmo.get(), RTLD_LAZY | RTLD_LOCAL); - return result; -} - -void UnloadNativeLibrary(NativeLibrary library) { - // dlclose() is a no-op on Fuchsia, so do nothing here. -} - -void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, - StringPiece name) { - return dlsym(library, name.data()); -} - -std::string GetNativeLibraryName(StringPiece name) { - return base::StringPrintf("lib%s.so", name.as_string().c_str()); -} - -std::string GetLoadableModuleName(StringPiece name) { - return GetNativeLibraryName(name); -} - -} // namespace base diff --git a/native_library_ios.mm b/native_library_ios.mm deleted file mode 100644 index dbcafb41f..000000000 --- a/native_library_ios.mm +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/native_library.h" - -#include "base/logging.h" - -#include "base/strings/string_util.h" - -namespace base { - -std::string NativeLibraryLoadError::ToString() const { - return message; -} - -NativeLibrary LoadNativeLibraryWithOptions(const base::FilePath& library_path, - const NativeLibraryOptions& options, - NativeLibraryLoadError* error) { - NOTIMPLEMENTED(); - if (error) - error->message = "Not implemented."; - return nullptr; -} - -void UnloadNativeLibrary(NativeLibrary library) { - NOTIMPLEMENTED(); - DCHECK(!library); -} - -void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, - StringPiece name) { - NOTIMPLEMENTED(); - return nullptr; -} - -std::string GetNativeLibraryName(StringPiece name) { - DCHECK(IsStringASCII(name)); - return name.as_string(); -} - -std::string GetLoadableModuleName(StringPiece name) { - return GetNativeLibraryName(name); -} - -} // namespace base diff --git a/native_library_mac.mm b/native_library_mac.mm deleted file mode 100644 index 0d31b80a8..000000000 --- a/native_library_mac.mm +++ /dev/null @@ -1,128 +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/native_library.h" - -#include -#include - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/logging.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_restrictions.h" - -namespace base { - -static NativeLibraryObjCStatus GetObjCStatusForImage( - const void* function_pointer) { - Dl_info info; - if (!dladdr(function_pointer, &info)) - return OBJC_UNKNOWN; - - // See if the the image contains an "ObjC image info" segment. This method - // of testing is used in _CFBundleGrokObjcImageInfoFromFile in - // CF-744/CFBundle.c, around lines 2447-2474. - // - // In 64-bit images, ObjC can be recognized in __DATA,__objc_imageinfo. - const section_64* section = getsectbynamefromheader_64( - reinterpret_cast(info.dli_fbase), - SEG_DATA, "__objc_imageinfo"); - return section ? OBJC_PRESENT : OBJC_NOT_PRESENT; -} - -std::string NativeLibraryLoadError::ToString() const { - return message; -} - -NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path, - const NativeLibraryOptions& options, - 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); - if (!dylib) { - if (error) - error->message = dlerror(); - return nullptr; - } - NativeLibrary native_lib = new NativeLibraryStruct(); - native_lib->type = DYNAMIC_LIB; - native_lib->dylib = dylib; - native_lib->objc_status = OBJC_UNKNOWN; - return native_lib; - } - ScopedCFTypeRef url(CFURLCreateFromFileSystemRepresentation( - kCFAllocatorDefault, - (const UInt8*)library_path.value().c_str(), - library_path.value().length(), - true)); - if (!url) - return nullptr; - CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, url.get()); - if (!bundle) - return nullptr; - - NativeLibrary native_lib = new NativeLibraryStruct(); - native_lib->type = BUNDLE; - native_lib->bundle = bundle; - native_lib->bundle_resource_ref = CFBundleOpenBundleResourceMap(bundle); - native_lib->objc_status = OBJC_UNKNOWN; - return native_lib; -} - -void UnloadNativeLibrary(NativeLibrary library) { - if (library->objc_status == OBJC_NOT_PRESENT) { - if (library->type == BUNDLE) { - CFBundleCloseBundleResourceMap(library->bundle, - library->bundle_resource_ref); - CFRelease(library->bundle); - } else { - dlclose(library->dylib); - } - } else { - VLOG(2) << "Not unloading NativeLibrary because it may contain an ObjC " - "segment. library->objc_status = " << library->objc_status; - // Deliberately do not CFRelease the bundle or dlclose the dylib because - // doing so can corrupt the ObjC runtime method caches. See - // http://crbug.com/172319 for details. - } - delete library; -} - -void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, - StringPiece name) { - void* function_pointer = nullptr; - - // Get the function pointer using the right API for the type. - if (library->type == BUNDLE) { - ScopedCFTypeRef symbol_name(CFStringCreateWithCString( - kCFAllocatorDefault, name.data(), kCFStringEncodingUTF8)); - function_pointer = CFBundleGetFunctionPointerForName(library->bundle, - symbol_name); - } else { - function_pointer = dlsym(library->dylib, name.data()); - } - - // If this library hasn't been tested for having ObjC, use the function - // pointer to look up the section information for the library. - if (function_pointer && library->objc_status == OBJC_UNKNOWN) - library->objc_status = GetObjCStatusForImage(function_pointer); - - return function_pointer; -} - -std::string GetNativeLibraryName(StringPiece name) { - DCHECK(IsStringASCII(name)); - return "lib" + name.as_string() + ".dylib"; -} - -std::string GetLoadableModuleName(StringPiece name) { - DCHECK(IsStringASCII(name)); - return name.as_string() + ".so"; -} - -} // namespace base diff --git a/native_library_posix.cc b/native_library_posix.cc deleted file mode 100644 index 19ff7a4b0..000000000 --- a/native_library_posix.cc +++ /dev/null @@ -1,70 +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/native_library.h" - -#include - -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_restrictions.h" - -namespace base { - -std::string NativeLibraryLoadError::ToString() const { - return message; -} - -NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path, - const NativeLibraryOptions& options, - NativeLibraryLoadError* error) { - // dlopen() opens the file off disk. - AssertBlockingAllowed(); - - // We deliberately do not use RTLD_DEEPBIND by default. For the history why, - // please refer to the bug tracker. Some useful bug reports to read include: - // http://crbug.com/17943, http://crbug.com/17557, http://crbug.com/36892, - // and http://crbug.com/40794. - int flags = RTLD_LAZY; -#if defined(OS_ANDROID) || !defined(RTLD_DEEPBIND) - // Certain platforms don't define RTLD_DEEPBIND. Android dlopen() requires - // further investigation, as it might vary across versions. Crash here to - // warn developers that they're trying to rely on uncertain behavior. - CHECK(!options.prefer_own_symbols); -#else - if (options.prefer_own_symbols) - flags |= RTLD_DEEPBIND; -#endif - void* dl = dlopen(library_path.value().c_str(), flags); - if (!dl && error) - error->message = dlerror(); - - return dl; -} - -void UnloadNativeLibrary(NativeLibrary library) { - int ret = dlclose(library); - if (ret < 0) { - DLOG(ERROR) << "dlclose failed: " << dlerror(); - NOTREACHED(); - } -} - -void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, - StringPiece name) { - return dlsym(library, name.data()); -} - -std::string GetNativeLibraryName(StringPiece name) { - DCHECK(IsStringASCII(name)); - return "lib" + name.as_string() + ".so"; -} - -std::string GetLoadableModuleName(StringPiece name) { - return GetNativeLibraryName(name); -} - -} // namespace base diff --git a/native_library_unittest.cc b/native_library_unittest.cc deleted file mode 100644 index 2bfb9ec94..000000000 --- a/native_library_unittest.cc +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/native_library.h" -#include "base/path_service.h" -#include "base/test/native_library_test_utils.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -const FilePath::CharType kDummyLibraryPath[] = - FILE_PATH_LITERAL("dummy_library"); - -TEST(NativeLibraryTest, LoadFailure) { - NativeLibraryLoadError error; - EXPECT_FALSE(LoadNativeLibrary(FilePath(kDummyLibraryPath), &error)); - EXPECT_FALSE(error.ToString().empty()); -} - -// |error| is optional and can be null. -TEST(NativeLibraryTest, LoadFailureWithNullError) { - EXPECT_FALSE(LoadNativeLibrary(FilePath(kDummyLibraryPath), nullptr)); -} - -TEST(NativeLibraryTest, GetNativeLibraryName) { - const char kExpectedName[] = -#if defined(OS_WIN) - "mylib.dll"; -#elif defined(OS_IOS) - "mylib"; -#elif defined(OS_MACOSX) - "libmylib.dylib"; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - "libmylib.so"; -#endif - EXPECT_EQ(kExpectedName, GetNativeLibraryName("mylib")); -} - -TEST(NativeLibraryTest, GetLoadableModuleName) { - const char kExpectedName[] = -#if defined(OS_WIN) - "mylib.dll"; -#elif defined(OS_IOS) - "mylib"; -#elif defined(OS_MACOSX) - "mylib.so"; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - "libmylib.so"; -#endif - EXPECT_EQ(kExpectedName, GetLoadableModuleName("mylib")); -} - -// We don't support dynamic loading on iOS, and ASAN will complain about our -// intentional ODR violation because of |g_native_library_exported_value| being -// defined globally both here and in the shared library. -#if !defined(OS_IOS) && !defined(ADDRESS_SANITIZER) - -const char kTestLibraryName[] = -#if defined(OS_WIN) - "test_shared_library.dll"; -#elif defined(OS_MACOSX) - "libtest_shared_library.dylib"; -#elif defined(OS_ANDROID) && defined(COMPONENT_BUILD) - "libtest_shared_library.cr.so"; -#elif defined(OS_POSIX) || defined(OS_FUCHSIA) - "libtest_shared_library.so"; -#endif - -class TestLibrary { - public: - TestLibrary() : TestLibrary(NativeLibraryOptions()) {} - - explicit TestLibrary(const NativeLibraryOptions& options) - : library_(nullptr) { - base::FilePath exe_path; - -#if !defined(OS_FUCHSIA) - // Libraries do not sit alongside the executable in Fuchsia. NativeLibrary - // is aware of this and is able to resolve library paths correctly. - CHECK(base::PathService::Get(base::DIR_EXE, &exe_path)); -#endif - - library_ = LoadNativeLibraryWithOptions( - exe_path.AppendASCII(kTestLibraryName), options, nullptr); - CHECK(library_); - } - - ~TestLibrary() { - UnloadNativeLibrary(library_); - } - - template - ReturnType Call(const char* function_name, Args... args) { - return reinterpret_cast( - GetFunctionPointerFromNativeLibrary(library_, function_name))(args...); - } - - private: - NativeLibrary library_; - - DISALLOW_COPY_AND_ASSIGN(TestLibrary); -}; - -// NativeLibraaryTest.LoadLibrary is failing on M tablets only. -// crbug/641309 -#if !defined(OS_ANDROID) - -// Verifies that we can load a native library and resolve its exported symbols. -TEST(NativeLibraryTest, LoadLibrary) { - TestLibrary library; - EXPECT_EQ(5, library.Call("GetSimpleTestValue")); -} - -#endif // !defined(OS_ANDROID) - -// Android dlopen() requires further investigation, as it might vary across -// versions with respect to symbol resolution scope. -// TSan and MSan error out on RTLD_DEEPBIND, https://crbug.com/705255 -#if !defined(OS_ANDROID) && !defined(THREAD_SANITIZER) && \ - !defined(MEMORY_SANITIZER) - -// Verifies that the |prefer_own_symbols| option satisfies its guarantee that -// a loaded library will always prefer local symbol resolution before -// considering global symbols. -TEST(NativeLibraryTest, LoadLibraryPreferOwnSymbols) { - NativeLibraryOptions options; - options.prefer_own_symbols = true; - TestLibrary library(options); - - // Verify that this binary and the DSO use different storage for - // |g_native_library_exported_value|. - g_native_library_exported_value = 1; - library.Call("SetExportedValue", 2); - EXPECT_EQ(1, g_native_library_exported_value); - g_native_library_exported_value = 3; - EXPECT_EQ(2, library.Call("GetExportedValue")); - - // Both this binary and the library link against the - // native_library_test_utils source library, which in turn exports the - // NativeLibraryTestIncrement() function whose return value depends on some - // static internal state. - // - // The DSO's GetIncrementValue() forwards to that function inside the DSO. - // - // Here we verify that direct calls to NativeLibraryTestIncrement() in this - // binary return a sequence of values independent from the sequence returned - // by GetIncrementValue(), ensuring that the DSO is calling its own local - // definition of NativeLibraryTestIncrement(). - EXPECT_EQ(1, library.Call("GetIncrementValue")); - EXPECT_EQ(1, NativeLibraryTestIncrement()); - EXPECT_EQ(2, library.Call("GetIncrementValue")); - EXPECT_EQ(3, library.Call("GetIncrementValue")); - EXPECT_EQ(4, library.Call("NativeLibraryTestIncrement")); - EXPECT_EQ(2, NativeLibraryTestIncrement()); - EXPECT_EQ(3, NativeLibraryTestIncrement()); -} - -#endif // !defined(OS_ANDROID) - -#endif // !defined(OS_IOS) && !defined(ADDRESS_SANITIZER) - -} // namespace base diff --git a/native_library_win.cc b/native_library_win.cc deleted file mode 100644 index ca9446816..000000000 --- a/native_library_win.cc +++ /dev/null @@ -1,177 +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/native_library.h" - -#include - -#include "base/files/file_util.h" -#include "base/metrics/histogram_macros.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_restrictions.h" - -namespace base { - -using AddDllDirectory = HMODULE (*)(PCWSTR new_directory); - -namespace { -// This enum is used to back an UMA histogram, and should therefore be treated -// as append-only. -enum LoadLibraryResult { - // LoadLibraryExW API/flags are available and the call succeeds. - SUCCEED = 0, - // LoadLibraryExW API/flags are availabe to use but the call fails, then - // LoadLibraryW is used and succeeds. - FAIL_AND_SUCCEED, - // LoadLibraryExW API/flags are availabe to use but the call fails, then - // LoadLibraryW is used but fails as well. - FAIL_AND_FAIL, - // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used - // and succeeds. - UNAVAILABLE_AND_SUCCEED, - // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used - // but fails. - UNAVAILABLE_AND_FAIL, - // Add new items before this one, always keep this one at the end. - END -}; - -// A helper method to log library loading result to UMA. -void LogLibrarayLoadResultToUMA(LoadLibraryResult result) { - UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result, - LoadLibraryResult::END); -} - -// A helper method to check if AddDllDirectory method is available, thus -// LOAD_LIBRARY_SEARCH_* flags are available on systems. -bool AreSearchFlagsAvailable() { - // The LOAD_LIBRARY_SEARCH_* flags are available on systems that have - // KB2533623 installed. To determine whether the flags are available, use - // GetProcAddress to get the address of the AddDllDirectory, - // RemoveDllDirectory, or SetDefaultDllDirectories function. If GetProcAddress - // succeeds, the LOAD_LIBRARY_SEARCH_* flags can be used with LoadLibraryEx. - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx - // The LOAD_LIBRARY_SEARCH_* flags are used in the LoadNativeLibraryHelper - // method. - auto add_dll_dir_func = reinterpret_cast( - GetProcAddress(GetModuleHandle(L"kernel32.dll"), "AddDllDirectory")); - return !!add_dll_dir_func; -} - -// A helper method to encode the library loading result to enum -// LoadLibraryResult. -LoadLibraryResult GetLoadLibraryResult(bool are_search_flags_available, - bool has_load_library_succeeded) { - LoadLibraryResult result; - if (are_search_flags_available) { - if (has_load_library_succeeded) - result = LoadLibraryResult::FAIL_AND_SUCCEED; - else - result = LoadLibraryResult::FAIL_AND_FAIL; - } else if (has_load_library_succeeded) { - result = LoadLibraryResult::UNAVAILABLE_AND_SUCCEED; - } else { - result = LoadLibraryResult::UNAVAILABLE_AND_FAIL; - } - return result; -} - -NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path, - NativeLibraryLoadError* error) { - // LoadLibrary() opens the file off disk. - AssertBlockingAllowed(); - - HMODULE module = nullptr; - - // This variable records the library loading result. - LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED; - - bool are_search_flags_available = AreSearchFlagsAvailable(); - if (are_search_flags_available) { - // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library - // directory as the library may have dependencies on DLLs in this - // directory. - module = ::LoadLibraryExW( - library_path.value().c_str(), nullptr, - LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); - // If LoadLibraryExW succeeds, log this metric and return. - if (module) { - LogLibrarayLoadResultToUMA(load_library_result); - return module; - } - // GetLastError() needs to be called immediately after - // LoadLibraryExW call. - if (error) - error->code = GetLastError(); - } - - // If LoadLibraryExW API/flags are unavailable or API call fails, try - // LoadLibraryW API. - // TODO(chengx): Currently, if LoadLibraryExW API call fails, LoadLibraryW is - // still tried. We should strictly prefer the LoadLibraryExW over the - // LoadLibraryW if LoadLibraryW is statistically showing no extra benefits. If - // UMA metric shows that FAIL_AND_FAIL is the primary failure mode and/or - // FAIL_AND_SUCCESS is close to zero, we should remove this fallback. - // (http://crbug.com/701944) - - // 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 (GetCurrentDirectory(¤t_directory)) { - FilePath plugin_path = library_path.DirName(); - if (!plugin_path.empty()) { - SetCurrentDirectory(plugin_path); - restore_directory = true; - } - } - - module = ::LoadLibraryW(library_path.value().c_str()); - - // GetLastError() needs to be called immediately after LoadLibraryW call. - if (!module && error) - error->code = GetLastError(); - - if (restore_directory) - SetCurrentDirectory(current_directory); - - // Get the library loading result and log it to UMA. - LogLibrarayLoadResultToUMA( - GetLoadLibraryResult(are_search_flags_available, !!module)); - - return module; -} -} // namespace - -std::string NativeLibraryLoadError::ToString() const { - return StringPrintf("%lu", code); -} - -NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path, - const NativeLibraryOptions& options, - NativeLibraryLoadError* error) { - return LoadNativeLibraryHelper(library_path, error); -} - -void UnloadNativeLibrary(NativeLibrary library) { - FreeLibrary(library); -} - -void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, - StringPiece name) { - return reinterpret_cast(GetProcAddress(library, name.data())); -} - -std::string GetNativeLibraryName(StringPiece name) { - DCHECK(IsStringASCII(name)); - return name.as_string() + ".dll"; -} - -std::string GetLoadableModuleName(StringPiece name) { - return GetNativeLibraryName(name); -} - -} // namespace base diff --git a/nix/OWNERS b/nix/OWNERS deleted file mode 100644 index 280ba478d..000000000 --- a/nix/OWNERS +++ /dev/null @@ -1 +0,0 @@ -thomasanderson@chromium.org diff --git a/nix/mime_util_xdg.cc b/nix/mime_util_xdg.cc deleted file mode 100644 index 6b5b11d63..000000000 --- a/nix/mime_util_xdg.cc +++ /dev/null @@ -1,33 +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/nix/mime_util_xdg.h" - -#include "base/files/file_path.h" -#include "base/lazy_instance.h" -#include "base/synchronization/lock.h" -#include "base/third_party/xdg_mime/xdgmime.h" -#include "base/threading/thread_restrictions.h" - -namespace base { -namespace nix { - -namespace { - -// None of the XDG stuff is thread-safe, so serialize all access under -// this lock. -LazyInstance::Leaky g_mime_util_xdg_lock = LAZY_INSTANCE_INITIALIZER; - -} // namespace - -std::string GetFileMimeType(const FilePath& filepath) { - if (filepath.empty()) - return std::string(); - AssertBlockingAllowed(); - AutoLock scoped_lock(g_mime_util_xdg_lock.Get()); - return xdg_mime_get_mime_type_from_file_name(filepath.value().c_str()); -} - -} // namespace nix -} // namespace base diff --git a/nix/mime_util_xdg.h b/nix/mime_util_xdg.h deleted file mode 100644 index e0f264a56..000000000 --- a/nix/mime_util_xdg.h +++ /dev/null @@ -1,36 +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_NIX_MIME_UTIL_XDG_H_ -#define BASE_NIX_MIME_UTIL_XDG_H_ - -#include - -#include "base/base_export.h" -#include "build/build_config.h" - -namespace base { - -class FilePath; - -namespace nix { - -// Gets the mime type for a file at |filepath|. -// -// The mime type is calculated based only on the file name of |filepath|. In -// particular |filepath| will not be touched on disk and |filepath| doesn't even -// have to exist. This means that the function does not work for directories -// (i.e. |filepath| is assumed to be a path to a file). -// -// Note that this function might need to read from disk the mime-types data -// provided by the OS. Therefore this function should not be called from -// threads that disallow IO via base::ThreadRestrictions::SetIOAllowed(false). -// -// If the mime type is unknown, this will return application/octet-stream. -BASE_EXPORT std::string GetFileMimeType(const FilePath& filepath); - -} // namespace nix -} // namespace base - -#endif // BASE_NIX_MIME_UTIL_XDG_H_ diff --git a/nix/xdg_util.cc b/nix/xdg_util.cc deleted file mode 100644 index 109624a1e..000000000 --- a/nix/xdg_util.cc +++ /dev/null @@ -1,150 +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/nix/xdg_util.h" - -#include - -#include "base/base_paths.h" -#include "base/environment.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/path_service.h" -#include "base/strings/string_util.h" -#include "base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h" - -namespace { - -// The KDE session version environment variable introduced in KDE 4. -const char kKDESessionEnvVar[] = "KDE_SESSION_VERSION"; - -} // namespace - -namespace base { -namespace nix { - -const char kDotConfigDir[] = ".config"; -const char kXdgConfigHomeEnvVar[] = "XDG_CONFIG_HOME"; - -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()) { - path = FilePath(env_value); - } else { - PathService::Get(DIR_HOME, &path); - path = path.Append(fallback_dir); - } - return path.StripTrailingSeparators(); -} - -FilePath GetXDGUserDirectory(const char* dir_name, const char* fallback_dir) { - FilePath path; - char* xdg_dir = xdg_user_dir_lookup(dir_name); - if (xdg_dir) { - path = FilePath(xdg_dir); - free(xdg_dir); - } else { - PathService::Get(DIR_HOME, &path); - path = path.Append(fallback_dir); - } - return path.StripTrailingSeparators(); -} - -DesktopEnvironment GetDesktopEnvironment(Environment* env) { - // XDG_CURRENT_DESKTOP is the newest standard circa 2012. - std::string xdg_current_desktop; - if (env->GetVar("XDG_CURRENT_DESKTOP", &xdg_current_desktop)) { - // Not all desktop environments set this env var as of this writing. - if (base::StartsWith(xdg_current_desktop, "Unity", - base::CompareCase::SENSITIVE)) { - // gnome-fallback sessions set XDG_CURRENT_DESKTOP to Unity - // DESKTOP_SESSION can be gnome-fallback or gnome-fallback-compiz - std::string desktop_session; - if (env->GetVar("DESKTOP_SESSION", &desktop_session) && - desktop_session.find("gnome-fallback") != std::string::npos) { - return DESKTOP_ENVIRONMENT_GNOME; - } - return DESKTOP_ENVIRONMENT_UNITY; - } - if (xdg_current_desktop == "GNOME") - return DESKTOP_ENVIRONMENT_GNOME; - if (xdg_current_desktop == "X-Cinnamon") - return DESKTOP_ENVIRONMENT_CINNAMON; - if (xdg_current_desktop == "KDE") { - std::string kde_session; - if (env->GetVar(kKDESessionEnvVar, &kde_session)) { - if (kde_session == "5") { - return DESKTOP_ENVIRONMENT_KDE5; - } - } - return DESKTOP_ENVIRONMENT_KDE4; - } - if (xdg_current_desktop == "Pantheon") - return DESKTOP_ENVIRONMENT_PANTHEON; - } - - // DESKTOP_SESSION was what everyone used in 2010. - std::string desktop_session; - if (env->GetVar("DESKTOP_SESSION", &desktop_session)) { - if (desktop_session == "gnome" || desktop_session == "mate") - return DESKTOP_ENVIRONMENT_GNOME; - if (desktop_session == "kde4" || desktop_session == "kde-plasma") - return DESKTOP_ENVIRONMENT_KDE4; - if (desktop_session == "kde") { - // This may mean KDE4 on newer systems, so we have to check. - if (env->HasVar(kKDESessionEnvVar)) - return DESKTOP_ENVIRONMENT_KDE4; - return DESKTOP_ENVIRONMENT_KDE3; - } - if (desktop_session.find("xfce") != std::string::npos || - desktop_session == "xubuntu") { - return DESKTOP_ENVIRONMENT_XFCE; - } - } - - // Fall back on some older environment variables. - // Useful particularly in the DESKTOP_SESSION=default case. - if (env->HasVar("GNOME_DESKTOP_SESSION_ID")) - return DESKTOP_ENVIRONMENT_GNOME; - if (env->HasVar("KDE_FULL_SESSION")) { - if (env->HasVar(kKDESessionEnvVar)) - return DESKTOP_ENVIRONMENT_KDE4; - return DESKTOP_ENVIRONMENT_KDE3; - } - - return DESKTOP_ENVIRONMENT_OTHER; -} - -const char* GetDesktopEnvironmentName(DesktopEnvironment env) { - switch (env) { - case DESKTOP_ENVIRONMENT_OTHER: - return nullptr; - case DESKTOP_ENVIRONMENT_CINNAMON: - return "CINNAMON"; - case DESKTOP_ENVIRONMENT_GNOME: - return "GNOME"; - case DESKTOP_ENVIRONMENT_KDE3: - return "KDE3"; - case DESKTOP_ENVIRONMENT_KDE4: - return "KDE4"; - case DESKTOP_ENVIRONMENT_KDE5: - return "KDE5"; - case DESKTOP_ENVIRONMENT_PANTHEON: - return "PANTHEON"; - case DESKTOP_ENVIRONMENT_UNITY: - return "UNITY"; - case DESKTOP_ENVIRONMENT_XFCE: - return "XFCE"; - } - return nullptr; -} - -const char* GetDesktopEnvironmentName(Environment* env) { - return GetDesktopEnvironmentName(GetDesktopEnvironment(env)); -} - -} // namespace nix -} // namespace base diff --git a/nix/xdg_util.h b/nix/xdg_util.h deleted file mode 100644 index 65f7d1570..000000000 --- a/nix/xdg_util.h +++ /dev/null @@ -1,77 +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_NIX_XDG_UTIL_H_ -#define BASE_NIX_XDG_UTIL_H_ - -// XDG refers to http://en.wikipedia.org/wiki/Freedesktop.org . -// This file contains utilities found across free desktop environments. -// -// TODO(brettw) this file should be in app/x11, but is currently used by -// net. We should have a net API to allow the embedder to specify the behavior -// that it uses XDG for, and then move this file. - -#include "base/base_export.h" - -#ifdef nix -#error asdf -#endif - -namespace base { - -class Environment; -class FilePath; - -namespace nix { - -// The default XDG config directory name. -BASE_EXPORT extern const char kDotConfigDir[]; - -// The XDG config directory environment variable. -BASE_EXPORT extern const char kXdgConfigHomeEnvVar[]; - -// Utility function for getting XDG directories. -// |env_name| is the name of an environment variable that we want to use to get -// a directory path. |fallback_dir| is the directory relative to $HOME that we -// use if |env_name| cannot be found or is empty. |fallback_dir| may be NULL. -// Examples of |env_name| are XDG_CONFIG_HOME and XDG_DATA_HOME. -BASE_EXPORT FilePath GetXDGDirectory(Environment* env, const char* env_name, - const char* fallback_dir); - -// Wrapper around xdg_user_dir_lookup() from src/base/third_party/xdg-user-dirs -// This looks up "well known" user directories like the desktop and music -// folder. Examples of |dir_name| are DESKTOP and MUSIC. -BASE_EXPORT FilePath GetXDGUserDirectory(const char* dir_name, - const char* fallback_dir); - -enum DesktopEnvironment { - DESKTOP_ENVIRONMENT_OTHER, - DESKTOP_ENVIRONMENT_CINNAMON, - DESKTOP_ENVIRONMENT_GNOME, - // KDE3, KDE4 and KDE5 are sufficiently different that we count - // them as different desktop environments here. - DESKTOP_ENVIRONMENT_KDE3, - DESKTOP_ENVIRONMENT_KDE4, - DESKTOP_ENVIRONMENT_KDE5, - DESKTOP_ENVIRONMENT_PANTHEON, - DESKTOP_ENVIRONMENT_UNITY, - DESKTOP_ENVIRONMENT_XFCE, -}; - -// Return an entry from the DesktopEnvironment enum with a best guess -// of which desktop environment we're using. We use this to know when -// to attempt to use preferences from the desktop environment -- -// proxy settings, password manager, etc. -BASE_EXPORT DesktopEnvironment GetDesktopEnvironment(Environment* env); - -// Return a string representation of the given desktop environment. -// May return NULL in the case of DESKTOP_ENVIRONMENT_OTHER. -BASE_EXPORT const char* GetDesktopEnvironmentName(DesktopEnvironment env); -// Convenience wrapper that calls GetDesktopEnvironment() first. -BASE_EXPORT const char* GetDesktopEnvironmentName(Environment* env); - -} // namespace nix -} // namespace base - -#endif // BASE_NIX_XDG_UTIL_H_ diff --git a/nix/xdg_util_unittest.cc b/nix/xdg_util_unittest.cc deleted file mode 100644 index e195303ca..000000000 --- a/nix/xdg_util_unittest.cc +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/nix/xdg_util.h" - -#include "base/environment.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::Eq; -using ::testing::Return; -using ::testing::SetArgPointee; - -namespace base { -namespace nix { - -namespace { - -class MockEnvironment : public Environment { - public: - MOCK_METHOD2(GetVar, bool(StringPiece, std::string* result)); - MOCK_METHOD2(SetVar, bool(StringPiece, const std::string& new_value)); - MOCK_METHOD1(UnSetVar, bool(StringPiece)); -}; - -// Needs to be const char* to make gmock happy. -const char* const kDesktopGnome = "gnome"; -const char* const kDesktopGnomeFallback = "gnome-fallback"; -const char* const kDesktopMATE = "mate"; -const char* const kDesktopKDE4 = "kde4"; -const char* const kDesktopKDE = "kde"; -const char* const kDesktopXFCE = "xfce"; -const char* const kXdgDesktopCinnamon = "X-Cinnamon"; -const char* const kXdgDesktopGNOME = "GNOME"; -const char* const kXdgDesktopKDE = "KDE"; -const char* const kXdgDesktopPantheon = "Pantheon"; -const char* const kXdgDesktopUnity = "Unity"; -const char* const kXdgDesktopUnity7 = "Unity:Unity7"; -const char* const kXdgDesktopUnity8 = "Unity:Unity8"; -const char* const kKDESessionKDE5 = "5"; - -const char kDesktopSession[] = "DESKTOP_SESSION"; -const char kKDESession[] = "KDE_SESSION_VERSION"; -const char kXdgDesktop[] = "XDG_CURRENT_DESKTOP"; - -} // namespace - -TEST(XDGUtilTest, GetDesktopEnvironmentGnome) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _)) - .WillOnce(DoAll(SetArgPointee<1>(kDesktopGnome), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetDesktopEnvironmentMATE) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _)) - .WillOnce(DoAll(SetArgPointee<1>(kDesktopMATE), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetDesktopEnvironmentKDE4) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _)) - .WillOnce(DoAll(SetArgPointee<1>(kDesktopKDE4), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE4, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetDesktopEnvironmentKDE3) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _)) - .WillOnce(DoAll(SetArgPointee<1>(kDesktopKDE), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE3, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetDesktopEnvironmentXFCE) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _)) - .WillOnce(DoAll(SetArgPointee<1>(kDesktopXFCE), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_XFCE, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetXdgDesktopCinnamon) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _)) - .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopCinnamon), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_CINNAMON, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetXdgDesktopGnome) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _)) - .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopGNOME), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetXdgDesktopGnomeFallback) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _)) - .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopUnity), Return(true))); - EXPECT_CALL(getter, GetVar(Eq(kDesktopSession), _)) - .WillOnce(DoAll(SetArgPointee<1>(kDesktopGnomeFallback), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_GNOME, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetXdgDesktopKDE5) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _)) - .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopKDE), Return(true))); - EXPECT_CALL(getter, GetVar(Eq(kKDESession), _)) - .WillOnce(DoAll(SetArgPointee<1>(kKDESessionKDE5), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE5, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetXdgDesktopKDE4) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _)) - .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopKDE), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_KDE4, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetXdgDesktopPantheon) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _)) - .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopPantheon), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_PANTHEON, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetXdgDesktopUnity) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _)) - .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopUnity), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_UNITY, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetXdgDesktopUnity7) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _)) - .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopUnity7), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_UNITY, GetDesktopEnvironment(&getter)); -} - -TEST(XDGUtilTest, GetXdgDesktopUnity8) { - MockEnvironment getter; - EXPECT_CALL(getter, GetVar(_, _)).WillRepeatedly(Return(false)); - EXPECT_CALL(getter, GetVar(Eq(kXdgDesktop), _)) - .WillOnce(DoAll(SetArgPointee<1>(kXdgDesktopUnity8), Return(true))); - - EXPECT_EQ(DESKTOP_ENVIRONMENT_UNITY, GetDesktopEnvironment(&getter)); -} - -} // namespace nix -} // namespace base diff --git a/no_destructor.h b/no_destructor.h deleted file mode 100644 index aabc6e6a7..000000000 --- a/no_destructor.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2018 The Chromium 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_NO_DESTRUCTOR_H_ -#define BASE_NO_DESTRUCTOR_H_ - -#include -#include - -namespace base { - -// A wrapper that makes it easy to create an object of type T with static -// storage duration that: -// - is only constructed on first access -// - never invokes the destructor -// in order to satisfy the styleguide ban on global constructors and -// destructors. -// -// Runtime constant example: -// const std::string& GetLineSeparator() { -// // Forwards to std::string(size_t, char, const Allocator&) constructor. -// static const base::NoDestructor s(5, '-'); -// return *s; -// } -// -// More complex initialization with a lambda: -// const std::string& GetSessionNonce() { -// static const base::NoDestructor nonce([] { -// std::string s(16); -// crypto::RandString(s.data(), s.size()); -// return s; -// }()); -// return *nonce; -// } -// -// NoDestructor stores the object inline, so it also avoids a pointer -// indirection and a malloc. Also note that since C++11 static local variable -// initialization is thread-safe and so is this pattern. Code should prefer to -// use NoDestructor over: -// - The CR_DEFINE_STATIC_LOCAL() helper macro. -// - A function scoped static T* or T& that is dynamically initialized. -// - A global base::LazyInstance. -// -// Note that since the destructor is never run, this *will* leak memory if used -// as a stack or member variable. Furthermore, a NoDestructor should never -// have global scope as that may require a static initializer. -template -class NoDestructor { - public: - // Not constexpr; just write static constexpr T x = ...; if the value should - // be a constexpr. - template - explicit NoDestructor(Args&&... args) { - new (storage_) T(std::forward(args)...); - } - - // Allows copy and move construction of the contained type, to allow - // construction from an initializer list, e.g. for std::vector. - explicit NoDestructor(const T& x) { new (storage_) T(x); } - explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); } - - NoDestructor(const NoDestructor&) = delete; - NoDestructor& operator=(const NoDestructor&) = delete; - - ~NoDestructor() = default; - - const T& operator*() const { return *get(); } - T& operator*() { return *get(); } - - const T* operator->() const { return get(); } - T* operator->() { return get(); } - - const T* get() const { return reinterpret_cast(storage_); } - T* get() { return reinterpret_cast(storage_); } - - private: - alignas(T) char storage_[sizeof(T)]; - -#if defined(LEAK_SANITIZER) - // TODO(https://crbug.com/812277): This is a hack to work around the fact - // that LSan doesn't seem to treat NoDestructor as a root for reachability - // analysis. This means that code like this: - // static base::NoDestructor> v({1, 2, 3}); - // is considered a leak. Using the standard leak sanitizer annotations to - // suppress leaks doesn't work: std::vector is implicitly constructed before - // calling the base::NoDestructor constructor. - // - // Unfortunately, I haven't been able to demonstrate this issue in simpler - // reproductions: until that's resolved, hold an explicit pointer to the - // placement-new'd object in leak sanitizer mode to help LSan realize that - // objects allocated by the contained type are still reachable. - T* storage_ptr_ = reinterpret_cast(storage_); -#endif // defined(LEAK_SANITIZER) -}; - -} // namespace base - -#endif // BASE_NO_DESTRUCTOR_H_ diff --git a/no_destructor_unittest.cc b/no_destructor_unittest.cc deleted file mode 100644 index 8f9d4a48c..000000000 --- a/no_destructor_unittest.cc +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2018 The Chromium 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/no_destructor.h" - -#include -#include - -#include "base/logging.h" -#include "build/build_config.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -struct CheckOnDestroy { - ~CheckOnDestroy() { CHECK(false); } -}; - -TEST(NoDestructorTest, SkipsDestructors) { - NoDestructor destructor_should_not_run; -} - -struct CopyOnly { - CopyOnly() = default; - - CopyOnly(const CopyOnly&) = default; - CopyOnly& operator=(const CopyOnly&) = default; - - CopyOnly(CopyOnly&&) = delete; - CopyOnly& operator=(CopyOnly&&) = delete; -}; - -struct MoveOnly { - MoveOnly() = default; - - MoveOnly(const MoveOnly&) = delete; - MoveOnly& operator=(const MoveOnly&) = delete; - - MoveOnly(MoveOnly&&) = default; - MoveOnly& operator=(MoveOnly&&) = default; -}; - -struct ForwardingTestStruct { - ForwardingTestStruct(const CopyOnly&, MoveOnly&&) {} -}; - -TEST(NoDestructorTest, ForwardsArguments) { - CopyOnly copy_only; - MoveOnly move_only; - - static NoDestructor test_forwarding( - copy_only, std::move(move_only)); -} - -TEST(NoDestructorTest, Accessors) { - static NoDestructor awesome("awesome"); - - EXPECT_EQ("awesome", *awesome); - EXPECT_EQ(0, awesome->compare("awesome")); - EXPECT_EQ(0, awesome.get()->compare("awesome")); -} - -// Passing initializer list to a NoDestructor like in this test -// is ambiguous in GCC. -// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84849 -#if !defined(COMPILER_GCC) && !defined(__clang__) -TEST(NoDestructorTest, InitializerList) { - static NoDestructor> vector({"a", "b", "c"}); -} -#endif -} // namespace - -} // namespace base diff --git a/numerics/BUILD.gn b/numerics/BUILD.gn deleted file mode 100644 index 0bb8dd103..000000000 --- a/numerics/BUILD.gn +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# This is a dependency-free, header-only, library, and it needs to stay that -# way to facilitate pulling it into various third-party projects. So, this -# file is here to protect against accidentally introducing external -# dependencies or depending on internal implementation details. -source_set("base_numerics") { - visibility = [ "//base/*" ] - sources = [ - "checked_math_impl.h", - "clamped_math_impl.h", - "safe_conversions_arm_impl.h", - "safe_conversions_impl.h", - "safe_math_arm_impl.h", - "safe_math_clang_gcc_impl.h", - "safe_math_shared_impl.h", - ] - public = [ - "checked_math.h", - "clamped_math.h", - "math_constants.h", - "ranges.h", - "safe_conversions.h", - "safe_math.h", - ] -} diff --git a/numerics/DEPS b/numerics/DEPS deleted file mode 100644 index d95bf133f..000000000 --- a/numerics/DEPS +++ /dev/null @@ -1,7 +0,0 @@ -# This is a dependency-free, header-only, library, and it needs to stay that -# way to facilitate pulling it into various third-party projects. So, this -# file is here to protect against accidentally introducing dependencies. -include_rules = [ - "-base", - "+base/numerics", -] diff --git a/numerics/OWNERS b/numerics/OWNERS deleted file mode 100644 index 5493fba47..000000000 --- a/numerics/OWNERS +++ /dev/null @@ -1,5 +0,0 @@ -jschuh@chromium.org -tsepez@chromium.org - - -# COMPONENT: Internals diff --git a/numerics/README.md b/numerics/README.md deleted file mode 100644 index 896b12421..000000000 --- a/numerics/README.md +++ /dev/null @@ -1,409 +0,0 @@ -# `base/numerics` - -This directory contains a dependency-free, header-only library of templates -providing well-defined semantics for safely and performantly handling a variety -of numeric operations, including most common arithmetic operations and -conversions. - -The public API is broken out into the following header files: - -* `checked_math.h` contains the `CheckedNumeric` template class and helper - functions for performing arithmetic and conversion operations that detect - errors and boundary conditions (e.g. overflow, truncation, etc.). -* `clamped_math.h` contains the `ClampedNumeric` template class and - helper functions for performing fast, clamped (i.e. non-sticky saturating) - arithmetic operations and conversions. -* `safe_conversions.h` contains the `StrictNumeric` template class and - a collection of custom casting templates and helper functions for safely - converting between a range of numeric types. -* `safe_math.h` includes all of the previously mentioned headers. - -*** aside -**Note:** The `Numeric` template types implicitly convert from C numeric types -and `Numeric` templates that are convertable to an underlying C numeric type. -The conversion priority for `Numeric` type coercions is: - -* `StrictNumeric` coerces to `ClampedNumeric` and `CheckedNumeric` -* `ClampedNumeric` coerces to `CheckedNumeric` -*** - -[TOC] - -## Common patterns and use-cases - -The following covers the preferred style for the most common uses of this -library. Please don't cargo-cult from anywhere else. 😉 - -### Performing checked arithmetic conversions - -The `checked_cast` template converts between arbitrary arithmetic types, and is -used for cases where a conversion failure should result in program termination: - -```cpp -// Crash if signed_value is out of range for buff_size. -size_t buff_size = checked_cast(signed_value); -``` - -### Performing saturated (clamped) arithmetic conversions - -The `saturated_cast` template converts between arbitrary arithmetic types, and -is used in cases where an out-of-bounds source value should be saturated to the -corresponding maximum or minimum of the destination type: - -```cpp -// Convert from float with saturation to INT_MAX, INT_MIN, or 0 for NaN. -int int_value = saturated_cast(floating_point_value); -``` - -### Enforcing arithmetic conversions at compile-time - -The `strict_cast` enforces type restrictions at compile-time and results in -emitted code that is identical to a normal `static_cast`. However, a -`strict_cast` assignment will fail to compile if the destination type cannot -represent the full range of the source type: - -```cpp -// Throw a compiler error if byte_value is changed to an out-of-range-type. -int int_value = strict_cast(byte_value); -``` - -You can also enforce these compile-time restrictions on function parameters by -using the `StrictNumeric` template: - -```cpp -// Throw a compiler error if the size argument cannot be represented by a -// size_t (e.g. passing an int will fail to compile). -bool AllocateBuffer(void** buffer, StrictCast size); -``` - -### Comparing values between arbitrary arithmetic types - -Both the `StrictNumeric` and `ClampedNumeric` types provide well defined -comparisons between arbitrary arithmetic types. This allows you to perform -comparisons that are not legal or would trigger compiler warnings or errors -under the normal arithmetic promotion rules: - -```cpp -bool foo(unsigned value, int upper_bound) { - // Converting to StrictNumeric allows this comparison to work correctly. - if (MakeStrictNum(value) >= upper_bound) - return false; -``` - -*** note -**Warning:** Do not perform manual conversions using the comparison operators. -Instead, use the cast templates described in the previous sections, or the -constexpr template functions `IsValueInRangeForNumericType` and -`IsTypeInRangeForNumericType`, as these templates properly handle the full range -of corner cases and employ various optimizations. -*** - -### Calculating a buffer size (checked arithmetic) - -When making exact calculations—such as for buffer lengths—it's often necessary -to know when those calculations trigger an overflow, undefined behavior, or -other boundary conditions. The `CheckedNumeric` template does this by storing -a bit determining whether or not some arithmetic operation has occured that -would put the variable in an "invalid" state. Attempting to extract the value -from a variable in an invalid state will trigger a check/trap condition, that -by default will result in process termination. - -Here's an example of a buffer calculation using a `CheckedNumeric` type (note: -the AssignIfValid method will trigger a compile error if the result is ignored). - -```cpp -// Calculate the buffer size and detect if an overflow occurs. -size_t size; -if (!CheckAdd(kHeaderSize, CheckMul(count, kItemSize)).AssignIfValid(&size)) { - // Handle an overflow error... -} -``` - -### Calculating clamped coordinates (non-sticky saturating arithmetic) - -Certain classes of calculations—such as coordinate calculations—require -well-defined semantics that always produce a valid result on boundary -conditions. The `ClampedNumeric` template addresses this by providing -performant, non-sticky saturating arithmetic operations. - -Here's an example of using a `ClampedNumeric` to calculate an operation -insetting a rectangle. - -```cpp -// Use clamped arithmetic since inset calculations might overflow. -void Rect::Inset(int left, int top, int right, int bottom) { - origin_ += Vector2d(left, top); - set_width(ClampSub(width(), ClampAdd(left, right))); - set_height(ClampSub(height(), ClampAdd(top, bottom))); -} -``` - -*** note -The `ClampedNumeric` type is not "sticky", which means the saturation is not -retained across individual operations. As such, one arithmetic operation may -result in a saturated value, while the next operation may then "desaturate" -the value. Here's an example: - -```cpp -ClampedNumeric value = INT_MAX; -++value; // value is still INT_MAX, due to saturation. ---value; // value is now (INT_MAX - 1), because saturation is not sticky. -``` - -*** - -## Conversion functions and StrictNumeric<> in safe_conversions.h - -This header includes a collection of helper `constexpr` templates for safely -performing a range of conversions, assignments, and tests. - -### Safe casting templates - -* `as_signed()` - Returns the supplied integral value as a signed type of - the same width. -* `as_unsigned()` - Returns the supplied integral value as an unsigned type - of the same width. -* `checked_cast<>()` - Analogous to `static_cast<>` for numeric types, except - that by default it will trigger a crash on an out-of-bounds conversion (e.g. - overflow, underflow, NaN to integral) or a compile error if the conversion - error can be detected at compile time. The crash handler can be overridden - to perform a behavior other than crashing. -* `saturated_cast<>()` - Analogous to `static_cast` for numeric types, except - that it returns a saturated result when the specified numeric conversion - would otherwise overflow or underflow. An NaN source returns 0 by - default, but can be overridden to return a different result. -* `strict_cast<>()` - Analogous to `static_cast` for numeric types, except - this causes a compile failure if the destination type is not large - enough to contain any value in the source type. It performs no runtime - checking and thus introduces no runtime overhead. - -### Other helper and conversion functions - -* `IsValueInRangeForNumericType<>()` - A convenience function that returns - true if the type supplied as the template parameter can represent the value - passed as an argument to the function. -* `IsTypeInRangeForNumericType<>()` - A convenience function that evaluates - entirely at compile-time and returns true if the destination type (first - template parameter) can represent the full range of the source type - (second template parameter). -* `IsValueNegative()` - A convenience function that will accept any - arithmetic type as an argument and will return whether the value is less - than zero. Unsigned types always return false. -* `SafeUnsignedAbs()` - Returns the absolute value of the supplied integer - parameter as an unsigned result (thus avoiding an overflow if the value - is the signed, two's complement minimum). - -### StrictNumeric<> - -`StrictNumeric<>` is a wrapper type that performs assignments and copies via -the `strict_cast` template, and can perform valid arithmetic comparisons -across any range of arithmetic types. `StrictNumeric` is the return type for -values extracted from a `CheckedNumeric` class instance. The raw numeric value -is extracted via `static_cast` to the underlying type or any type with -sufficient range to represent the underlying type. - -* `MakeStrictNum()` - Creates a new `StrictNumeric` from the underlying type - of the supplied arithmetic or StrictNumeric type. -* `SizeT` - Alias for `StrictNumeric`. - -## CheckedNumeric<> in checked_math.h - -`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.: `+`, -`-`, `*`, `/` for all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers). -However, *the [variadic template functions -](#CheckedNumeric_in-checked_math_h-Non_member-helper-functions) -are the prefered API,* as they remove type ambiguities and help prevent a number -of common errors. The variadic functions can also be more performant, as they -eliminate redundant expressions that are unavoidable with the with the operator -overloads. (Ideally the compiler should optimize those away, but better to avoid -them in the first place.) - -Type promotions are a slightly modified version of the [standard C/C++ numeric -promotions -](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions) -with the two differences being that *there is no default promotion to int* -and *bitwise logical operations always return an unsigned of the wider type.* - -### Members - -The unary negation, increment, and decrement operators are supported, along -with the following unary arithmetic methods, which return a new -`CheckedNumeric` as a result of the operation: - -* `Abs()` - Absolute value. -* `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type - (valid for only integral types). -* `Max()` - Returns whichever is greater of the current instance or argument. - The underlying return type is whichever has the greatest magnitude. -* `Min()` - Returns whichever is lowest of the current instance or argument. - The underlying return type is whichever has can represent the lowest - number in the smallest width (e.g. int8_t over unsigned, int over - int8_t, and float over int). - -The following are for converting `CheckedNumeric` instances: - -* `type` - The underlying numeric type. -* `AssignIfValid()` - Assigns the underlying value to the supplied - destination pointer if the value is currently valid and within the - range supported by the destination type. Returns true on success. -* `Cast<>()` - Instance method returning a `CheckedNumeric` derived from - casting the current instance to a `CheckedNumeric` of the supplied - destination type. - -*** aside -The following member functions return a `StrictNumeric`, which is valid for -comparison and assignment operations, but will trigger a compile failure on -attempts to assign to a type of insufficient range. The underlying value can -be extracted by an explicit `static_cast` to the underlying type or any type -with sufficient range to represent the underlying type. -*** - -* `IsValid()` - Returns true if the underlying numeric value is valid (i.e. - has not wrapped or saturated and is not the result of an invalid - conversion). -* `ValueOrDie()` - Returns the underlying value. If the state is not valid - this call will trigger a crash by default (but may be overridden by - supplying an alternate handler to the template). -* `ValueOrDefault()` - Returns the current value, or the supplied default if - the state is not valid (but will not crash). - -**Comparison operators are explicitly not provided** for `CheckedNumeric` -types because they could result in a crash if the type is not in a valid state. -Patterns like the following should be used instead: - -```cpp -// Either input or padding (or both) may be arbitrary sizes. -size_t buff_size; -if (!CheckAdd(input, padding, kHeaderLength).AssignIfValid(&buff_size) || - buff_size >= kMaxBuffer) { - // Handle an error... -} else { - // Do stuff on success... -} -``` - -### Non-member helper functions - -The following variadic convenience functions, which accept standard arithmetic -or `CheckedNumeric` types, perform arithmetic operations, and return a -`CheckedNumeric` result. The supported functions are: - -* `CheckAdd()` - Addition. -* `CheckSub()` - Subtraction. -* `CheckMul()` - Multiplication. -* `CheckDiv()` - Division. -* `CheckMod()` - Modulus (integer only). -* `CheckLsh()` - Left integer shift (integer only). -* `CheckRsh()` - Right integer shift (integer only). -* `CheckAnd()` - Bitwise AND (integer only with unsigned result). -* `CheckOr()` - Bitwise OR (integer only with unsigned result). -* `CheckXor()` - Bitwise XOR (integer only with unsigned result). -* `CheckMax()` - Maximum of supplied arguments. -* `CheckMin()` - Minimum of supplied arguments. - -The following wrapper functions can be used to avoid the template -disambiguator syntax when converting a destination type. - -* `IsValidForType<>()` in place of: `a.template IsValid<>()` -* `ValueOrDieForType<>()` in place of: `a.template ValueOrDie<>()` -* `ValueOrDefaultForType<>()` in place of: `a.template ValueOrDefault<>()` - -The following general utility methods is are useful for converting from -arithmetic types to `CheckedNumeric` types: - -* `MakeCheckedNum()` - Creates a new `CheckedNumeric` from the underlying type - of the supplied arithmetic or directly convertible type. - -## ClampedNumeric<> in clamped_math.h - -`ClampedNumeric<>` implements all the logic and operators for clamped -(non-sticky saturating) arithmetic operations and conversions. The -`ClampedNumeric` type implicitly converts back and forth between floating point -and integer data types, saturating on assignment as appropriate. It contains -overloads for basic arithmetic operations (i.e.: `+`, `-`, `*`, `/` for -all types and `%`, `<<`, `>>`, `&`, `|`, `^` for integers) along with comparison -operators for arithmetic types of any size. However, *the [variadic template -functions -](#ClampedNumeric_in-clamped_math_h-Non_member-helper-functions) -are the prefered API,* as they remove type ambiguities and help prevent -a number of common errors. The variadic functions can also be more performant, -as they eliminate redundant expressions that are unavoidable with the operator -overloads. (Ideally the compiler should optimize those away, but better to avoid -them in the first place.) - -Type promotions are a slightly modified version of the [standard C/C++ numeric -promotions -](http://en.cppreference.com/w/cpp/language/implicit_conversion#Numeric_promotions) -with the two differences being that *there is no default promotion to int* -and *bitwise logical operations always return an unsigned of the wider type.* - -*** aside -Most arithmetic operations saturate normally, to the numeric limit in the -direction of the sign. The potentially unusual cases are: - -* **Division:** Division by zero returns the saturated limit in the direction - of sign of the dividend (first argument). The one exception is 0/0, which - returns zero (although logically is NaN). -* **Modulus:** Division by zero returns the dividend (first argument). -* **Left shift:** Non-zero values saturate in the direction of the signed - limit (max/min), even for shifts larger than the bit width. 0 shifted any - amount results in 0. -* **Right shift:** Negative values saturate to -1. Positive or 0 saturates - to 0. (Effectively just an unbounded arithmetic-right-shift.) -* **Bitwise operations:** No saturation; bit pattern is identical to - non-saturated bitwise operations. -*** - -### Members - -The unary negation, increment, and decrement operators are supported, along -with the following unary arithmetic methods, which return a new -`ClampedNumeric` as a result of the operation: - -* `Abs()` - Absolute value. -* `UnsignedAbs()` - Absolute value as an equal-width unsigned underlying type - (valid for only integral types). -* `Max()` - Returns whichever is greater of the current instance or argument. - The underlying return type is whichever has the greatest magnitude. -* `Min()` - Returns whichever is lowest of the current instance or argument. - The underlying return type is whichever has can represent the lowest - number in the smallest width (e.g. int8_t over unsigned, int over - int8_t, and float over int). - -The following are for converting `ClampedNumeric` instances: - -* `type` - The underlying numeric type. -* `RawValue()` - Returns the raw value as the underlying arithmetic type. This - is useful when e.g. assigning to an auto type or passing as a deduced - template parameter. -* `Cast<>()` - Instance method returning a `ClampedNumeric` derived from - casting the current instance to a `ClampedNumeric` of the supplied - destination type. - -### Non-member helper functions - -The following variadic convenience functions, which accept standard arithmetic -or `ClampedNumeric` types, perform arithmetic operations, and return a -`ClampedNumeric` result. The supported functions are: - -* `ClampAdd()` - Addition. -* `ClampSub()` - Subtraction. -* `ClampMul()` - Multiplication. -* `ClampDiv()` - Division. -* `ClampMod()` - Modulus (integer only). -* `ClampLsh()` - Left integer shift (integer only). -* `ClampRsh()` - Right integer shift (integer only). -* `ClampAnd()` - Bitwise AND (integer only with unsigned result). -* `ClampOr()` - Bitwise OR (integer only with unsigned result). -* `ClampXor()` - Bitwise XOR (integer only with unsigned result). -* `ClampMax()` - Maximum of supplied arguments. -* `ClampMin()` - Minimum of supplied arguments. - -The following is a general utility method that is useful for converting -to a `ClampedNumeric` type: - -* `MakeClampedNum()` - Creates a new `ClampedNumeric` from the underlying type - of the supplied arithmetic or directly convertible type. diff --git a/numerics/checked_math.h b/numerics/checked_math.h deleted file mode 100644 index ede3344f8..000000000 --- a/numerics/checked_math.h +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright 2017 The Chromium 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_NUMERICS_CHECKED_MATH_H_ -#define BASE_NUMERICS_CHECKED_MATH_H_ - -#include - -#include -#include - -#include "base/numerics/checked_math_impl.h" - -namespace base { -namespace internal { - -template -class CheckedNumeric { - static_assert(std::is_arithmetic::value, - "CheckedNumeric: T must be a numeric type."); - - public: - using type = T; - - constexpr CheckedNumeric() = default; - - // Copy constructor. - template - constexpr CheckedNumeric(const CheckedNumeric& rhs) - : state_(rhs.state_.value(), rhs.IsValid()) {} - - template - friend class CheckedNumeric; - - // This is not an explicit constructor because we implicitly upgrade regular - // numerics to CheckedNumerics to make them easier to use. - template - constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit) - : state_(value) { - static_assert(std::is_arithmetic::value, "Argument must be numeric."); - } - - // This is not an explicit constructor because we want a seamless conversion - // from StrictNumeric types. - template - constexpr CheckedNumeric( - StrictNumeric value) // NOLINT(runtime/explicit) - : state_(static_cast(value)) {} - - // IsValid() - The public API to test if a CheckedNumeric is currently valid. - // A range checked destination type can be supplied using the Dst template - // parameter. - template - constexpr bool IsValid() const { - return state_.is_valid() && - IsValueInRangeForNumericType(state_.value()); - } - - // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid - // and is within the range supported by the destination type. Returns true if - // successful and false otherwise. - template -#if defined(__clang__) || defined(__GNUC__) - __attribute__((warn_unused_result)) -#elif defined(_MSC_VER) - _Check_return_ -#endif - constexpr bool - AssignIfValid(Dst* result) const { - return BASE_NUMERICS_LIKELY(IsValid()) - ? ((*result = static_cast(state_.value())), true) - : false; - } - - // ValueOrDie() - The primary accessor for the underlying value. If the - // current state is not valid it will CHECK and crash. - // A range checked destination type can be supplied using the Dst template - // parameter, which will trigger a CHECK if the value is not in bounds for - // the destination. - // The CHECK behavior can be overridden by supplying a handler as a - // template parameter, for test code, etc. However, the handler cannot access - // the underlying value, and it is not available through other means. - template - constexpr StrictNumeric ValueOrDie() const { - return BASE_NUMERICS_LIKELY(IsValid()) - ? static_cast(state_.value()) - : CheckHandler::template HandleFailure(); - } - - // 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. - // A range checked destination type can be supplied using the Dst template - // parameter. WARNING: This function may fail to compile or CHECK at runtime - // if the supplied default_value is not within range of the destination type. - template - constexpr StrictNumeric ValueOrDefault(const Src default_value) const { - return BASE_NUMERICS_LIKELY(IsValid()) - ? static_cast(state_.value()) - : checked_cast(default_value); - } - - // Returns a checked numeric of the specified type, cast from the current - // CheckedNumeric. If the current state is invalid or the destination cannot - // represent the result then the returned CheckedNumeric will be invalid. - template - constexpr CheckedNumeric::type> Cast() const { - return *this; - } - - // This friend method is available solely for providing more detailed logging - // in the the tests. Do not implement it in production code, because the - // underlying values may change at any time. - template - friend U GetNumericValueForTest(const CheckedNumeric& src); - - // Prototypes for the supported arithmetic operator overloads. - template - constexpr CheckedNumeric& operator+=(const Src rhs); - template - constexpr CheckedNumeric& operator-=(const Src rhs); - template - constexpr CheckedNumeric& operator*=(const Src rhs); - template - constexpr CheckedNumeric& operator/=(const Src rhs); - template - constexpr CheckedNumeric& operator%=(const Src rhs); - template - constexpr CheckedNumeric& operator<<=(const Src rhs); - template - constexpr CheckedNumeric& operator>>=(const Src rhs); - template - constexpr CheckedNumeric& operator&=(const Src rhs); - template - constexpr CheckedNumeric& operator|=(const Src rhs); - template - constexpr CheckedNumeric& operator^=(const Src rhs); - - constexpr CheckedNumeric operator-() const { - // The negation of two's complement int min is int min, so we simply - // check for that in the constexpr case. - // We use an optimized code path for a known run-time variable. - return MustTreatAsConstexpr(state_.value()) || !std::is_signed::value || - std::is_floating_point::value - ? CheckedNumeric( - NegateWrapper(state_.value()), - IsValid() && (!std::is_signed::value || - std::is_floating_point::value || - NegateWrapper(state_.value()) != - std::numeric_limits::lowest())) - : FastRuntimeNegate(); - } - - constexpr CheckedNumeric operator~() const { - return CheckedNumeric( - InvertWrapper(state_.value()), IsValid()); - } - - constexpr CheckedNumeric Abs() const { - return !IsValueNegative(state_.value()) ? *this : -*this; - } - - template - constexpr CheckedNumeric::type> Max( - const U rhs) const { - using R = typename UnderlyingType::type; - using result_type = typename MathWrapper::type; - // TODO(jschuh): This can be converted to the MathOp version and remain - // constexpr once we have C++14 support. - return CheckedNumeric( - static_cast( - IsGreater::Test(state_.value(), Wrapper::value(rhs)) - ? state_.value() - : Wrapper::value(rhs)), - state_.is_valid() && Wrapper::is_valid(rhs)); - } - - template - constexpr CheckedNumeric::type> Min( - const U rhs) const { - using R = typename UnderlyingType::type; - using result_type = typename MathWrapper::type; - // TODO(jschuh): This can be converted to the MathOp version and remain - // constexpr once we have C++14 support. - return CheckedNumeric( - static_cast( - IsLess::Test(state_.value(), Wrapper::value(rhs)) - ? state_.value() - : Wrapper::value(rhs)), - state_.is_valid() && Wrapper::is_valid(rhs)); - } - - // This function is available only for integral types. It returns an unsigned - // integer of the same width as the source type, containing the absolute value - // of the source, and properly handling signed min. - constexpr CheckedNumeric::type> - UnsignedAbs() const { - return CheckedNumeric::type>( - SafeUnsignedAbs(state_.value()), state_.is_valid()); - } - - constexpr CheckedNumeric& operator++() { - *this += 1; - return *this; - } - - constexpr CheckedNumeric operator++(int) { - CheckedNumeric value = *this; - *this += 1; - return value; - } - - constexpr CheckedNumeric& operator--() { - *this -= 1; - return *this; - } - - constexpr CheckedNumeric operator--(int) { - CheckedNumeric value = *this; - *this -= 1; - return value; - } - - // These perform the actual math operations on the CheckedNumerics. - // Binary arithmetic operations. - template